Set up documentation

This commit is contained in:
david-swift 2024-06-30 12:08:02 +02:00
parent 83a03441b5
commit 2551c0a14d
18 changed files with 122 additions and 219 deletions

View File

@ -1,29 +0,0 @@
name: Components request
description: Suggest an idea for a new component
title: Description of the component request
labels: enhancement
body:
- type: textarea
attributes:
label: Why would you like to add a new component?
placeholder: >-
A clear and concise description of why the component should be added.
validations:
required: false
- type: textarea
attributes:
label: Describe your idea for the implementation.
placeholder: >-
What could the implementation be like in Adwaita?
validations:
required: false
- type: textarea
attributes:
label: Additional context
placeholder: >-
Add any other context about the component request here.
validations:
required: false

View File

@ -1,5 +1,4 @@
## Steps
- [ ] Add your name or username and a link to your GitHub profile into the [Contributors.md][1] file.
- [ ] Build the project on your machine. If it does not compile, fix the errors.
- [ ] Describe the purpose and approach of your pull request below.
- [ ] Submit the pull request. Thank you very much for your contribution!
@ -10,5 +9,3 @@ _If there is a related issue, add the link._
## Approach
_Describe how this pull request solves the problem or adds the feature._
[1]: /Contributors.md

View File

@ -3,7 +3,6 @@ name: Deploy Docs
on:
push:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
@ -22,31 +21,20 @@ jobs:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Install Libadwaita
run: |
brew update
brew install libadwaita
sed -i '' 's/-I..includedir.//g' $(brew --prefix)/Library/Homebrew/os/mac/pkgconfig/*/libffi.pc
- name: Clone DocC Repo
run: |
git clone https://github.com/AparokshaUI/Adwaita.docc Sources/Adwaita/Adwaita.docc
rm Sources/Adwaita/Adwaita.docc/LICENSE.md
rm Sources/Adwaita/Adwaita.docc/README.md
y | rm -r Sources/Adwaita/Adwaita.docc/.git
- name: Build Docs
run: |
xcrun xcodebuild docbuild \
-scheme Adwaita \
-scheme Meta \
-destination 'generic/platform=macOS' \
-derivedDataPath "$PWD/.derivedData"
-derivedDataPath "$PWD/.derivedData" \
-skipPackagePluginValidation
xcrun docc process-archive transform-for-static-hosting \
"$PWD/.derivedData/Build/Products/Debug/Adwaita.doccarchive" \
"$PWD/.derivedData/Build/Products/Debug/Meta.doccarchive" \
--output-path "docs" \
--hosting-base-path "adwaita-swift"
--hosting-base-path "Meta"
- name: Modify Docs
run: |
echo "<script>window.location.href += \"/documentation/adwaita\"</script>" > docs/index.html;
sed -i '' 's/#06f/#ea3358/g' docs/css/documentation-topic~topic~tutorials-overview.d6f5411c.css
echo "<script>window.location.href += \"/documentation/meta\"</script>" > docs/index.html;
sed -i '' 's/,2px/,10px/g' docs/css/index.038e887c.css
- name: Upload Artifact
uses: actions/upload-pages-artifact@v3

View File

@ -1,4 +1,4 @@
// swift-tools-version: 5.10
// swift-tools-version: 5.9
//
// Package.swift
// Meta

134
README.md
View File

@ -1,141 +1,57 @@
<p align="center">
<img width="256" alt="Adwaita Icon" src="Icons/AdwaitaIcon.png">
<h1 align="center">Adwaita for Swift</h1>
<h1 align="center">Meta</h1>
</p>
<p align="center">
<a href="https://aparokshaui.github.io/adwaita-swift/">
<a href="https://aparokshaui.github.io/meta/">
Documentation
</a>
·
<a href="https://github.com/AparokshaUI/Adwaita">
<a href="https://github.com/AparokshaUI/Meta">
GitHub
</a>
</p>
_Adwaita_ is a framework for creating user interfaces for GNOME with an API similar to SwiftUI.
The following code:
```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
}
}
}
}
```
Describes a simple counter view:
![Counter Example][image-1]
More examples are available in the [demo app][1].
_Meta_ is a framework allowing the creation of user interface (UI) frameworks in Swift.
## Table of Contents
- [Goals][2]
- [Widgets][3]
- [Installation][4]
- [Usage][5]
- [Thanks][6]
- [Overview](#overview)
- [Usage](#usage)
- [Thanks](#thanks)
## Goals
## Overview
_Adwaita_s main goal is to provide an easy-to-use interface for creating GNOME apps. The backend should stay as simple as possible, while not limiting the possibilities there are with [Libadwaita][7] and [GTK][8].
_Meta_ follows the following principles:
If you want to use _Adwaita_ in a project, but there are widgets missing, open an [issue on GitHub][9].
- It is a **declarative** framework, meaning that instead of writing _how_ to construct a user interface, you write _what_ it looks like.
- The user interface is treated as a function of its **state**. Instead of directly modifying the UI, modify its state to update views.
- Multiple UI frameworks can be used in the same code, but the **selection of the framework** happens when executing the app. This enables the creation of cross-platform UI frameworks combining several UI frameworks rendering always with the same backend.
Find more information about the project's motivation in [this blog post](https://www.swift.org/blog/adwaita-swift/).
It knows the following layers of UI:
## Widgets
- An app is the entry point of the executable, containing the windows.
- A window is a container holding one or multiple views.
- A view is a part of the actual UI inside a window, another view or a menu.
- A menu is a list of buttons, other menus, and views. Certain views (such as menu buttons) allow menus to be used.
An overview of the widgets supported by _Adwaita_ is available [here](user-manual/Information/Widgets.md).
Detailed information can be found in the [docs](https://aparokshaui.github.io/meta/).
## Installation
### Dependencies
#### Flatpak
## Usage
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.
_Meta_ can be used for creating UI frameworks in Swift which can then be used to create apps.
#### Directly on System
Follow those steps if you want to create a UI framework.
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][11].
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")
.package(url: "https://github.com/AparokshaUI/Meta", from: "0.1.0")
```
## Usage
I recommend using the [template repository](https://github.com/AparokshaUI/AdwaitaTemplate) as a starting point.
Follow the [interactive tutorial](https://aparokshaui.github.io/adwaita-swift/tutorials/table-of-contents) or [read the docs](https://aparokshaui.github.io/adwaita-swift/documentation/adwaita) in order to get to know _Adwaita for Swift_.
## Thanks
### Dependencies
- [XMLCoder][18] licensed under the [MIT license][19]
- [Levenshtein Transformations](https://github.com/david-swift/LevenshteinTransformations) licensed under the [MIT license](https://github.com/david-swift/LevenshteinTransformations/blob/main/LICENSE.md)
### Other Thanks
- The [contributors][20]
- The auto-generation of widgets is based on [Swift Cross UI](https://github.com/stackotter/swift-cross-ui)
- [SwiftLint][21] for checking whether code style conventions are violated
- The programming language [Swift][22]
[1]: Tests/
[2]: #goals
[3]: #widgets
[4]: #installation
[5]: #usage
[6]: #thanks
[7]: https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/index.html
[8]: https://docs.gtk.org/gtk4/
[9]: https://github.com/AparokshaUI/Adwaita/issues
[10]: https://github.com/AparokshaUI/Libadwaita
[11]: https://brew.sh
[12]: user-manual/GettingStarted.md
[13]: user-manual/Basics/HelloWorld.md
[14]: user-manual/Basics/CreatingViews.md
[15]: user-manual/Basics/Windows.md
[16]: user-manual/Basics/KeyboardShortcuts.md
[17]: user-manual/Advanced/CreatingWidgets.md
[18]: https://github.com/CoreOffice/XMLCoder
[19]: https://github.com/CoreOffice/XMLCoder/blob/main/LICENSE
[20]: Contributors.md
[21]: https://github.com/realm/SwiftLint
[22]: https://github.com/apple/swift
[23]: https://github.com/SourceDocs/SourceDocs
[image-1]: Icons/Counter.png
[image-2]: Icons/Demo.png
- [DocC](https://github.com/apple/swift-docc) used for the documentation
- [SwiftLint](https://github.com/realm/SwiftLint) for checking whether code style conventions are violated
- The programming language [Swift](https://github.com/swiftlang/swift)

18
Sources/Meta.docc/Meta.md Normal file
View File

@ -0,0 +1,18 @@
# ``Meta``
_Meta_ is a framework allowing the creation of user interface (UI) frameworks in Swift.
## Overview
_Meta_ follows the following principles:
- It is a **declarative** framework, meaning that instead of writing _how_ to construct a user interface, you write _what_ it looks like.
- The user interface is treated as a function of its **state**. Instead of directly modifying the UI, modify its state to update views.
- Multiple UI frameworks can be used in the same code, but the **selection of the framework** happens when executing the app. This enables the creation of cross-platform UI frameworks combining several UI frameworks rendering always with the same backend.
It knows the following layers of UI:
- An app is the entry point of the executable, containing the windows.
- A window is a container holding one or multiple views.
- A view is a part of the actual UI inside a window, another view or a menu.
- A menu is a list of buttons, other menus, and views. Certain views (such as menu buttons) allow menus to be used.

View File

@ -0,0 +1,46 @@
{
"theme": {
"border-radius": "10px",
"button": {
"border-radius": "20px"
},
"color": {
"button-background": "#ea3358",
"button-background-active": "#ea3358",
"button-background-hover": "#fc557a",
"button-text": "#ffffff",
"header": "#7f313b",
"documentation-intro-accent": "var(--color-header)",
"documentation-intro-fill": "radial-gradient(circle at top, var(--color-header) 30%, #000 100%)",
"link": "#ea3358",
"nav-link-color": "#ea3358",
"nav-dark-link-color": "#ea3358",
"tutorials-overview-link": "#fb4469",
"step-background": {
"light": "#fffaff",
"dark": "#302c2d"
},
"step-focused": "#ea3358",
"tabnav-item-border-color": "#ea3358",
"tutorial-background": {
"light": "",
"dark": "#1d1d1f"
},
"tutorials-overview-background": "linear-gradient(180deg, rgba(43,20,23,1) 0%, rgba(41, 3, 8, 0.808) 60%, rgba(0,0,0,1) 100%)",
"fill-light-blue-secondary": "#ea3358",
"fill-blue": "#ea3358",
"figure-blue": "#ea3358",
"standard-blue-documentation-intro-fill": "#ea3358",
"figure-blue": "#ea3358",
"tutorial-hero-background": "#100a0b",
"navigator-item-hover": {
"light": "#ea335815",
"dark": "#7f313b"
}
},
"additionalProperties": "#ea3358",
"tutorial-step": {
"border-radius": "15px"
}
}
}

View File

@ -71,7 +71,7 @@ public struct Binding<Value> {
private var handlers: [(Value) -> Void] = []
/// Get a property of any content of a `Binding` as a `Binding`.
/// - Parameter dynamicMember: The path to the member.
/// - Parameter keyPath: The path to the member.
/// - Returns: The binding.
public subscript<Subject>(dynamicMember keyPath: WritableKeyPath<Value, Subject>) -> Binding<Subject> {
.init {

View File

@ -54,6 +54,7 @@ extension Array: AnyView where Element == AnyView {
/// - Parameters:
/// - modifiers: Modify views before generating the storages.
/// - type: The type of the widgets.
/// - Returns: The storages.
public func storages<WidgetType>(
modifiers: [(AnyView) -> AnyView],
type: WidgetType.Type

View File

@ -1,35 +0,0 @@
//
// DefaultStringInterpolation.swift
// Meta
//
// Created by david-swift on 26.05.24.
//
// Thanks to Eneko Alonso, Pyry Jahkola, cukr for the comments in this Swift forum discussion:
// "Multi-line string nested indentation with interpolation"
// https://forums.swift.org/t/multi-line-string-nested-indentation-with-interpolation/36933
//
extension DefaultStringInterpolation {
/// Preserve the indentation in a multi line string.
/// - Parameter string: The string.
///
/// Use it the following way:
/// """
/// Hello
/// \(indented: "World\n Test")
/// """
public mutating func appendInterpolation(indented string: String) {
// swiftlint:disable compiler_protocol_init
let indent = String(stringInterpolation: self).reversed().prefix { " \t".contains($0) }
// swiftlint:enable compiler_protocol_init
if indent.isEmpty {
appendInterpolation(string)
} else {
appendLiteral(
string.split(separator: "\n", omittingEmptySubsequences: false).joined(separator: "\n" + indent)
)
}
}
}

View File

@ -7,7 +7,7 @@
extension String {
/// A label for main content in a view storage.
static var mainContent: Self { "main" }
/// An identifier for main content in a view storage.
public static var mainContent: Self { "main" }
}

View File

@ -51,7 +51,7 @@ extension AnyView {
/// Wrap the view into a widget.
/// - Parameter modifiers: Modify views before being updated.
/// - Returns: The widget.
public func widget(modifiers: [(AnyView) -> AnyView]) -> Widget {
func widget(modifiers: [(AnyView) -> AnyView]) -> Widget {
let modified = getModified(modifiers: modifiers)
if let peer = modified as? Widget {
return peer

View File

@ -11,7 +11,7 @@ import Foundation
@resultBuilder
public enum ViewBuilder {
/// A component used in the ``ArrayBuilder``.
/// A component used in the ``ViewBuilder``.
public enum Component {
/// A view as a component.

View File

@ -5,19 +5,22 @@
// Created by david-swift on 26.05.24.
//
/// Store a rendered view in a view storage.
/// Store a reference to a rendered view in a view storage.
public class ViewStorage {
/// The pointer.
///
/// It can be a C pointer, a Swift class, or other information depending on the backend.
/// Some convenience widgets do not need a pointer to a native framework at all.
public var pointer: Any?
/// The view's content.
/// The view's content for container widgets.
public var content: [String: [ViewStorage]]
/// The view's state (used in `StateWrapper`).
var state: [String: StateProtocol] = [:]
/// Other properties.
/// Various properties of a widget.
public var fields: [String: Any] = [:]
/// The pointer as an opaque pointer.
/// The pointer as an opaque pointer, as this is needed with backends interoperating with C or C++.
public var opaquePointer: OpaquePointer? {
get {
pointer as? OpaquePointer
@ -30,7 +33,7 @@ public class ViewStorage {
/// Initialize a view storage.
/// - Parameters:
/// - pointer: The pointer to the widget, its type depends on the backend.
/// - content: The view's content.
/// - content: The view's content for container widgets.
public init(
_ pointer: Any?,
content: [String: [ViewStorage]] = [:]

View File

@ -5,7 +5,10 @@
// Created by david-swift on 26.05.24.
//
/// A widget is a view that know about its GTUI widget.
/// A widget is a view that know about its native backend widget.
///
/// It enables the translation from the declarative definition to the creation
/// and updating of widgets in an imperative way.
public protocol Widget: AnyView {
/// The view storage.

View File

@ -8,19 +8,16 @@
import Observation
/// A storage for `@State` properties.
public struct StateWrapper: ConvenienceWidget {
struct StateWrapper: ConvenienceWidget {
/// The content.
var content: () -> Body
/// The state information (from properties with the `State` wrapper).
var state: [String: StateProtocol] = [:]
/// The identifier of the field storing whether to update the wrapper's content.
private var updateID: String { "update" }
/// Initialize a `StateWrapper`.
/// - Parameter content: The view content.
public init(@ViewBuilder content: @escaping () -> Body) {
init(@ViewBuilder content: @escaping () -> Body) {
self.content = content
}
@ -39,14 +36,13 @@ public struct StateWrapper: ConvenienceWidget {
/// - modifiers: Modify views before being updated.
/// - updateProperties: Whether to update properties.
/// - type: The type of the widgets.
public func update<WidgetType>(
func update<WidgetType>(
_ storage: ViewStorage,
modifiers: [(AnyView) -> AnyView],
updateProperties: Bool,
type: WidgetType.Type
) {
var updateProperties = updateProperties ? true : (storage.fields[updateID] as? Bool ?? false)
storage.fields[updateID] = false
var updateProperties = updateProperties
for property in state {
if let oldID = storage.state[property.key]?.id {
StateManager.changeID(old: oldID, new: property.value.id)
@ -68,7 +64,7 @@ public struct StateWrapper: ConvenienceWidget {
/// - modifiers: Modify views before being updated.
/// - type: The type of the widgets.
/// - Returns: The view storage.
public func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
func container<WidgetType>(modifiers: [(AnyView) -> AnyView], type: WidgetType.Type) -> ViewStorage {
let content = content().storages(modifiers: modifiers, type: type)
let storage = ViewStorage(nil, content: [.mainContent: content])
storage.state = state

View File

@ -5,7 +5,7 @@
// Created by david-swift on 27.05.24.
//
/// Wrap a view into a single widget.
/// Wrap a body into a single widget.
public struct Wrapper: ConvenienceWidget {
/// The content.

View File

@ -65,12 +65,11 @@ struct DemoApp {
DemoView().updateStorage(storage, modifiers: [], updateProperties: true, type: backendType)
}
StateManager.addUpdateHandler { _ in
DemoView().updateStorage(storage, modifiers: [], updateProperties: false, type: backendType)
StateManager.addUpdateHandler { force in
DemoView().updateStorage(storage, modifiers: [], updateProperties: force, type: backendType)
}
sleep(2)
DemoView().updateStorage(storage, modifiers: [], updateProperties: true, type: backendType)
}
}