Initial commit
63
Advanced/CreatingWidgets.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Creating widgets
|
||||
|
||||
Widgets are special views that do not provide a collection of other views as a content,
|
||||
but have functions that are called when creating or updating the view.
|
||||
Normally, a widget manages a GTK or Libadwaita widget using the C API.
|
||||
|
||||
## Recreate the Text widget
|
||||
In this tutorial, we will recreate the ``Text`` widget.
|
||||
A widget conforms to the ``Widget`` protocol:
|
||||
```swift
|
||||
struct CustomText: Widget { }
|
||||
```
|
||||
You can add properties to the widget:
|
||||
```swift
|
||||
struct CustomText: Widget {
|
||||
|
||||
var text: String
|
||||
|
||||
}
|
||||
```
|
||||
This widget can be called in a view body using `CustomText(text: "Hello, world!")`.
|
||||
Now, add the two functions required by the protocol:
|
||||
```swift
|
||||
import CAdw
|
||||
|
||||
struct CustomText: Widget {
|
||||
|
||||
var text: String
|
||||
|
||||
public func container(modifiers: [(View) -> View]) -> ViewStorage { }
|
||||
public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { }
|
||||
|
||||
}
|
||||
```
|
||||
Import CAdw which exposes the whole C Libadwaita and Gtk API to Swift.
|
||||
|
||||
## The `container(modifiers:)` function
|
||||
This function initializes the widget when the widget appears for the first time.
|
||||
It expects a ``ViewStorage`` as the return type.
|
||||
In our case, this function is very simple:
|
||||
```swift
|
||||
func container(modifiers: [(View) -> View]) -> ViewStorage {
|
||||
.init(gtk_label_new(text)?.opaque())
|
||||
}
|
||||
```
|
||||
|
||||
## The `update(_:modifiers:updateProperties:)` function
|
||||
Whenever a state of the app changes, the ``Widget/update(_:modifiers:updateProperties:)`` function of the widget gets called.
|
||||
You get the view storage that you have previously initialized as a parameter.
|
||||
Update the storage to reflect the current state of the widget:
|
||||
```swift
|
||||
func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) {
|
||||
if updateProperties {
|
||||
gtk_label_set_label(storage.pointer, text)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Containers
|
||||
Some widgets act as containers that accept other widgets as children.
|
||||
In that case, use the ``ViewStorage``'s `content` property for storing their view storages.
|
||||
In the ``Widget/update(_:modifiers:updateProperties:)`` function, update the children's storages.
|
||||
Take a look at the code of the container widgets in this library as a reference.
|
48
Advanced/PublishingApps.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Publishing apps
|
||||
|
||||
Learn how to publish your apps using Flatpak.
|
||||
|
||||
Once you feel ready to publish your app to [Flathub](https://flathub.org/), or
|
||||
to a [self-hosted repository](https://docs.flatpak.org/en/latest/hosting-a-repository.html),
|
||||
you can follow this step-by-step guide.
|
||||
|
||||
## Create a Flatpak manifest
|
||||
You have to create a Flatpak manifest, similar to the one [here](https://github.com/flathub/io.github.david_swift.Flashcards/blob/master/io.github.david_swift.Flashcards.json).
|
||||
Find detailed information in the [Flatpak documentation](https://docs.flatpak.org/en/latest/manifests.html).
|
||||
This is the only code that should be submitted to Flathub.
|
||||
There will be a new repository created under `flathub/app-id` that hosts the manifest.
|
||||
|
||||
### SDK Extension for Swift 5
|
||||
I recommend using the SDK Extension for building the app.
|
||||
Add the following snippet.
|
||||
|
||||
```json
|
||||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.swift5"
|
||||
]
|
||||
```
|
||||
|
||||
### Generate sources for the Swift Package Manager
|
||||
You cannot access the web while building the app.
|
||||
Therefore, you need to add all the dependencies as modules to the manifest file.
|
||||
This can be automated using the [Flatpak builder tool for SPM](https://github.com/flatpak/flatpak-builder-tools/tree/master/spm).
|
||||
|
||||
## MetaInfo file
|
||||
In the Flatpak Manifest file, under the build commands, you should add the [code to install the app's
|
||||
MetaInfo file](https://github.com/flathub/io.github.david_swift.Flashcards/blob/c5c0421ffb5589641ddb44a269a6e7e07d430581/io.github.david_swift.Flashcards.json#L49).
|
||||
The MetaInfo file is located in the app's main repository (not in the Flathub repository).
|
||||
Take a look at the example [here](https://github.com/david-swift/Memorize/blob/main/data/io.github.david_swift.Flashcards.metainfo.xml).
|
||||
|
||||
## Desktop entry file
|
||||
[This line](https://github.com/flathub/io.github.david_swift.Flashcards/blob/c5c0421ffb5589641ddb44a269a6e7e07d430581/io.github.david_swift.Flashcards.json#L50) in the example installs the Desktop Entry file.
|
||||
It is located in the [main repository](https://github.com/david-swift/Memorize/blob/main/data/io.github.david_swift.Flashcards.desktop).
|
||||
|
||||
## Check the requirements
|
||||
Before submitting an app to Flathub, make sure to check the [requirements](https://docs.flathub.org/docs/for-app-authors/requirements),
|
||||
[MetaInfo guidelines](https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines/), and [quality guidelines](https://docs.flathub.org/docs/for-app-authors/metainfo-guidelines/quality-guidelines).
|
||||
Then, test and submit the app following the [submission instructions](https://docs.flathub.org/docs/for-app-authors/submission).
|
||||
|
||||
## Get the badges
|
||||
Use the [Flathub badges](https://flathub.org/badges) to inform users about the simple installation option.
|
||||
Even more assets are available [here](https://github.com/flathub-infra/assets).
|
||||
|
164
Adwaita.md
Normal file
@ -0,0 +1,164 @@
|
||||
# ``Adwaita``
|
||||
|
||||
_Adwaita for Swift_ is a framework for creating user interfaces for GNOME with an API similar to SwiftUI.
|
||||
|
||||
## Overview
|
||||
|
||||
Write user interfaces in a declarative way.
|
||||
|
||||
As an example, the following code defines a _view_ (more information: ``View``).
|
||||
|
||||
```swift
|
||||
struct Counter: View {
|
||||
|
||||
@State private var count = 0
|
||||
|
||||
var view: Body {
|
||||
HStack {
|
||||
Button(icon: .default(icon: .goPrevious)) {
|
||||
count -= 1
|
||||
}
|
||||
Text("\(count)")
|
||||
.style("title-1")
|
||||
.frame(minWidth: 100)
|
||||
Button(icon: .default(icon: .goNext)) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
A view can be implemented in different ways, the following screenshot showing an example.
|
||||
|
||||
![Screenshot of the counter app](Counter.png)
|
||||
|
||||
## Goals
|
||||
|
||||
_Adwaita for Swift_'s main goal is to provide an easy-to-use interface for creating apps for the GNOME ecosystem.
|
||||
An article about the project's motivation is available on the [website of the Swift programming language](https://www.swift.org/blog/adwaita-swift/).
|
||||
|
||||
## Installation
|
||||
|
||||
### Dependencies
|
||||
|
||||
#### Flatpak
|
||||
|
||||
It is recommended to develop apps inside of a Flatpak.
|
||||
That way, you don't have to install Swift or any of the dependencies on your system, and you always have access to the latest versions.
|
||||
Take a look at the [template repository](https://github.com/AparokshaUI/AdwaitaTemplate).
|
||||
This works on Linux only.
|
||||
|
||||
#### Directly on system
|
||||
|
||||
You can also run your apps directly on the system.
|
||||
|
||||
If you are using a Linux distribution, install `libadwaita-devel` or `libadwaita` (or something similar, based on the package manager) as well as `gtk4-devel`, `gtk4` or similar.
|
||||
|
||||
On macOS, follow these steps:
|
||||
1. Install [Homebrew](https://brew.sh).
|
||||
2. Install Libadwaita (and thereby GTK 4):
|
||||
```
|
||||
brew install libadwaita
|
||||
```
|
||||
|
||||
### Swift package
|
||||
1. Open your Swift package in GNOME Builder, Xcode, or any other IDE.
|
||||
2. Open the `Package.swift` file.
|
||||
3. Into the `Package` initializer, under `dependencies`, paste:
|
||||
```swift
|
||||
.package(url: "https://github.com/AparokshaUI/Adwaita", from: "0.1.0")
|
||||
```
|
||||
|
||||
## Template repository
|
||||
|
||||
It is recommended to develop apps on Linux inside a Flatpak.
|
||||
Find more information in the [template repository](https://github.com/AparokshaUI/AdwaitaTemplate).
|
||||
|
||||
## Topics
|
||||
|
||||
### Tutorials
|
||||
|
||||
- <doc:Table-of-Contents>
|
||||
|
||||
### Basics
|
||||
|
||||
- <doc:CreatingViews>
|
||||
- <doc:Windows>
|
||||
- <doc:KeyboardShortcuts>
|
||||
|
||||
### Advanced
|
||||
|
||||
- <doc:CreatingWidgets>
|
||||
- <doc:PublishingApps>
|
||||
|
||||
### Views
|
||||
|
||||
- ``ActionRow``
|
||||
- ``AlertDialog``
|
||||
- ``Avatar``
|
||||
- ``Banner``
|
||||
- ``Bin``
|
||||
- ``Box``
|
||||
- ``Button``
|
||||
- ``ButtonContent``
|
||||
- ``Carousel``
|
||||
- ``CenterBox``
|
||||
- ``CheckButton``
|
||||
- ``Clamp``
|
||||
- ``ComboRow``
|
||||
- ``EntryRow``
|
||||
- ``ExpanderRow``
|
||||
- ``FlowBox``
|
||||
- ``ForEach``
|
||||
- ``Form``
|
||||
- ``FormSection``
|
||||
- ``HStack``
|
||||
- ``HeaderBar``
|
||||
- ``Label``
|
||||
- ``LevelBar``
|
||||
- ``LinkButton``
|
||||
- ``List``
|
||||
- ``ListBox``
|
||||
- ``Menu``
|
||||
- ``NavigationSplitView``
|
||||
- ``NavigationView``
|
||||
- ``Overlay``
|
||||
- ``OverlaySplitView``
|
||||
- ``PasswordEntryRow``
|
||||
- ``Popover``
|
||||
- ``PreferencesGroup``
|
||||
- ``PreferencesPage``
|
||||
- ``PreferencesRow``
|
||||
- ``ProgressBar``
|
||||
- ``ScrolledWindow``
|
||||
- ``ScrollView``
|
||||
- ``SearchBar``
|
||||
- ``SearchEntry``
|
||||
- ``SpinRow``
|
||||
- ``Spinner``
|
||||
- ``SplitButton``
|
||||
- ``StatusPage``
|
||||
- ``SwitchRow``
|
||||
- ``Text``
|
||||
- ``ToastOverlay``
|
||||
- ``Toggle``
|
||||
- ``ToggleButton``
|
||||
- ``ToolbarView``
|
||||
- ``ViewStack``
|
||||
- ``ViewSwitcher``
|
||||
- ``VStack``
|
||||
- ``WindowTitle``
|
||||
|
||||
### Windows
|
||||
|
||||
- ``AboutWindow``
|
||||
- ``FileDialog``
|
||||
- ``Window``
|
||||
|
||||
### Menus
|
||||
|
||||
- ``MenuButton``
|
||||
- ``MenuSection``
|
||||
- ``Submenu``
|
156
Basics/CreatingViews.md
Normal file
@ -0,0 +1,156 @@
|
||||
# Creating views
|
||||
|
||||
Views are the building blocks of your application.
|
||||
A view can be as simple as the ``Text`` widget, or as complex as the whole content of a single window.
|
||||
|
||||
## Add views to a window
|
||||
You can add views to a window:
|
||||
```swift
|
||||
import Adwaita
|
||||
|
||||
@main
|
||||
struct HelloWorld: App {
|
||||
|
||||
let id = "io.github.david_swift.HelloWorld"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "content") { _ in
|
||||
// These are the views:
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the widgets ``HeaderBar`` and ``Text`` are used.
|
||||
`padding` is a view modifier, a function that modifies a view, which adds some padding around the text.
|
||||
|
||||
## Create custom views
|
||||
While directly adding widgets into the ``Window``'s body might work for very simple apps,
|
||||
it can get messy very quickly.
|
||||
Create custom views by declaring types that conform to the ``View`` protocol:
|
||||
```swift
|
||||
// A custom view named "ContentView":
|
||||
struct ContentView: View {
|
||||
|
||||
var view: Body {
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Properties
|
||||
As every structure in Swift, custom views can have properties:
|
||||
```swift
|
||||
struct HelloView: View {
|
||||
|
||||
// The property "text":
|
||||
var text: String
|
||||
var view: Body {
|
||||
Text("Hello, \(text)!")
|
||||
.padding()
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
This view can be called via `HelloView(text: "world")` in another view.
|
||||
|
||||
## State
|
||||
Whenever you want to modify a property that is stored in the view's structure from within your view,
|
||||
wrap the property with the ``State`` property wrapper:
|
||||
```swift
|
||||
struct MyView: View {
|
||||
|
||||
// This property can be modified form within the view:
|
||||
@State private var text = "world"
|
||||
var view: Body {
|
||||
Text("Hello, \(text)!")
|
||||
.padding()
|
||||
Button("Change Text") {
|
||||
text = Bool.random() ? "world" : "John"
|
||||
}
|
||||
.padding(10, .horizontal.add(.bottom))
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
In this example, the text property is set whenever you press the "Change Text" button.
|
||||
|
||||
## Change the state in child views
|
||||
You can access state properties in child views in the same way as you would access any other property
|
||||
if the child view cannot modify the state (`HelloView` is defined above):
|
||||
```swift
|
||||
struct MyView: View {
|
||||
|
||||
@State private var text = "world"
|
||||
var view: Body {
|
||||
// "HelloView" can read the "text" property:
|
||||
HelloView(text: text)
|
||||
Button("Change Text") {
|
||||
text = Bool.random() ? "world" : "John"
|
||||
}
|
||||
.padding(10, .horizontal.add(.bottom))
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
If the child view should be able to modify the state, use the ``Binding`` property wrapper in the child view
|
||||
and pass the property with a dollar sign (`$`) to that view.
|
||||
```swift
|
||||
struct MyView: View {
|
||||
|
||||
@State private var text = "world"
|
||||
var view: Body {
|
||||
HelloView(text: text)
|
||||
// Pass the editable text property to the child view:
|
||||
ChangeTextView(text: $text)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ChangeTextView: View {
|
||||
|
||||
// Accept the editable text property:
|
||||
@Binding var text: String
|
||||
var view: Body {
|
||||
Button("Change Text") {
|
||||
// Binding properties can be used the same way as state properties:
|
||||
text = Bool.random() ? "world" : "John"
|
||||
}
|
||||
.padding(10, .horizontal.add(.bottom))
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
If you have a more complex type and want to pass a property of the type as a binding,
|
||||
you can simply access the property on the binding.
|
||||
|
||||
```swift
|
||||
HelloView(text: $complexType.text)
|
||||
```
|
||||
|
||||
Whenever you modify a state property (directly or indirectly through bindings),
|
||||
the user interface gets automatically updated to reflect that change.
|
||||
|
||||
## Save state values between app launches
|
||||
It's possible to automatically save a value that conforms to `Codable` whenever it changes to a file.
|
||||
The value in the file is read when the view containing the state value appears for the first time (e.g. when the user launches the app).
|
||||
|
||||
Use the following syntax, where `"text"` is a unique identifier.
|
||||
```swift
|
||||
@State("text") private var text = "world"
|
||||
```
|
||||
|
||||
You can organize your content by specifying a custom folder path which will be appended to the XDG data home directory.
|
||||
```swift
|
||||
@State("text", folder: "io.github.david_swift.HelloWorld/my-view") private var text = "world"
|
||||
```
|
115
Basics/KeyboardShortcuts.md
Normal file
@ -0,0 +1,115 @@
|
||||
# Keyboard shortcuts
|
||||
|
||||
Keyboard shortcuts can be attached to individual windows or whole applications.
|
||||
|
||||
## About keyboard shortcuts
|
||||
Keyboard shortcuts are represented as a `String`.
|
||||
You can add a single character by adding itself to the string, e.g. `"n"`.
|
||||
The F keys are written as `"F1"`, `"F2"`, etc.
|
||||
For character keys, write the lowercase name instead of the symbol, such as `"minus"` instead of `"-"`.
|
||||
|
||||
Add modifiers to the shortcut using the following string modifiers:
|
||||
- `.shift()`
|
||||
- `.ctrl()`
|
||||
- `.alt()`
|
||||
- `.meta()`
|
||||
- `.super()`
|
||||
- `.hyper()`
|
||||
|
||||
As an example, the following syntax represents the `Ctrl + N` shortcut: `"n".ctrl()`.
|
||||
|
||||
## Add shortcuts to a window
|
||||
Add a keyboard shortcut to an invividual window. It is only available in that window.
|
||||
```swift
|
||||
import Adwaita
|
||||
|
||||
@main
|
||||
struct HelloWorld: App {
|
||||
|
||||
let id = "io.github.david_swift.HelloWorld"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "content") { _ in
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
// Add the shortcut "Ctrl + W" for closing the window
|
||||
.keyboardShortcut("w".ctrl()) { window in
|
||||
window.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Add shortcuts to an app
|
||||
Add a keyboard to an app so that the shortcut is available in every top-level window.
|
||||
```swift
|
||||
import Adwaita
|
||||
|
||||
@main
|
||||
struct HelloWorld: App {
|
||||
|
||||
let id = "io.github.david_swift.HelloWorld"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "content") { _ in
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
// Add the shortcut "Ctrl + Q" for terminating the app
|
||||
.appKeyboardShortcut("q".ctrl()) { app in
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Create shortcuts from a menu
|
||||
The most elegant way for adding keyboard shortcuts is in many cases adding them via menus.
|
||||
Here is an example using a menu button:
|
||||
```swift
|
||||
struct TestView: View {
|
||||
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
Menu(icon: .default(icon: .openMenu), app: app) {
|
||||
MenuButton("New Window", window: false) {
|
||||
app.addWindow("main")
|
||||
}
|
||||
// Add a keyboard shortcut to the app.
|
||||
.keyboardShortcut("n".ctrl())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
Add the keyboard shortcut to a single window by specifying the `window` parameter in the initializer of `Menu`,
|
||||
and removing `window: false` in the initializer of `MenuButton`.
|
||||
|
||||
## Create shortcuts from a button
|
||||
It's possible to easily create a keyboard shortcut from a button.
|
||||
Use `appKeyboardShortcut` instead of `keyboardShortcut` for shortcuts on an application level.
|
||||
Note that the shortcut gets activated after presenting the view for the first time.
|
||||
```swift
|
||||
struct HelloView: View {
|
||||
|
||||
var window: GTUIWindow
|
||||
|
||||
var view: Body {
|
||||
Button("New Item") {
|
||||
print("New Item")
|
||||
}
|
||||
// Add a keyboard shortcut to the window "window".
|
||||
.keyboardShortcut("n".ctrl().shift(), window: window)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
137
Basics/Windows.md
Normal file
@ -0,0 +1,137 @@
|
||||
# Windows
|
||||
|
||||
Windows in _Adwaita_ are not actually single windows in the user interface,
|
||||
but rather instructions on how to create one type of window.
|
||||
|
||||
## The simplest case
|
||||
A single window app is an app having exactly one window, and when this window is closed, the app terminates.
|
||||
We can add multiple windows to an app as well.
|
||||
Whenever the last one disappears, the app terminates.
|
||||
```swift
|
||||
@main
|
||||
struct HelloWorld: App {
|
||||
|
||||
let id = "io.github.david_swift.HelloWorld"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "content") { _ in
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
// Add a second window:
|
||||
Window(id: "window-2") { _ in
|
||||
HeaderBar.empty()
|
||||
Text("Window 2")
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Showing windows
|
||||
Every app contains the property ``App/app``.
|
||||
You can use this property for running functions that affect the whole app, e.g. quitting the app.
|
||||
Another use case is showing a window:
|
||||
```swift
|
||||
@main
|
||||
struct HelloWorld: App {
|
||||
|
||||
let id = "io.github.david_swift.HelloWorld"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "content") { _ in
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
Window(id: "control") { _ in
|
||||
HeaderBar.empty()
|
||||
Button("Show Window") {
|
||||
// Show the window with the identifier "content":
|
||||
app.showWindow("content")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
"Showing" a window means creating an instance of the window type if there isn't one,
|
||||
or focusing the window that already exists of that type otherwise.
|
||||
It should be used for opening windows that cannot be presented more than once
|
||||
and for moving a window that is already open into the foreground.
|
||||
|
||||
## Adding windows
|
||||
You can call the ``GTUIApp/addWindow(_:parent:)`` function instead of ``GTUIApp/showWindow(_:)``
|
||||
if you want to add and focus another instance of a window type:
|
||||
```swift
|
||||
@main
|
||||
struct HelloWorld: App {
|
||||
|
||||
let id = "io.github.david_swift.HelloWorld"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "content") { _ in
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
Window(id: "control") { _ in
|
||||
HeaderBar.empty()
|
||||
Button("Add Window") {
|
||||
// Add a new instance of the "content" window type
|
||||
app.addWindow("content")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
It can be used to add an overlay window to a certain instance of a window type
|
||||
by specifying the `parent` parameter, e.g. in the example above:
|
||||
```swift
|
||||
Window(id: "control") { window in
|
||||
HeaderBar.empty()
|
||||
Button("Add Child Window") {
|
||||
// Add the new instance as a child window of this window
|
||||
app.addWindow("content", parent: window)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
```
|
||||
|
||||
## Customizing the initial number of windows
|
||||
By default, every window type of the app's scene appears once when the app starts.
|
||||
It's possible to customize how many windows are being presented at the app's startup:
|
||||
```swift
|
||||
@main
|
||||
struct HelloWorld: App {
|
||||
|
||||
let id = "io.github.david_swift.HelloWorld"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
// Open no window of the "content" type
|
||||
Window(id: "content", open: 0) { _ in
|
||||
HeaderBar.empty()
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
// Open two windows of the "control" type
|
||||
Window(id: "control", open: 2) { _ in
|
||||
HeaderBar.empty()
|
||||
Button("Show Window") {
|
||||
app.addWindow("content")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
674
LICENSE.md
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
<p align="center">
|
||||
<h1 align="center">Adwaita.docc</h1>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://aparokshaui.github.io/adwaita-swift/">
|
||||
Documentation
|
||||
</a>
|
||||
·
|
||||
<a href="https://github.com/AparokshaUI/Adwaita">
|
||||
Adwaita for Swift on GitHub
|
||||
</a>
|
||||
</p>
|
||||
|
||||
This repository contains documentation for [Adwaita for Swift](https://github.com/AparokshaUI/Adwaita). It is hosted [here](https://aparokshaui.github.io/adwaita-swift/).
|
||||
|
||||
## Thanks
|
||||
|
||||
- This is built using [swift-docc](https://github.com/apple/swift-docc)
|
||||
- Some symbolic icons are from the [GNOME icon library](https://gitlab.gnome.org/World/design/icon-library)
|
||||
- The colored images representing whole tutorials are inspired by [GNOME app illustrations](https://gitlab.gnome.org/Teams/Design/app-illustrations)
|
||||
- The GNOME logo has been downloaded from the [GNOME website](https://brand.gnome.org/)
|
||||
- The Swift logo has been downloaded form the [Swift website](https://developer.apple.com/swift/resources/)
|
||||
- The laptop image showing the Asahi logo has been downloaded from the [Asahi Linux website](https://asahilinux.org/)
|
||||
|
BIN
Resources/AppProject/ActiveConfiguration.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
Resources/AppProject/CloneTemplateSubtasks.png
Normal file
After Width: | Height: | Size: 50 KiB |
11
Resources/AppProject/DesktopNew.desktop
Normal file
@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
|
||||
Name=Subtasks
|
||||
Comment=Organize your tasks
|
||||
Categories=Utility;GNOME;
|
||||
|
||||
Icon=io.github.david_swift.Subtasks
|
||||
Exec=Subtasks
|
||||
Terminal=false
|
11
Resources/AppProject/DesktopOld.desktop
Normal file
@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
|
||||
Name=Subtasks
|
||||
Comment=A template for creating GNOME apps with Swift
|
||||
Categories=Development;GNOME;
|
||||
|
||||
Icon=io.github.david_swift.Subtasks
|
||||
Exec=Subtasks
|
||||
Terminal=false
|
BIN
Resources/AppProject/GroupRename.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
Resources/AppProject/GroupRename2.png
Normal file
After Width: | Height: | Size: 39 KiB |
25
Resources/AppProject/MetainfoNew.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.david_swift.Subtasks</id>
|
||||
|
||||
<name>Subtasks</name>
|
||||
<summary>Organize your tasks</summary>
|
||||
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>LGPL-3.0-or-later</project_license>
|
||||
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>touch</control>
|
||||
</supports>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
A simple task management app for the GNOME desktop focused on structuring tasks.
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">io.github.david_swift.Subtasks.desktop</launchable>
|
||||
</component>
|
||||
|
25
Resources/AppProject/MetainfoOld.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.david_swift.Subtasks</id>
|
||||
|
||||
<name>Subtasks</name>
|
||||
<summary>A template for creating GNOME apps with Swift</summary>
|
||||
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>LGPL-3.0-or-later</project_license>
|
||||
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>touch</control>
|
||||
</supports>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
This is a <em>long</em> description of this project. Yes - that is required!
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">io.github.david_swift.Subtasks.desktop</launchable>
|
||||
</component>
|
||||
|
BIN
Resources/AppProject/OpenContainingFolder.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
Resources/AppProject/Rename.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
Resources/AppProject/Rename2.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
Resources/AppProject/RenameManifest.png
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
Resources/AppProject/RenameManifestContent.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
Resources/AppProject/ReplaceContent.png
Normal file
After Width: | Height: | Size: 226 KiB |
BIN
Resources/AppProject/ReplaceContentName.png
Normal file
After Width: | Height: | Size: 140 KiB |
BIN
Resources/Counter.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
52
Resources/Data.svg
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="300"
|
||||
viewBox="0 0 300 300"
|
||||
width="300"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="Data.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.65243742"
|
||||
inkscape:cx="-6.8972132"
|
||||
inkscape:cy="203.85097"
|
||||
inkscape:window-width="1436"
|
||||
inkscape:window-height="484"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1" />
|
||||
<g
|
||||
id="g1"
|
||||
transform="matrix(1.1,0,0,1.1,-14.998177,-16.970273)">
|
||||
<g
|
||||
fill="#222222"
|
||||
id="g2"
|
||||
transform="matrix(10.368,0,0,10.368,65.021443,66.032248)"
|
||||
style="fill:#ffffff">
|
||||
<path
|
||||
d="M 16,9.988281 C 16,7.132812 14.484375,4.449219 12,3.015625 9.527344,1.589844 6.472656,1.589844 4,3.015625 1.515625,4.449219 0,7.132812 0,9.988281 c 0,0.554688 0.449219,1 1,1 h 1 c 0.550781,0 1,-0.445312 1,-1 C 2.96875,9.558594 2.664062,9.191406 2.238281,9.089844 2.523438,7.308594 3.425781,5.660156 5,4.75 c 1.859375,-1.074219 4.140625,-1.074219 6,0 1.574219,0.910156 2.476562,2.558594 2.761719,4.339844 C 13.335938,9.191406 13.03125,9.558594 13,9.988281 c 0,0.554688 0.449219,1 1,1 h 1 c 0.550781,0 1,-0.445312 1,-1 z m 0,0"
|
||||
id="path1"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
d="m 11,5.683594 -4.480469,4.976562 c -0.00781,0.0078 -0.015625,0.01563 -0.023437,0.02344 -0.125,0.140625 -0.226563,0.296875 -0.308594,0.464844 -0.472656,0.996093 -0.046875,2.191406 0.949219,2.664062 0.996093,0.472656 2.191406,0.05078 2.664062,-0.949219 C 9.871094,12.71875 9.921875,12.5625 9.953125,12.402344 h 0.00391 l 0.00781,-0.05469 0.00781,-0.04297 c 0.00391,-0.01172 0.00391,-0.02344 0.00391,-0.03516 z m 0,0"
|
||||
id="path2"
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
1
Resources/Declarative/Array1.swift
Normal file
@ -0,0 +1 @@
|
||||
let array = [5, 10, 2, 3]
|
1
Resources/Declarative/Array2.swift
Normal file
@ -0,0 +1 @@
|
||||
var array: [Int] = .init()
|
6
Resources/Declarative/Array3.swift
Normal file
@ -0,0 +1,6 @@
|
||||
var array: [Int] = .init()
|
||||
|
||||
array.append(5)
|
||||
array.append(10)
|
||||
array.append(2)
|
||||
array.append(3)
|
BIN
Resources/Declarative/HelloWorld1.png
Normal file
After Width: | Height: | Size: 11 KiB |
19
Resources/Declarative/HelloWorld1.swift
Normal file
@ -0,0 +1,19 @@
|
||||
// The status page
|
||||
let page = adw_status_page_new()
|
||||
adw_status_page_set_title(page?.opaque(), "Hello, world!")
|
||||
adw_status_page_set_description(page?.opaque(), "Adwaita for Swift")
|
||||
|
||||
// The header bar view
|
||||
let headerBar = adw_header_bar_new()
|
||||
adw_header_bar_set_show_title(headerBar?.opaque(), 0)
|
||||
|
||||
// The toolbar view
|
||||
let contentView = adw_toolbar_view_new()
|
||||
adw_toolbar_view_set_content(contentView?.opaque(), page)
|
||||
adw_toolbar_view_add_top_bar(contentView?.opaque(), headerBar)
|
||||
|
||||
// The window
|
||||
let window = adw_application_window_new(app?.pointer?.cast())
|
||||
adw_application_window_set_content(window?.cast(), contentView)
|
||||
gtk_window_set_default_size(window?.cast(), 300, 300)
|
||||
gtk_window_present(window?.cast())
|
8
Resources/Declarative/HelloWorld2.swift
Normal file
@ -0,0 +1,8 @@
|
||||
Window(id: "example") { _ in
|
||||
StatusPage("Hello, world!", description: "Adwaita for Swift")
|
||||
.topToolbar {
|
||||
HeaderBar()
|
||||
.showTitle(false)
|
||||
}
|
||||
}
|
||||
.defaultSize(width: 300, height: 300)
|
148
Resources/Essentials.svg
Normal file
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="300"
|
||||
version="1.1"
|
||||
style="enable-background:new"
|
||||
id="svg7384"
|
||||
height="300"
|
||||
sodipodi:docname="Essentials.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#000000"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1131"
|
||||
id="namedview837"
|
||||
showgrid="true"
|
||||
inkscape:zoom="1.6129961"
|
||||
inkscape:cx="103.22406"
|
||||
inkscape:cy="154.06113"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7384"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid839"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingy="1"
|
||||
spacingx="1"
|
||||
units="px"
|
||||
visible="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata90">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Gnome Symbolic Icons</dc:title>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<title
|
||||
id="title8473">Gnome Symbolic Icons</title>
|
||||
<defs
|
||||
id="defs7386" />
|
||||
<g
|
||||
transform="translate(-191.9899,-552)"
|
||||
id="layer10" />
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer1" />
|
||||
<g
|
||||
transform="translate(-191.9899,-552)"
|
||||
id="layer11" />
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer7" />
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer6" />
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer5" />
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer9" />
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer2" />
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer8" />
|
||||
<g
|
||||
transform="matrix(12.274047,0,0,12.543219,-2303.2276,-6075.0566)"
|
||||
id="layer3"
|
||||
style="fill:#ffffff">
|
||||
<path
|
||||
id="path4042"
|
||||
d="m 200,490.5 a 2.5,2.5 0 0 1 -2.5,2.5 2.5,2.5 0 0 1 -2.5,-2.5 2.5,2.5 0 0 1 2.5,-2.5 2.5,2.5 0 0 1 2.5,2.5 z"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.33333;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
id="rect4002"
|
||||
d="m 204.5,493 c 1.385,0 2.5,1.115 2.5,2.5 0,1.385 -1.115,2.5 -2.5,2.5 -1.385,0 -2.5,-1.115 -2.5,-2.5 0,-1.385 1.115,-2.5 2.5,-2.5 z"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.33333;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
d="m 202,499 c -1.35232,-0.0191 -1.35232,2.01913 0,2 h 1 v 2 c -1.2e-4,0.77087 0.83516,1.25205 1.50195,0.86523 0.66593,0.38233 1.49645,-0.0974 1.49805,-0.86523 v -0.13477 c 0.86619,0.49882 1.8477,-0.44122 1.38672,-1.32812 l -0.90474,-1.72832 C 206.16814,499.2652 205.8993,499 205,499 h -1 z"
|
||||
id="path4048"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccc" />
|
||||
<path
|
||||
id="path4051"
|
||||
d="m 196.5293,494.48633 a 1.0001,1.0001 0 0 0 -1.01953,0.87305 L 195,498.92969 V 503 a 1.0001,1.0001 0 1 0 2,0 v -3.92969 l 0.49023,-3.42969 a 1.0001,1.0001 0 0 0 -0.96093,-1.15429 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
id="path4053"
|
||||
d="m 198.54102,494.48633 a 1.0001,1.0001 0 0 0 -1.03125,1.15429 L 198,499.07031 V 503 a 1.0001,1.0001 0 1 0 2,0 v -4.07031 l -0.50977,-3.57031 a 1.0001,1.0001 0 0 0 -0.94921,-0.87305 z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
d="m 196,495 h 3 v 7 h -1 v -1.5 c 0,0 0,-0.5 -0.5,-0.5 -0.5,0 -0.5,0.5 -0.5,0.5 v 1.5 h -1 z"
|
||||
id="path4055"
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4057"
|
||||
d="m 196,494 a 1.0001,1.0001 0 0 0 -0.70703,0.29297 l -3,3 a 1.0001,1.0001 0 1 0 1.41406,1.41406 L 196.41406,496 h 1.93555 l 1.73633,3.90625 a 1.0002723,1.0002723 0 1 0 1.82812,-0.8125 l -2,-4.5 A 1.0001,1.0001 0 0 0 199,494 Z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(-191.9899,-488)"
|
||||
id="layer4" />
|
||||
</svg>
|
After Width: | Height: | Size: 10 KiB |
39
Resources/FinalSteps.svg
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="300"
|
||||
viewBox="0 0 300 300"
|
||||
width="300"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="FinalSteps.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.65243742"
|
||||
inkscape:cx="85.831987"
|
||||
inkscape:cy="167.06583"
|
||||
inkscape:window-width="862"
|
||||
inkscape:window-height="738"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
d="m 111.99655,86.2493 c -5.51251,0 -11.02503,1.612512 -15.6,4.649952 -4.612506,3.037536 -8.287487,7.425024 -10.537535,12.450048 -2.249952,5.02503 -3.074976,10.64996 -2.812512,16.12504 0.26304,5.51251 1.537536,10.9125 2.887584,16.27497 l 6.862467,27.3 h 38.399996 l 6.82502,-28.91251 c 1.23744,-5.21251 2.43744,-10.46246 2.58749,-15.82502 0.1872,-5.36247 -0.71232,-10.80001 -2.96256,-15.67499 -2.24995,-4.837538 -5.88749,-9.074978 -10.42493,-11.962466 -4.5,-2.887488 -9.86256,-4.425025 -15.22502,-4.425024 z m 76.8,19.2 c -5.51251,0 -11.02503,1.61251 -15.6,4.64996 -4.61251,3.03754 -8.28749,7.42503 -10.53754,12.45005 -2.24995,5.02503 -3.07497,10.64995 -2.81251,16.12501 0.26304,5.51252 1.53754,10.91252 2.88758,16.27498 l 6.86247,27.3 h 38.4 l 6.82502,-28.91251 c 1.23744,-5.21251 2.43744,-10.46247 2.58749,-15.82503 0.1872,-5.36246 -0.71232,-10.79998 -2.96256,-15.67496 -2.24995,-4.83754 -5.88749,-9.07498 -10.42493,-11.96247 -4.5,-2.88749 -9.86256,-4.42503 -15.22502,-4.42503 z m -95.999996,67.2 v 9.6 h 0.0384 c 0,6.82502 3.674966,13.12502 9.599996,16.57498 5.92502,3.41251 13.2,3.41251 19.16246,0 5.88749,-3.44996 9.56247,-9.74996 9.56247,-16.57498 h 0.0384 v -9.6 z m 76.799996,19.2 v 9.6 h 0.0384 c 0,6.82502 3.67497,13.12502 9.6,16.57498 5.92502,3.41251 13.2,3.41251 19.16246,0 5.88759,-3.44996 9.56256,-9.74996 9.56256,-16.57498 h 0.0384 v -9.6 z m 0,0"
|
||||
fill="#222222"
|
||||
id="path1"
|
||||
style="fill:#ffffff;stroke-width:9.6" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
1492
Resources/Foundation.svg
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
Resources/GNOME.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
381
Resources/GNOME/Asahi.svg
Normal file
@ -0,0 +1,381 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="1993.6678"
|
||||
height="1153.5312"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
|
||||
sodipodi:docname="asahilinux_laptop.svg"
|
||||
style="display:inline">
|
||||
<title
|
||||
id="title1371">MacBook Pro with Asahi Linux logo</title>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient4034">
|
||||
<stop
|
||||
style="stop-color:#7a7a7a;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4036" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4038" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4014">
|
||||
<stop
|
||||
style="stop-color:#1f1e1c;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4016" />
|
||||
<stop
|
||||
style="stop-color:#56514d;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4018" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3926">
|
||||
<stop
|
||||
style="stop-color:#a7a7a7;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3928" />
|
||||
<stop
|
||||
style="stop-color:#534f4f;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3930" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3910">
|
||||
<stop
|
||||
style="stop-color:#dddddd;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3912" />
|
||||
<stop
|
||||
style="stop-color:#616161;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3914" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3880">
|
||||
<stop
|
||||
style="stop-color:#676767;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3882" />
|
||||
<stop
|
||||
id="stop3906"
|
||||
offset="0.00363211"
|
||||
style="stop-color:#aeaeae;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3890"
|
||||
offset="0.00984098"
|
||||
style="stop-color:#f5f5f5;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#cecece;stop-opacity:1"
|
||||
offset="0.02833396"
|
||||
id="stop3894" />
|
||||
<stop
|
||||
id="stop3904"
|
||||
offset="0.96774817"
|
||||
style="stop-color:#cecece;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3888"
|
||||
offset="0.9868266"
|
||||
style="stop-color:#f5f5f5;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#aeaeae;stop-opacity:1"
|
||||
offset="0.99710274"
|
||||
id="stop3908" />
|
||||
<stop
|
||||
style="stop-color:#676767;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3884" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3880"
|
||||
id="linearGradient3902"
|
||||
x1="2632.8438"
|
||||
y1="1372.9688"
|
||||
x2="4788.9727"
|
||||
y2="1372.9688"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.92465149,0,0,0.6070637,279.61143,556.05593)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3910"
|
||||
id="linearGradient3924"
|
||||
x1="3700.292"
|
||||
y1="1343.4175"
|
||||
x2="3700.292"
|
||||
y2="1370.7554"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1062456,0,0,0.86553976,-390.64729,208.90629)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3926"
|
||||
id="linearGradient3940"
|
||||
x1="3703.1587"
|
||||
y1="1402.9609"
|
||||
x2="3703.1587"
|
||||
y2="1434.0886"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.92465149,0,0,0.84590539,279.61143,220.99089)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4014"
|
||||
id="linearGradient4028"
|
||||
x1="1518.2593"
|
||||
y1="314.88751"
|
||||
x2="1518.2593"
|
||||
y2="325.87311"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4034"
|
||||
id="radialGradient4040"
|
||||
cx="1518.6073"
|
||||
cy="319.28983"
|
||||
fx="1518.6073"
|
||||
fy="319.28983"
|
||||
r="2.9673231"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-0.69207955,-0.1201309,0.10291907,-0.59292143,2536.774,691.36823)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.49497475"
|
||||
inkscape:cx="1378.5572"
|
||||
inkscape:cy="276.4325"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer3"
|
||||
showgrid="false"
|
||||
showborder="true"
|
||||
objecttolerance="20"
|
||||
guidetolerance="20"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2038"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:object-paths="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid945"
|
||||
originx="-81.2306"
|
||||
originy="0" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>MacBook Pro with Asahi Linux logo</dc:title>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
<dc:date>2021-01-05</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>marcan</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>CC BY-SA</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>Based on "Apple MacBook Pro SVG" by averywebdesign:
|
||||
https://www.deviantart.com/averywebdesign/art/Apple-MacBook-Pro-SVG-275421698</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Working Vector"
|
||||
style="display:inline"
|
||||
transform="translate(-2714.0744,-280.59375)">
|
||||
<path
|
||||
style="fill:#aca5a4;fill-opacity:1;stroke:none"
|
||||
d="M 2957.7295,280.59375 C 2926.3905,280.59375 2901.167,305.84853 2901.167,337.1875 V 1375.0626 L 4521.4483,1397.0626 V 337.1875 C 4521.4483,305.84853 4496.2248,280.59375 4464.8858,280.59375 Z"
|
||||
id="rect3049-5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ssccsss"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:none"
|
||||
d="M 2966.9905,288.16989 C 2934.9182,288.16989 2910.7634,308.50784 2910.7634,344.36009 L 2906.4703,1397.0626 H 4516.7083 L 4512.1626,344.36009 C 4512.029,313.43181 4490.3065,288.16989 4457.5325,288.16989 Z"
|
||||
id="path3982"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ssccsss"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997" />
|
||||
<path
|
||||
style="fill:#524d49;fill-opacity:1;stroke:none"
|
||||
d="M 2962.5938,284.625 C 2931.6842,284.625 2906.2188,305.92344 2906.2188,341 V 382.8125 1342.1607 1364.5446 C 2906.2188,1399.6212 2931.6842,1404.491 2962.5938,1404.491 H 4464.3438 C 4490.7635,1404.491 4517.1562,1398.2644 4517.1562,1364.5446 V 1323.4732 403.25 341 C 4517.1562,307.28022 4490.7635,284.625 4464.3438,284.625 Z M 2967,288.15625 H 4457.5312 C 4490.3053,288.15625 4512.1562,313.44643 4512.1562,344.375 V 403.25 1323.4732 1361.1696 C 4512.1562,1392.0982 4490.3053,1400.9597 4457.5312,1400.9597 H 2967 C 2934.9277,1400.9597 2910.75,1397.0219 2910.75,1361.1696 V 1342.1607 382.8125 344.375 C 2910.75,308.52275 2934.9277,288.15625 2967,288.15625 Z"
|
||||
id="path3987"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ssccssssccssssssccssssccss"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3902);fill-opacity:1;stroke:none;stroke-width:1"
|
||||
d="M 2714.0744,1371.3804 V 1406.4194 C 2714.7618,1406.8447 2715.4969,1407.2675 2716.2415,1407.6904 H 4705.7737 C 4706.4245,1407.3665 4707.0732,1407.0496 4707.7096,1406.7229 4707.8296,1395.6 4707.5789,1381.8492 4707.594,1371.3804 Z"
|
||||
id="path3102"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3940);fill-opacity:1;stroke:none;stroke-width:1"
|
||||
d="M 2716.2415,1407.6904 C 2733.2024,1421.1157 2824.7166,1433.4476 2858.7377,1434.125 H 4562.5839 C 4604.1547,1431.0326 4687.8009,1420.1531 4705.7737,1407.6904 Z"
|
||||
id="rect3007-1"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3924);fill-opacity:1;stroke:none;stroke-width:1"
|
||||
d="M 3547.91,1371.3804 V 1373.4662 C 3547.91,1384.1224 3562.8697,1396.7245 3576.4895,1396.7245 H 3845.3276 C 3858.9473,1396.7245 3873.907,1384.1224 3873.907,1373.4662 V 1371.3804 Z"
|
||||
id="rect3038-4"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997"
|
||||
sodipodi:nodetypes="csssscc" />
|
||||
<path
|
||||
style="fill:#f4eaea;fill-opacity:1;stroke:none;stroke-width:1"
|
||||
d="M 2948.5469,355.60417 H 4470.0136 V 1310.4297 H 2948.5469 Z"
|
||||
id="rect4077"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<ellipse
|
||||
style="fill:#23201f;fill-opacity:1;stroke:none;stroke-width:0.332343"
|
||||
id="path3978"
|
||||
transform="matrix(-0.99997156,-0.00754152,-0.00901081,0.9999594,0,0)"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997"
|
||||
cx="-3414.0981"
|
||||
cy="1400.1359"
|
||||
rx="10.732503"
|
||||
ry="2.4168887" />
|
||||
<ellipse
|
||||
transform="matrix(-0.99997156,-0.00754152,-0.00901081,0.9999594,0,0)"
|
||||
id="path3980"
|
||||
style="fill:#23201f;fill-opacity:1;stroke:none;stroke-width:0.332343"
|
||||
inkscape:export-filename="C:\Users\Ben Avery\Desktop\MacBook Pro.png"
|
||||
inkscape:export-xdpi="553.15997"
|
||||
inkscape:export-ydpi="553.15997"
|
||||
cx="-4033.0359"
|
||||
cy="1395.4674"
|
||||
rx="10.732503"
|
||||
ry="2.4168887" />
|
||||
<path
|
||||
id="path4012"
|
||||
style="fill:url(#linearGradient4028);fill-opacity:1;stroke:none"
|
||||
transform="translate(2192.2049,0.06313453)"
|
||||
d="M 1524.699,320.31726 A 6.6922607,6.6922607 0 0 1 1518.0067,327.00952 6.6922607,6.6922607 0 0 1 1511.3145,320.31726 6.6922607,6.6922607 0 0 1 1518.0067,313.625 6.6922607,6.6922607 0 0 1 1524.699,320.31726 Z" />
|
||||
<path
|
||||
id="path4032"
|
||||
style="fill:url(#radialGradient4040);fill-opacity:1;stroke:none"
|
||||
transform="translate(2192.2049,0.06313453)"
|
||||
d="M 1520.974,320.31726 A 2.9673231,2.9673231 0 0 1 1518.0067,323.28458 2.9673231,2.9673231 0 0 1 1515.0394,320.31726 2.9673231,2.9673231 0 0 1 1518.0067,317.34994 2.9673231,2.9673231 0 0 1 1520.974,320.31726 Z" />
|
||||
<g
|
||||
style="clip-rule:evenodd;fill-rule:evenodd;stroke-width:1.41157;stroke-linejoin:round;stroke-miterlimit:2"
|
||||
id="g1369"
|
||||
transform="matrix(0.70842931,0,0,0.70842931,3505.6068,629.3435)">
|
||||
<g
|
||||
id="layer8"
|
||||
style="display:inline;stroke-width:1.41157"
|
||||
transform="translate(287.5,550)" />
|
||||
<g
|
||||
id="layer7"
|
||||
style="display:inline;stroke-width:1.41157"
|
||||
transform="translate(287.5,550)">
|
||||
<path
|
||||
id="path1639"
|
||||
style="color:#000000;overflow:visible;fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.41157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M 475,150 H 350 L 349.99805,550 483.73242,455.27344 460,390 515.63477,432.67383 600,372.91602 Z"
|
||||
transform="translate(-350,-550)" />
|
||||
<path
|
||||
style="color:#000000;overflow:visible;fill:#d3506f;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.41157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M -250,-177.08333 0,0 15,-45 21.340931,-300 0,-347.67442 5e-5,-360 -100,-400 H -125 Z"
|
||||
id="path1627" />
|
||||
<path
|
||||
style="color:#000000;overflow:visible;fill:#a61200;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.41157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M -125,-400 0,-442.45283 59.405965,-422.27722 40,-405 125,-400 0,-347.67442"
|
||||
id="path1629" />
|
||||
<path
|
||||
style="color:#000000;overflow:visible;fill:#00a67c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.41157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M -24.999998,-475 H 50 L 40,-525 Z"
|
||||
id="path1631" />
|
||||
<path
|
||||
style="color:#000000;overflow:visible;fill:#edbb60;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.41157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M 4.9025e-5,-400 5e-5,-525 -50.000003,-500 Z"
|
||||
id="path1633" />
|
||||
<path
|
||||
style="color:#000000;overflow:visible;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.41157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M 133.73205,-94.726837 100.91329,-140.33988 -0.00182072,0 0,-347.67442 165.63569,-117.32525 Z"
|
||||
id="path1641" />
|
||||
<path
|
||||
style="color:#000000;overflow:visible;fill:#96caf3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.41157;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M 40,-106.7907 V -225 L 82.566303,-165.89331 Z"
|
||||
id="path1643" />
|
||||
<path
|
||||
style="color:#000000;overflow:visible;opacity:1;fill:#530900;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.54507;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:0.5;paint-order:stroke markers fill"
|
||||
d="M 4.9025e-5,-400 H 125 L 59.405965,-422.27722"
|
||||
id="path1955" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 16 KiB |
BIN
Resources/GNOME/GNOMELaptop.png
Normal file
After Width: | Height: | Size: 990 KiB |
BIN
Resources/GNOME/GNOMEMacBook.png
Normal file
After Width: | Height: | Size: 416 KiB |
BIN
Resources/GNOME/Screenshot.png
Normal file
After Width: | Height: | Size: 1.7 MiB |
BIN
Resources/GNOME/SelectGNOME.png
Normal file
After Width: | Height: | Size: 410 KiB |
BIN
Resources/GNOME/VanillaOS.png
Normal file
After Width: | Height: | Size: 235 KiB |
BIN
Resources/HelloWorld/AdwaitaTemplate.png
Normal file
After Width: | Height: | Size: 10 KiB |
23
Resources/HelloWorld/AdwaitaTemplate.swift
Normal file
@ -0,0 +1,23 @@
|
||||
// The Swift Programming Language
|
||||
// https://docs.swift.org/swift-book
|
||||
|
||||
import Adwaita
|
||||
|
||||
@main
|
||||
struct AdwaitaTemplate: App {
|
||||
|
||||
let id = "io.github.AparokshaUI.AdwaitaTemplate"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "main") { window in
|
||||
Text(Loc.helloWorld)
|
||||
.padding()
|
||||
.topToolbar {
|
||||
ToolbarView(app: app, window: window)
|
||||
}
|
||||
}
|
||||
.defaultSize(width: 450, height: 300)
|
||||
}
|
||||
|
||||
}
|
BIN
Resources/HelloWorld/Builder.png
Normal file
After Width: | Height: | Size: 91 KiB |
BIN
Resources/HelloWorld/ChangeDirectory.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
Resources/HelloWorld/GitClone.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
Resources/HelloWorld/InstallLibadwaita.png
Normal file
After Width: | Height: | Size: 240 KiB |
BIN
Resources/HelloWorld/OpenFolder.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
Resources/HelloWorld/OpenXcode.png
Normal file
After Width: | Height: | Size: 411 KiB |
31
Resources/HelloWorld/Package.swift
Normal file
@ -0,0 +1,31 @@
|
||||
// swift-tools-version: 5.8
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Adwaita Template",
|
||||
platforms: [
|
||||
.macOS(.v13)
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/AparokshaUI/Adwaita", from: "0.2.0"),
|
||||
.package(url: "https://github.com/AparokshaUI/Localized", from: "0.2.0")
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "AdwaitaTemplate",
|
||||
dependencies: [
|
||||
.product(name: "Adwaita", package: "Adwaita"),
|
||||
.product(name: "Localized", package: "Localized")
|
||||
],
|
||||
path: "Sources",
|
||||
resources: [
|
||||
.process("Localized.yml")
|
||||
],
|
||||
plugins: [
|
||||
.plugin(name: "GenerateLocalized", package: "Localized")
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
@ -0,0 +1,52 @@
|
||||
{
|
||||
"app-id": "io.github.AparokshaUI.AdwaitaTemplate",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "46",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.swift5"
|
||||
],
|
||||
"command": "AdwaitaTemplate",
|
||||
"finish-args": [
|
||||
"--share=ipc",
|
||||
"--socket=fallback-x11",
|
||||
"--device=dri",
|
||||
"--socket=wayland"
|
||||
],
|
||||
"build-options": {
|
||||
"append-path": "/usr/lib/sdk/swift5/bin",
|
||||
"prepend-ld-library-path": "/usr/lib/sdk/swift5/lib"
|
||||
},
|
||||
"cleanup": [
|
||||
"/include",
|
||||
"/lib/pkgconfig",
|
||||
"/man",
|
||||
"/share/doc",
|
||||
"/share/gtk-doc",
|
||||
"/share/man",
|
||||
"/share/pkgconfig",
|
||||
"*.la",
|
||||
"*.a"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "AdwaitaTemplate",
|
||||
"builddir": true,
|
||||
"buildsystem": "simple",
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"build-commands": [
|
||||
"swift build -c release --static-swift-stdlib",
|
||||
"install -Dm755 .build/release/AdwaitaTemplate /app/bin/AdwaitaTemplate",
|
||||
"install -Dm644 data/io.github.AparokshaUI.AdwaitaTemplate.metainfo.xml $DESTDIR/app/share/metainfo/io.github.AparokshaUI.AdwaitaTemplate.metainfo.xml",
|
||||
"install -Dm644 data/io.github.AparokshaUI.AdwaitaTemplate.desktop $DESTDIR/app/share/applications/io.github.AparokshaUI.AdwaitaTemplate.desktop",
|
||||
"install -Dm644 data/icons/io.github.AparokshaUI.AdwaitaTemplate.svg $DESTDIR/app/share/icons/hicolor/scalable/apps/io.github.AparokshaUI.AdwaitaTemplate.svg",
|
||||
"install -Dm644 data/icons/io.github.AparokshaUI.AdwaitaTemplate-symbolic.svg $DESTDIR/app/share/icons/hicolor/symbolic/apps/io.github.AparokshaUI.AdwaitaTemplate-symbolic.svg"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
BIN
Resources/Installing/Export.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
Resources/Installing/Software.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
Resources/ModellingData/NewFile.png
Normal file
After Width: | Height: | Size: 105 KiB |
0
Resources/ModellingData/Task1.swift
Normal file
8
Resources/ModellingData/Task2.swift
Normal file
@ -0,0 +1,8 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
struct Task {
|
||||
|
||||
}
|
12
Resources/ModellingData/Task3.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
struct Task {
|
||||
|
||||
var label: String
|
||||
var done: Bool
|
||||
var subtasks: [Self]
|
||||
|
||||
}
|
12
Resources/ModellingData/Task4.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
struct Task {
|
||||
|
||||
var label: String
|
||||
var done = false
|
||||
var subtasks: [Self] = []
|
||||
|
||||
}
|
18
Resources/ModellingData/Task5.swift
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
struct Task {
|
||||
|
||||
var label: String
|
||||
var done = false
|
||||
var subtasks: [Self] = []
|
||||
|
||||
var mixed: Bool {
|
||||
!done && subtasks.contains { subtask in
|
||||
subtask.mixed || subtask.done
|
||||
}
|
||||
}
|
||||
|
||||
}
|
18
Resources/ModellingData/Task6.swift
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
struct Task: Codable {
|
||||
|
||||
var label: String
|
||||
var done = false
|
||||
var subtasks: [Self] = []
|
||||
|
||||
var mixed: Bool {
|
||||
!done && subtasks.contains { subtask in
|
||||
subtask.mixed || subtask.done
|
||||
}
|
||||
}
|
||||
|
||||
}
|
21
Resources/ModellingData/Task7.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Task: Codable, Identifiable {
|
||||
|
||||
var id: UUID = .init()
|
||||
var label: String
|
||||
var done = false
|
||||
var subtasks: [Self] = []
|
||||
|
||||
var mixed: Bool {
|
||||
!done && subtasks.contains { subtask in
|
||||
subtask.mixed || subtask.done
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
Resources/ModellingData/Task8.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Task: Codable, Identifiable, CustomStringConvertible {
|
||||
|
||||
var id: UUID = .init()
|
||||
var label: String
|
||||
var done = false
|
||||
var subtasks: [Self] = []
|
||||
|
||||
var description: String {
|
||||
label
|
||||
}
|
||||
|
||||
var mixed: Bool {
|
||||
!done && subtasks.contains { subtask in
|
||||
subtask.mixed || subtask.done
|
||||
}
|
||||
}
|
||||
|
||||
}
|
17
Resources/Navigation/Array1.swift
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Array.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Array where Element == Task {
|
||||
|
||||
mutating func delete(id: UUID) {
|
||||
self = filter { $0.id != id }
|
||||
for index in indices {
|
||||
self[safe: index]?.delete(id: id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
14
Resources/Navigation/Binding.swift
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Binding.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
extension Binding: CustomStringConvertible where Value: CustomStringConvertible {
|
||||
|
||||
public var description: String {
|
||||
wrappedValue.description
|
||||
}
|
||||
|
||||
}
|
17
Resources/Navigation/ContentView1.swift
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
|
||||
}
|
18
Resources/Navigation/ContentView2.swift
Normal file
@ -0,0 +1,18 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
Text("Hello, world!")
|
||||
.padding()
|
||||
}
|
||||
|
||||
}
|
22
Resources/Navigation/ContentView3.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Task> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
Text(task.label)
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
22
Resources/Navigation/ContentView4.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Task> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
Text(task.label)
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
22
Resources/Navigation/ContentView5.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Task> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: .constant(task.subtasks), destination: $destination, app: app)
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
22
Resources/Navigation/ContentView6.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app)
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
Resources/Navigation/ContentView7.swift
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app) {
|
||||
print("Delete")
|
||||
}
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
Resources/Navigation/ContentView8.swift
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app) {
|
||||
tasks.delete(id: task.wrappedValue.id)
|
||||
}
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
Resources/Navigation/ContentView9.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app) {
|
||||
tasks.delete(id: task.wrappedValue.id)
|
||||
destination.pop()
|
||||
}
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
Resources/Navigation/Localized3.yml
Normal file
@ -0,0 +1,25 @@
|
||||
default: en
|
||||
|
||||
label:
|
||||
en: Label
|
||||
|
||||
cancel:
|
||||
en: Cancel
|
||||
|
||||
add:
|
||||
en: Add
|
||||
|
||||
addTooltip:
|
||||
en: Add Task...
|
||||
|
||||
delete:
|
||||
en: Delete
|
||||
|
||||
deleteTitle:
|
||||
en: Delete Task?
|
||||
|
||||
deleteTooltip:
|
||||
en: Delete Task...
|
||||
|
||||
deleteDescription:
|
||||
en: Deleted tasks and their subtasks cannot be restored
|
28
Resources/Navigation/Localized4.yml
Normal file
@ -0,0 +1,28 @@
|
||||
default: en
|
||||
|
||||
label:
|
||||
en: Label
|
||||
|
||||
cancel:
|
||||
en: Cancel
|
||||
|
||||
add:
|
||||
en: Add
|
||||
|
||||
addTooltip:
|
||||
en: Add Task...
|
||||
|
||||
delete:
|
||||
en: Delete
|
||||
|
||||
deleteTitle:
|
||||
en: Delete Task?
|
||||
|
||||
deleteTooltip:
|
||||
en: Delete Task...
|
||||
|
||||
deleteDescription:
|
||||
en: Deleted tasks and their subtasks cannot be restored
|
||||
|
||||
subtasks(count):
|
||||
en: (count) subtasks
|
29
Resources/Navigation/Localized5.yml
Normal file
@ -0,0 +1,29 @@
|
||||
default: en
|
||||
|
||||
label:
|
||||
en: Label
|
||||
|
||||
cancel:
|
||||
en: Cancel
|
||||
|
||||
add:
|
||||
en: Add
|
||||
|
||||
addTooltip:
|
||||
en: Add Task...
|
||||
|
||||
delete:
|
||||
en: Delete
|
||||
|
||||
deleteTitle:
|
||||
en: Delete Task?
|
||||
|
||||
deleteTooltip:
|
||||
en: Delete Task...
|
||||
|
||||
deleteDescription:
|
||||
en: Deleted tasks and their subtasks cannot be restored
|
||||
|
||||
subtasks(count):
|
||||
en(count == "1"): (count) subtask
|
||||
en: (count) subtasks
|
30
Resources/Navigation/Localized6.yml
Normal file
@ -0,0 +1,30 @@
|
||||
default: en
|
||||
|
||||
label:
|
||||
en: Label
|
||||
|
||||
cancel:
|
||||
en: Cancel
|
||||
|
||||
add:
|
||||
en: Add
|
||||
|
||||
addTooltip:
|
||||
en: Add Task...
|
||||
|
||||
delete:
|
||||
en: Delete
|
||||
|
||||
deleteTitle:
|
||||
en: Delete Task?
|
||||
|
||||
deleteTooltip:
|
||||
en: Delete Task...
|
||||
|
||||
deleteDescription:
|
||||
en: Deleted tasks and their subtasks cannot be restored
|
||||
|
||||
subtasks(count):
|
||||
en(count == "0"): ""
|
||||
en(count == "1"): (count) subtask
|
||||
en: (count) subtasks
|
BIN
Resources/Navigation/Subtasks7.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
21
Resources/Navigation/Subtasks7.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Subtasks.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
@main
|
||||
struct Subtasks: App {
|
||||
|
||||
let id = "io.github.david_swift.Subtasks"
|
||||
var app: GTUIApp!
|
||||
|
||||
var scene: Scene {
|
||||
Window(id: "main") { _ in
|
||||
ContentView(app: app)
|
||||
}
|
||||
.defaultSize(width: 450, height: 300)
|
||||
}
|
||||
|
||||
}
|
29
Resources/Navigation/Task10.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Task: Codable, Identifiable, CustomStringConvertible {
|
||||
|
||||
var id: UUID = .init()
|
||||
var label: String
|
||||
var done = false
|
||||
var subtasks: [Self] = []
|
||||
|
||||
var description: String {
|
||||
label
|
||||
}
|
||||
|
||||
var mixed: Bool {
|
||||
!done && subtasks.contains { subtask in
|
||||
subtask.mixed || subtask.done
|
||||
}
|
||||
}
|
||||
|
||||
mutating func delete(id: UUID) {
|
||||
subtasks.delete(id: id)
|
||||
}
|
||||
|
||||
}
|
32
Resources/Navigation/Task9.swift
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// Task.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Task: Codable, Identifiable, CustomStringConvertible {
|
||||
|
||||
var id: UUID = .init()
|
||||
var label: String
|
||||
var done = false
|
||||
var subtasks: [Self] = []
|
||||
|
||||
var description: String {
|
||||
label
|
||||
}
|
||||
|
||||
var mixed: Bool {
|
||||
!done && subtasks.contains { subtask in
|
||||
subtask.mixed || subtask.done
|
||||
}
|
||||
}
|
||||
|
||||
mutating func delete(id: UUID) {
|
||||
subtasks = subtasks.filter { $0.id != id }
|
||||
for index in subtasks.indices {
|
||||
subtasks[safe: index]?.delete(id: id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
103
Resources/Navigation/TaskList23.swift
Normal file
@ -0,0 +1,103 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Task>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar.start {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: task)
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Task) -> View {
|
||||
ActionRow()
|
||||
.title(task.label)
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active($tasks[id: task.id, default: .init(label: "")].done)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
103
Resources/Navigation/TaskList24.swift
Normal file
@ -0,0 +1,103 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar.start {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
104
Resources/Navigation/TaskList25.swift
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar.start {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.inconsistent(task.wrappedValue.mixed)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
BIN
Resources/Navigation/TaskList26.png
Normal file
After Width: | Height: | Size: 15 KiB |
113
Resources/Navigation/TaskList26.swift
Normal file
@ -0,0 +1,113 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
@State private var showDeleteDialog = false
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
} end: {
|
||||
Button(icon: .default(icon: .userTrash)) {
|
||||
showDeleteDialog = true
|
||||
}
|
||||
.tooltip("")
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
.alertDialog(visible: $showDeleteDialog, heading: "", body: "", id: "delete")
|
||||
.response(Loc.cancel, role: .close) { }
|
||||
.response("", appearance: .destructive, role: .default) { }
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.inconsistent(task.wrappedValue.mixed)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
113
Resources/Navigation/TaskList27.swift
Normal file
@ -0,0 +1,113 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
@State private var showDeleteDialog = false
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
} end: {
|
||||
Button(icon: .default(icon: .userTrash)) {
|
||||
showDeleteDialog = true
|
||||
}
|
||||
.tooltip(Loc.deleteTooltip)
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
.alertDialog(visible: $showDeleteDialog, heading: Loc.deleteTitle, body: Loc.deleteDescription, id: "delete")
|
||||
.response(Loc.cancel, role: .close) { }
|
||||
.response(Loc.delete, appearance: .destructive, role: .default) { }
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.inconsistent(task.wrappedValue.mixed)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
118
Resources/Navigation/TaskList28.swift
Normal file
@ -0,0 +1,118 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
@State private var showDeleteDialog = false
|
||||
var app: GTUIApp
|
||||
var deleteTask: (() -> Void)?
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
} end: {
|
||||
if deleteTask != nil {
|
||||
Button(icon: .default(icon: .userTrash)) {
|
||||
showDeleteDialog = true
|
||||
}
|
||||
.tooltip(Loc.deleteTooltip)
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
.alertDialog(visible: $showDeleteDialog, heading: Loc.deleteTitle, body: Loc.deleteDescription, id: "delete")
|
||||
.response(Loc.cancel, role: .close) { }
|
||||
.response(Loc.delete, appearance: .destructive, role: .default) {
|
||||
deleteTask?()
|
||||
}
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.inconsistent(task.wrappedValue.mixed)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
119
Resources/Navigation/TaskList29.swift
Normal file
@ -0,0 +1,119 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
@State private var showDeleteDialog = false
|
||||
var app: GTUIApp
|
||||
var deleteTask: (() -> Void)?
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
} end: {
|
||||
if deleteTask != nil {
|
||||
Button(icon: .default(icon: .userTrash)) {
|
||||
showDeleteDialog = true
|
||||
}
|
||||
.tooltip(Loc.deleteTooltip)
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
.alertDialog(visible: $showDeleteDialog, heading: Loc.deleteTitle, body: Loc.deleteDescription, id: "delete")
|
||||
.response(Loc.cancel, role: .close) { }
|
||||
.response(Loc.delete, appearance: .destructive, role: .default) {
|
||||
deleteTask?()
|
||||
}
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.subtitle("5 subtasks")
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.inconsistent(task.wrappedValue.mixed)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
BIN
Resources/Navigation/TaskList30.png
Normal file
After Width: | Height: | Size: 18 KiB |
119
Resources/Navigation/TaskList30.swift
Normal file
@ -0,0 +1,119 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
@State private var showDeleteDialog = false
|
||||
var app: GTUIApp
|
||||
var deleteTask: (() -> Void)?
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
} end: {
|
||||
if deleteTask != nil {
|
||||
Button(icon: .default(icon: .userTrash)) {
|
||||
showDeleteDialog = true
|
||||
}
|
||||
.tooltip(Loc.deleteTooltip)
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
.alertDialog(visible: $showDeleteDialog, heading: Loc.deleteTitle, body: Loc.deleteDescription, id: "delete")
|
||||
.response(Loc.cancel, role: .close) { }
|
||||
.response(Loc.delete, appearance: .destructive, role: .default) {
|
||||
deleteTask?()
|
||||
}
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.subtitle("\(task.wrappedValue.subtasks.count) subtasks")
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.inconsistent(task.wrappedValue.mixed)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
119
Resources/Navigation/TaskList31.swift
Normal file
@ -0,0 +1,119 @@
|
||||
//
|
||||
// TaskList.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct TaskList: View {
|
||||
|
||||
@Binding var tasks: [Task]
|
||||
@Binding var destination: NavigationStack<Binding<Task>>
|
||||
@State private var showAddDialog = false
|
||||
@State private var addDialogText = ""
|
||||
@State private var focusEntry: Signal = .init()
|
||||
@State private var showDeleteDialog = false
|
||||
var app: GTUIApp
|
||||
var deleteTask: (() -> Void)?
|
||||
|
||||
var view: Body {
|
||||
ScrollView {
|
||||
list
|
||||
}
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(icon: .default(icon: .listAdd)) {
|
||||
showAddDialog = true
|
||||
focusEntry.signal()
|
||||
}
|
||||
.keyboardShortcut("n".ctrl(), app: app)
|
||||
.tooltip(Loc.addTooltip)
|
||||
} end: {
|
||||
if deleteTask != nil {
|
||||
Button(icon: .default(icon: .userTrash)) {
|
||||
showDeleteDialog = true
|
||||
}
|
||||
.tooltip(Loc.deleteTooltip)
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog(visible: $showAddDialog.onSet { _ in cancel() }, id: "add") {
|
||||
dialog
|
||||
}
|
||||
.alertDialog(visible: $showDeleteDialog, heading: Loc.deleteTitle, body: Loc.deleteDescription, id: "delete")
|
||||
.response(Loc.cancel, role: .close) { }
|
||||
.response(Loc.delete, appearance: .destructive, role: .default) {
|
||||
deleteTask?()
|
||||
}
|
||||
}
|
||||
|
||||
var list: View {
|
||||
List(tasks, selection: nil) { task in
|
||||
taskRow(task: $tasks[id: task.id, default: .init(label: "")])
|
||||
}
|
||||
.valign(.start)
|
||||
.padding(20)
|
||||
.style("boxed-list")
|
||||
.frame(maxWidth: 500)
|
||||
}
|
||||
|
||||
var dialog: View {
|
||||
Form {
|
||||
EntryRow(Loc.label, text: $addDialogText)
|
||||
.entryActivated {
|
||||
add()
|
||||
}
|
||||
.frame(minWidth: 250)
|
||||
.focus(focusEntry)
|
||||
}
|
||||
.padding(20)
|
||||
.valign(.start)
|
||||
.topToolbar {
|
||||
HeaderBar {
|
||||
Button(Loc.cancel) {
|
||||
cancel()
|
||||
}
|
||||
} end: {
|
||||
Button(Loc.add) {
|
||||
add()
|
||||
}
|
||||
.style("suggested-action")
|
||||
}
|
||||
.showEndTitleButtons(false)
|
||||
}
|
||||
}
|
||||
|
||||
func taskRow(task: Binding<Task>) -> View {
|
||||
ActionRow()
|
||||
.title(task.wrappedValue.label)
|
||||
.subtitle(Loc.subtasks(count: task.wrappedValue.subtasks.count))
|
||||
.prefix {
|
||||
CheckButton()
|
||||
.active(task.done)
|
||||
.inconsistent(task.wrappedValue.mixed)
|
||||
.style("selection-mode")
|
||||
.valign(.center)
|
||||
}
|
||||
.suffix {
|
||||
ButtonContent()
|
||||
.iconName(Icon.default(icon: .goNext).string)
|
||||
}
|
||||
.activatableWidget {
|
||||
Button()
|
||||
.activate {
|
||||
destination.push(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
showAddDialog = false
|
||||
addDialogText = ""
|
||||
}
|
||||
|
||||
func add() {
|
||||
tasks.append(.init(label: addDialogText))
|
||||
cancel()
|
||||
}
|
||||
|
||||
}
|
64
Resources/Overview.svg
Normal file
After Width: | Height: | Size: 11 MiB |
29
Resources/Polishing/ContentView10.swift
Normal file
@ -0,0 +1,29 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: WindowView {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app) {
|
||||
tasks.delete(id: task.wrappedValue.id)
|
||||
destination.pop()
|
||||
}
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
func window(_ window: Window) -> Window {
|
||||
window
|
||||
}
|
||||
|
||||
}
|
BIN
Resources/Polishing/ContentView11.png
Normal file
After Width: | Height: | Size: 16 KiB |
34
Resources/Polishing/ContentView11.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: WindowView {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
@State("width") private var width = 500
|
||||
@State("height") private var height = 400
|
||||
@State("maximized") private var maximized = false
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app) {
|
||||
tasks.delete(id: task.wrappedValue.id)
|
||||
destination.pop()
|
||||
}
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
func window(_ window: Window) -> Window {
|
||||
window
|
||||
.size(width: $width, height: $height)
|
||||
.maximized($maximized)
|
||||
}
|
||||
|
||||
}
|
34
Resources/Polishing/ContentView12.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: WindowView {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
@State("width") private var width = 500
|
||||
@State("height") private var height = 450
|
||||
@State("maximized") private var maximized = false
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app) {
|
||||
tasks.delete(id: task.wrappedValue.id)
|
||||
destination.pop()
|
||||
}
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
}
|
||||
|
||||
func window(_ window: Window) -> Window {
|
||||
window
|
||||
.size(width: $width, height: $height)
|
||||
.maximized($maximized)
|
||||
}
|
||||
|
||||
}
|
35
Resources/Polishing/ContentView13.swift
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// Subtasks
|
||||
//
|
||||
|
||||
import Adwaita
|
||||
|
||||
struct ContentView: WindowView {
|
||||
|
||||
@State("tasks") private var tasks: [Task] = []
|
||||
@State private var destination: NavigationStack<Binding<Task>> = .init()
|
||||
@State("width") private var width = 500
|
||||
@State("height") private var height = 450
|
||||
@State("maximized") private var maximized = false
|
||||
var app: GTUIApp
|
||||
|
||||
var view: Body {
|
||||
NavigationView($destination, "Subtasks") { task in
|
||||
TaskList(tasks: task.subtasks, destination: $destination, app: app) {
|
||||
tasks.delete(id: task.wrappedValue.id)
|
||||
destination.pop()
|
||||
}
|
||||
} initialView: {
|
||||
TaskList(tasks: $tasks, destination: $destination, app: app)
|
||||
}
|
||||
.frame(minWidth: 250, minHeight: 200)
|
||||
}
|
||||
|
||||
func window(_ window: Window) -> Window {
|
||||
window
|
||||
.size(width: $width, height: $height)
|
||||
.maximized($maximized)
|
||||
}
|
||||
|
||||
}
|
BIN
Resources/Polishing/Localized7.png
Normal file
After Width: | Height: | Size: 16 KiB |
33
Resources/Polishing/Localized7.yml
Normal file
@ -0,0 +1,33 @@
|
||||
default: en
|
||||
|
||||
label:
|
||||
en: Label
|
||||
|
||||
cancel:
|
||||
en: Cancel
|
||||
|
||||
add:
|
||||
en: Add
|
||||
|
||||
addTooltip:
|
||||
en: Add Task...
|
||||
|
||||
delete:
|
||||
en: Delete
|
||||
|
||||
deleteTitle:
|
||||
en: Delete Task?
|
||||
|
||||
deleteTooltip:
|
||||
en: Delete Task...
|
||||
|
||||
deleteDescription:
|
||||
en: Deleted tasks and their subtasks cannot be restored
|
||||
|
||||
subtasks(count):
|
||||
en(count == "0"): ""
|
||||
en(count == "1"): (count) subtask
|
||||
en: (count) subtasks
|
||||
|
||||
about:
|
||||
en: About Subtasks
|