From 921f025e39b4431b2f4591b3703a48393287647e Mon Sep 17 00:00:00 2001 From: david-swift Date: Thu, 12 Oct 2023 22:15:15 +0200 Subject: [PATCH] Update demo and fix bugs - Fix build optional in ViewBuilder - Fix maximum size frame modifier - Improve inspector wrapper - Improve header bar - Improve status page - Improve the naming of some elements --- Documentation/Reference/README.md | 2 + Documentation/Reference/extensions/View.md | 14 ++-- .../Reference/extensions/WindowScene.md | 2 +- .../Reference/protocols/WindowSceneGroup.md | 2 +- Documentation/Reference/structs/Clamp.md | 25 +++++++ Documentation/Reference/structs/HeaderBar.md | 21 +++++- .../Reference/structs/InspectorWrapper.md | 2 +- Documentation/Reference/structs/List.md | 2 +- .../Reference/typealiases/GTUIWindow.md | 5 ++ Icons/Demo.png | Bin 22184 -> 26595 bytes .../Model/User Interface/GTUIWindow.swift | 11 +++ .../Model/User Interface/ViewBuilder.swift | 6 +- .../Model/User Interface/WindowScene.swift | 2 +- .../User Interface/WindowSceneGroup.swift | 4 +- .../Model/User Interface/WindowStorage.swift | 4 +- Sources/Adwaita/View/HeaderBar.swift | 36 +++++++++- Sources/Adwaita/View/List.swift | 2 +- Sources/Adwaita/View/Modifiers/Clamp.swift | 49 +++++++++++++ .../View/Modifiers/InspectorWrapper.swift | 12 +--- Sources/Adwaita/View/StatusPage.swift | 9 ++- Sources/Adwaita/Window/Window.swift | 10 +-- Tests/CounterDemo.swift | 37 ++++++---- Tests/Demo.swift | 36 ++++++---- Tests/DiceDemo.swift | 39 ++++++++++ Tests/Page.swift | 38 +++++++++- Tests/ToolbarDemo.swift | 68 ++++++++++++++++++ Tests/TransitionDemo.swift | 47 ++++++++++++ Tests/WelcomeDemo.swift | 29 -------- Tests/WindowsDemo.swift | 38 +++++++--- 29 files changed, 451 insertions(+), 101 deletions(-) create mode 100644 Documentation/Reference/structs/Clamp.md create mode 100644 Documentation/Reference/typealiases/GTUIWindow.md create mode 100644 Sources/Adwaita/Model/User Interface/GTUIWindow.swift create mode 100644 Sources/Adwaita/View/Modifiers/Clamp.swift create mode 100644 Tests/DiceDemo.swift create mode 100644 Tests/ToolbarDemo.swift create mode 100644 Tests/TransitionDemo.swift delete mode 100644 Tests/WelcomeDemo.swift diff --git a/Documentation/Reference/README.md b/Documentation/Reference/README.md index ace6cf4..ed8b37e 100644 --- a/Documentation/Reference/README.md +++ b/Documentation/Reference/README.md @@ -13,6 +13,7 @@ - [Binding](structs/Binding.md) - [Button](structs/Button.md) +- [Clamp](structs/Clamp.md) - [EitherView](structs/EitherView.md) - [HStack](structs/HStack.md) - [HeaderBar](structs/HeaderBar.md) @@ -57,6 +58,7 @@ ## Typealiases - [Body](typealiases/Body.md) +- [GTUIWindow](typealiases/GTUIWindow.md) - [Scene](typealiases/Scene.md) - [SceneBuilder](typealiases/SceneBuilder.md) diff --git a/Documentation/Reference/extensions/View.md b/Documentation/Reference/extensions/View.md index ffdce33..cc9ab2a 100644 --- a/Documentation/Reference/extensions/View.md +++ b/Documentation/Reference/extensions/View.md @@ -18,9 +18,15 @@ Update a storage to a view. Get a storage. - Returns: The storage. +### `frame(maxSize:)` + +Set the view's maximal size. +- Parameter maxSize: The maximal size. +- Returns: A view. + ### `inspect(_:)` -Modify a GTUI widget before being displayed. +Modify a GTUI widget before being displayed and when being updated. - Parameter modify: Modify the widget. - Returns: A view. @@ -64,12 +70,6 @@ Set the view's minimal width or height. - minHeight: The minimal height. - Returns: A view. -### `frame(maxSize:)` - -Set the view's maximal size. -- Parameter maxSize: The maximal size. -- Returns: A view. - ### `transition(_:)` Set the view's transition. diff --git a/Documentation/Reference/extensions/WindowScene.md b/Documentation/Reference/extensions/WindowScene.md index 82aa4d8..eb8d999 100644 --- a/Documentation/Reference/extensions/WindowScene.md +++ b/Documentation/Reference/extensions/WindowScene.md @@ -3,6 +3,6 @@ # `WindowScene` ## Properties -### `body` +### `scene` The window scene's body is itself. diff --git a/Documentation/Reference/protocols/WindowSceneGroup.md b/Documentation/Reference/protocols/WindowSceneGroup.md index dfde548..a2bd7e0 100644 --- a/Documentation/Reference/protocols/WindowSceneGroup.md +++ b/Documentation/Reference/protocols/WindowSceneGroup.md @@ -5,6 +5,6 @@ A structure conforming to `WindowScene` can be added to an app's `scene`. ## Properties -### `body` +### `scene` The group's content. diff --git a/Documentation/Reference/structs/Clamp.md b/Documentation/Reference/structs/Clamp.md new file mode 100644 index 0000000..7ee1b9f --- /dev/null +++ b/Documentation/Reference/structs/Clamp.md @@ -0,0 +1,25 @@ +**STRUCT** + +# `Clamp` + +A horizontal AdwClamp equivalent. + +## Properties +### `content` + +The content. + +### `maxSize` + +The maximum size. + +## Methods +### `update(_:)` + +Update a view storage. +- Parameter storage: The view storage. + +### `container()` + +Get a view storage. +- Returns: The view storage. diff --git a/Documentation/Reference/structs/HeaderBar.md b/Documentation/Reference/structs/HeaderBar.md index ed9e65b..425a3db 100644 --- a/Documentation/Reference/structs/HeaderBar.md +++ b/Documentation/Reference/structs/HeaderBar.md @@ -13,6 +13,14 @@ The start content of the header bar. The end content of the header bar. +### `titleButtons` + +Whether the title buttons are visible. + +### `headerBarTitle` + +The view acting as the title of the header bar. + ### `startID` The start content's id. @@ -21,11 +29,16 @@ The start content's id. The end content's id. +### `titleID` + +The title's id. + ## Methods -### `init(start:end:)` +### `init(titleButtons:start:end:)` Initialize a header bar. - Parameters: + - titleButtons: Whether the title buttons (e.g. close button) are visible. - start: The start content. - end: The end content. @@ -55,3 +68,9 @@ Update a header bar's view storage. Get the container for a header bar. - Returns: The view storage. + +### `headerBarTitle(view:)` + +Set the title widget for the header bar. +- Parameter view: The widget in the header bar. +- Returns: The header bar. diff --git a/Documentation/Reference/structs/InspectorWrapper.md b/Documentation/Reference/structs/InspectorWrapper.md index 38bfaa4..ffe72f5 100644 --- a/Documentation/Reference/structs/InspectorWrapper.md +++ b/Documentation/Reference/structs/InspectorWrapper.md @@ -2,7 +2,7 @@ # `InspectorWrapper` -A widget which executes a custom code on the GTUI widget when being created. +A widget which executes a custom code on the GTUI widget when being created and updated. ## Properties ### `modify` diff --git a/Documentation/Reference/structs/List.md b/Documentation/Reference/structs/List.md index 9cf821e..10fdf90 100644 --- a/Documentation/Reference/structs/List.md +++ b/Documentation/Reference/structs/List.md @@ -20,7 +20,7 @@ The identifier of the selected element. ## Methods ### `init(_:selection:content:)` -Initialize `ForEach`. +Initialize `List`. - Parameters: - elements: The elements. - selection: The identifier of the selected element. diff --git a/Documentation/Reference/typealiases/GTUIWindow.md b/Documentation/Reference/typealiases/GTUIWindow.md new file mode 100644 index 0000000..d80e760 --- /dev/null +++ b/Documentation/Reference/typealiases/GTUIWindow.md @@ -0,0 +1,5 @@ +**TYPEALIAS** + +# `GTUIWindow` + +A GTUI window. \ No newline at end of file diff --git a/Icons/Demo.png b/Icons/Demo.png index 96f06e93c940cc92fd1e72ae668ab6ae043c2d63..eb77fb1be346dd5b41c0e7234da8e974342bde9f 100644 GIT binary patch literal 26595 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%0A>@-5a@nG7g2A@T-Sel{;^Hd6HesO*3%X(Q}u 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 YENEm&``pKLG>*LNf197nI;Vst0Nw?41ONa4 diff --git a/Sources/Adwaita/Model/User Interface/GTUIWindow.swift b/Sources/Adwaita/Model/User Interface/GTUIWindow.swift new file mode 100644 index 0000000..eaf638d --- /dev/null +++ b/Sources/Adwaita/Model/User Interface/GTUIWindow.swift @@ -0,0 +1,11 @@ +// +// GTUIWindow.swift +// Adwaita +// +// Created by david-swift on 12.10.23. +// + +import GTUI + +/// A GTUI window. +public typealias GTUIWindow = GTUI.Window diff --git a/Sources/Adwaita/Model/User Interface/ViewBuilder.swift b/Sources/Adwaita/Model/User Interface/ViewBuilder.swift index 8709f6c..b3853c9 100644 --- a/Sources/Adwaita/Model/User Interface/ViewBuilder.swift +++ b/Sources/Adwaita/Model/User Interface/ViewBuilder.swift @@ -57,7 +57,11 @@ public enum ViewBuilder { /// - Parameter component: An optional component. /// - Returns: A nonoptional component. public static func buildOptional(_ component: Component?) -> Component { - component ?? .components([]) + if let component { + return .element(EitherView(true, { buildFinalResult(component) }, else: nil)) + } else { + return .element(EitherView(false, nil) { [] }) + } } /// Enables support for `if`-`else` and `switch` statements. diff --git a/Sources/Adwaita/Model/User Interface/WindowScene.swift b/Sources/Adwaita/Model/User Interface/WindowScene.swift index d9f9f00..0c30236 100644 --- a/Sources/Adwaita/Model/User Interface/WindowScene.swift +++ b/Sources/Adwaita/Model/User Interface/WindowScene.swift @@ -27,6 +27,6 @@ public protocol WindowScene: WindowSceneGroup { extension WindowScene { /// The window scene's body is itself. - @SceneBuilder public var body: Scene { self } + @SceneBuilder public var scene: Scene { self } } diff --git a/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift b/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift index 41ae5a2..4695c61 100644 --- a/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift +++ b/Sources/Adwaita/Model/User Interface/WindowSceneGroup.swift @@ -9,7 +9,7 @@ public protocol WindowSceneGroup { /// The group's content. - @SceneBuilder var body: Scene { get } + @SceneBuilder var scene: Scene { get } } @@ -19,7 +19,7 @@ extension WindowSceneGroup { /// - Returns: The windows. func windows() -> [WindowScene] { var content: [WindowScene] = [] - for element in body { + for element in scene { if let window = element as? WindowScene { content.append(window) } else { diff --git a/Sources/Adwaita/Model/User Interface/WindowStorage.swift b/Sources/Adwaita/Model/User Interface/WindowStorage.swift index dc1e04e..b94c89d 100644 --- a/Sources/Adwaita/Model/User Interface/WindowStorage.swift +++ b/Sources/Adwaita/Model/User Interface/WindowStorage.swift @@ -15,7 +15,7 @@ public class WindowStorage { /// Whether the reference to the window should disappear in the next update. public var destroy = false /// The GTUI window. - public var window: GTUI.Window + public var window: GTUIWindow /// The content's storage. public var view: ViewStorage @@ -24,7 +24,7 @@ public class WindowStorage { /// - id: The window's identifier. /// - window: The GTUI window. /// - view: The content's storage. - public init(id: String, window: GTUI.Window, view: ViewStorage) { + public init(id: String, window: GTUIWindow, view: ViewStorage) { self.id = id self.window = window self.view = view diff --git a/Sources/Adwaita/View/HeaderBar.swift b/Sources/Adwaita/View/HeaderBar.swift index 71f26b0..681e56a 100644 --- a/Sources/Adwaita/View/HeaderBar.swift +++ b/Sources/Adwaita/View/HeaderBar.swift @@ -14,17 +14,25 @@ public struct HeaderBar: Widget { var start: Body /// The end content of the header bar. var end: Body + /// Whether the title buttons are visible. + var titleButtons: Bool + /// The view acting as the title of the header bar. + var headerBarTitle: Body? /// The start content's id. let startID = "start" /// The end content's id. let endID = "end" + /// The title's id. + let titleID = "title" /// Initialize a header bar. /// - Parameters: + /// - titleButtons: Whether the title buttons (e.g. close button) are visible. /// - start: The start content. /// - end: The end content. - public init(@ViewBuilder start: () -> Body, @ViewBuilder end: () -> Body) { + public init(titleButtons: Bool = true, @ViewBuilder start: () -> Body, @ViewBuilder end: () -> Body) { + self.titleButtons = titleButtons self.start = start() self.end = end() } @@ -52,8 +60,14 @@ public struct HeaderBar: Widget { /// Update a header bar's view storage. /// - Parameter storage: The view storage. public func update(_ storage: ViewStorage) { + if let bar = storage.view as? GTUI.HeaderBar { + _ = bar.showTitleButtons(titleButtons) + } start.update(storage.content[startID] ?? []) end.update(storage.content[endID] ?? []) + if let first = storage.content[titleID]?.first { + headerBarTitle?.widget().update(first) + } } /// Get the container for a header bar. @@ -72,7 +86,25 @@ public struct HeaderBar: Widget { _ = bar.packEnd(element.view) endContent.append(element) } - return .init(bar, content: [startID: startContent, endID: endContent]) + let title = headerBarTitle?.widget().container() + let titleStorage: [ViewStorage] + if let title { + _ = bar.titleWidget(title.view) + titleStorage = [title] + } else { + titleStorage = [] + } + _ = bar.showTitleButtons(titleButtons) + return .init(bar, content: [startID: startContent, endID: endContent, titleID: titleStorage]) + } + + /// Set the title widget for the header bar. + /// - Parameter view: The widget in the header bar. + /// - Returns: The header bar. + public func headerBarTitle(@ViewBuilder view: () -> Body) -> Self { + var newSelf = self + newSelf.headerBarTitle = view() + return newSelf } } diff --git a/Sources/Adwaita/View/List.swift b/Sources/Adwaita/View/List.swift index 1f4917d..d395720 100644 --- a/Sources/Adwaita/View/List.swift +++ b/Sources/Adwaita/View/List.swift @@ -17,7 +17,7 @@ public struct List: Widget where Element: Identifiable { /// The identifier of the selected element. @Binding var selection: Element.ID - /// Initialize `ForEach`. + /// Initialize `List`. /// - Parameters: /// - elements: The elements. /// - selection: The identifier of the selected element. diff --git a/Sources/Adwaita/View/Modifiers/Clamp.swift b/Sources/Adwaita/View/Modifiers/Clamp.swift new file mode 100644 index 0000000..f25c633 --- /dev/null +++ b/Sources/Adwaita/View/Modifiers/Clamp.swift @@ -0,0 +1,49 @@ +// +// Clamp.swift +// Adwaita +// +// Created by david-swift on 12.10.23. +// + +import GTUI + +/// A horizontal AdwClamp equivalent. +struct Clamp: Widget { + + /// The content. + var content: View + /// The maximum size. + var maxSize: Int + + /// Update a view storage. + /// - Parameter storage: The view storage. + func update(_ storage: ViewStorage) { + if let clamp = storage.view as? GTUI.Clamp { + _ = clamp.maximumSize(maxSize) + } + if let storage = storage.content[.mainContent]?[safe: 0] { + content.widget().update(storage) + } + } + + /// Get a view storage. + /// - Returns: The view storage. + func container() -> ViewStorage { + let container = content.storage() + let clamp: GTUI.Clamp = .init(container.view) + _ = clamp.maximumSize(maxSize) + return .init(clamp, content: [.mainContent: [container]]) + } + +} + +extension View { + + /// Set the view's maximal size. + /// - Parameter maxSize: The maximal size. + /// - Returns: A view. + public func frame(maxSize: Int? = nil) -> View { + Clamp(content: self, maxSize: maxSize ?? -1) + } + +} diff --git a/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift b/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift index 0e65c87..1e9df46 100644 --- a/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift +++ b/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift @@ -7,7 +7,7 @@ import GTUI -/// A widget which executes a custom code on the GTUI widget when being created. +/// A widget which executes a custom code on the GTUI widget when being created and updated. struct InspectorWrapper: Widget { /// The custom code to edit the widget. @@ -27,13 +27,14 @@ struct InspectorWrapper: Widget { /// - Parameter storage: The content's storage. func update(_ storage: ViewStorage) { content.updateStorage(storage) + modify(storage.view) } } extension View { - /// Modify a GTUI widget before being displayed. + /// Modify a GTUI widget before being displayed and when being updated. /// - Parameter modify: Modify the widget. /// - Returns: A view. public func inspect(_ modify: @escaping (NativeWidgetPeer?) -> Void) -> View { @@ -86,13 +87,6 @@ extension View { inspect { _ = $0?.frame(minWidth: minWidth, minHeight: minHeight) } } - /// Set the view's maximal size. - /// - Parameter maxSize: The maximal size. - /// - Returns: A view. - public func frame(maxSize: Int? = nil) -> View { - inspect { _ = $0?.frame(maxSize: maxSize) } - } - /// Set the view's transition. /// - Parameter transition: The transition. /// - Returns: A view. diff --git a/Sources/Adwaita/View/StatusPage.swift b/Sources/Adwaita/View/StatusPage.swift index ffd0734..4eda7a9 100644 --- a/Sources/Adwaita/View/StatusPage.swift +++ b/Sources/Adwaita/View/StatusPage.swift @@ -25,10 +25,15 @@ public struct StatusPage: Widget { /// - icon: The icon. /// - description: Additional details. /// - content: Additional content. - public init(_ title: String, icon: Icon, description: String = "", @ViewBuilder content: () -> Body = { [] }) { + public init( + _ title: String, + icon: Icon? = nil, + description: String = "", + @ViewBuilder content: () -> Body = { [] } + ) { self.title = title self.description = description - self.icon = icon + self.icon = icon ?? .custom(name: "") self.content = content() } diff --git a/Sources/Adwaita/Window/Window.swift b/Sources/Adwaita/Window/Window.swift index ff7e632..8918623 100644 --- a/Sources/Adwaita/Window/Window.swift +++ b/Sources/Adwaita/Window/Window.swift @@ -15,7 +15,7 @@ public struct Window: WindowScene { /// The window's identifier. public var id: String /// The window's content. - var content: (GTUI.Window) -> Body + var content: (GTUIWindow) -> Body /// Whether an instance of the window type should be opened when the app is starting up. public var `open`: Int @@ -24,7 +24,7 @@ public struct Window: WindowScene { /// - id: The identifier. /// - open: The number of instances of the window type when the app is starting. /// - content: The window's content. - public init(id: String, `open`: Int = 1, @ViewBuilder content: @escaping (GTUI.Window) -> Body) { + public init(id: String, `open`: Int = 1, @ViewBuilder content: @escaping (GTUIWindow) -> Body) { self.content = content self.id = id self.open = open @@ -47,8 +47,8 @@ public struct Window: WindowScene { /// Get the window. /// - Parameter app: The application. /// - Returns: The window. - func createGTUIWindow(app: GTUIApp) -> GTUI.Window { - let window = GTUI.Window(app: app) + func createGTUIWindow(app: GTUIApp) -> GTUIWindow { + let window = GTUIWindow(app: app) window.show() return window } @@ -56,7 +56,7 @@ public struct Window: WindowScene { /// Get the storage of the content view. /// - Parameter window: The window. /// - Returns: The storage of the content of the window. - func getViewStorage(window: GTUI.Window) -> ViewStorage { + func getViewStorage(window: GTUIWindow) -> ViewStorage { let storage = content(window).widget().container() window.setChild(storage.view) return storage diff --git a/Tests/CounterDemo.swift b/Tests/CounterDemo.swift index 7416496..2ed9d72 100644 --- a/Tests/CounterDemo.swift +++ b/Tests/CounterDemo.swift @@ -8,28 +8,39 @@ // swiftlint:disable missing_docs import Adwaita +import GTUI struct CounterDemo: View { @State private var count = 0 var view: Body { - description - .topToolbar { - HeaderBar.start { - Button(icon: .default(icon: .goPrevious)) { - count -= 1 - } - Button(icon: .default(icon: .goNext)) { - count += 1 - } - } + VStack { + HStack { + CountButton(count: $count, icon: .goPrevious) { $0 -= 1 } + Text("\(count)") + .style("title-1") + .frame(minWidth: 100) + CountButton(count: $count, icon: .goNext) { $0 += 1 } } + .halign(.center) + } + .valign(.center) + .padding() } - @ViewBuilder private var description: Body { - Text("\(count)") - .style("title-1") + private struct CountButton: View { + + @Binding var count: Int + var icon: Icon.DefaultIcon + var action: (inout Int) -> Void + + var view: Body { + Button(icon: .default(icon: icon)) { + action(&count) + } + .style("circular") + } } } diff --git a/Tests/Demo.swift b/Tests/Demo.swift index 51bf81e..3cf2fd8 100644 --- a/Tests/Demo.swift +++ b/Tests/Demo.swift @@ -8,7 +8,6 @@ // swiftlint:disable missing_docs implicitly_unwrapped_optional no_magic_numbers import Adwaita -import GTUI @main struct Demo: App { @@ -21,22 +20,26 @@ struct Demo: App { Window(id: "main") { window in DemoContent(window: window, app: app) } - Window(id: "content", open: 0) { window in - Text("This window exists at most once.") - .padding() - .topToolbar { - HeaderBar.empty() - } - .onAppear { - window.setDefaultSize(width: 400, height: 250) - } + HelperWindows() + } + + struct HelperWindows: WindowSceneGroup { + + var scene: Scene { + Window(id: "content", open: 0) { window in + WindowsDemo.WindowContent(window: window) + } + Window(id: "toolbar-demo", open: 0) { window in + ToolbarDemo.WindowContent(window: window) + } } + } struct DemoContent: View { @State private var selection: Page = .welcome - var window: GTUI.Window + var window: GTUIWindow var app: GTUIApp! var view: Body { @@ -54,7 +57,16 @@ struct Demo: App { } .navigationTitle("Demo") } content: { - selection.view(app: app) + StatusPage( + selection.label, + icon: selection.icon, + description: selection.description + ) { + selection.view(app: app) + } + .topToolbar { + HeaderBar.empty() + } } .onAppear { window.setDefaultSize(width: 650, height: 450) diff --git a/Tests/DiceDemo.swift b/Tests/DiceDemo.swift new file mode 100644 index 0000000..aa5076d --- /dev/null +++ b/Tests/DiceDemo.swift @@ -0,0 +1,39 @@ +// +// DiceDemo.swift +// Adwaita +// +// Created by david-swift on 12.10.23. +// + +// swiftlint:disable missing_docs no_magic_numbers + +import Adwaita + +struct DiceDemo: View { + + @State private var number: Int? + + private var label: String { + if let number { + return "\(number)" + } else { + return "Roll the Dice!" + } + } + + var view: Body { + VStack { + Button(label) { + number = .random(in: 1...6) + } + .style("pill") + .style("suggested-action") + .frame(maxSize: 100) + } + .valign(.center) + .padding() + } + +} + +// swiftlint:enable missing_docs no_magic_numbers diff --git a/Tests/Page.swift b/Tests/Page.swift index 3018381..17b256e 100644 --- a/Tests/Page.swift +++ b/Tests/Page.swift @@ -8,12 +8,16 @@ // swiftlint:disable missing_docs implicitly_unwrapped_optional import Adwaita +import GTUI enum Page: String, Identifiable, CaseIterable { case welcome case counter case windows + case toolbar + case transition + case dice var id: Self { self @@ -23,15 +27,47 @@ enum Page: String, Identifiable, CaseIterable { rawValue.capitalized } + var icon: GTUI.Icon? { + switch self { + case .welcome: + return .default(icon: .gnomeAdwaita1Demo) + default: + return nil + } + } + + var description: String { + switch self { + case .welcome: + return "This is a collection of examples for the Swift Adwaita package." + case .counter: + return "A simple sample view." + case .windows: + return "Showcase window management." + case .toolbar: + return "Toggle the bottom toolbar." + case .transition: + return "A slide transition between two views." + case .dice: + return "Roll the dice." + } + } + @ViewBuilder func view(app: GTUIApp!) -> Body { switch self { case .welcome: - WelcomeDemo() + [] case .counter: CounterDemo() case .windows: WindowsDemo(app: app) + case .toolbar: + ToolbarDemo(app: app) + case .transition: + TransitionDemo() + case .dice: + DiceDemo() } } diff --git a/Tests/ToolbarDemo.swift b/Tests/ToolbarDemo.swift new file mode 100644 index 0000000..712ab0f --- /dev/null +++ b/Tests/ToolbarDemo.swift @@ -0,0 +1,68 @@ +// +// ToolbarDemo.swift +// Adwaita +// +// Created by david-swift on 12.10.23. +// + +// swiftlint:disable missing_docs no_magic_numbers + +import Adwaita + +struct ToolbarDemo: View { + + var app: GTUIApp + + var view: Body { + VStack { + Button("View Demo") { + app.showWindow("toolbar-demo") + } + .style("suggested-action") + .frame(maxSize: 100) + } + } + + struct WindowContent: View { + + @State private var visible = false + @State private var moreContent = false + var window: GTUIWindow + + var view: Body { + VStack { + Button("Toggle Toolbar") { + visible.toggle() + } + .style("suggested-action") + .frame(maxSize: 100) + .padding(15) + } + .valign(.center) + .bottomToolbar(visible: visible) { + HeaderBar(titleButtons: false) { + Button(icon: .default(icon: .audioInputMicrophone)) { } + } end: { + Button(icon: .default(icon: .userTrash)) { } + } + .headerBarTitle { } + } + .topToolbar { + HeaderBar.empty() + } + .onAppear { + window.setDefaultSize(width: 400, height: 250) + } + } + + } + +} + +extension Int: Identifiable { + + public var id: Self { self } + +} + +// swiftlint:enable missing_docs no_magic_numbers diff --git a/Tests/TransitionDemo.swift b/Tests/TransitionDemo.swift new file mode 100644 index 0000000..2135529 --- /dev/null +++ b/Tests/TransitionDemo.swift @@ -0,0 +1,47 @@ +// +// TransitionDemo.swift +// Adwaita +// +// Created by david-swift on 12.10.23. +// + +// swiftlint:disable missing_docs no_magic_numbers + +import Adwaita + +struct TransitionDemo: View { + + @State private var firstView = true + + var view: Body { + VStack { + if firstView { + content("First View") + .transition(.slideDown) + .style("accent") + } else { + content("Second View") + .transition(.slideUp) + .style("success") + } + } + .style("card") + .frame(maxSize: 200) + .padding() + Button("Toggle View") { + firstView.toggle() + } + .style("pill") + .padding() + .frame(maxSize: 100) + } + + private func content(_ text: String) -> View { + Text(text) + .style("title-2") + .padding() + } + +} + +// swiftlint:enable missing_docs no_magic_numbers diff --git a/Tests/WelcomeDemo.swift b/Tests/WelcomeDemo.swift deleted file mode 100644 index 420f294..0000000 --- a/Tests/WelcomeDemo.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// WelcomeDemo.swift -// Adwaita -// -// Created by david-swift on 25.09.23. -// - -// swiftlint:disable missing_docs - -import Adwaita - -struct WelcomeDemo: View { - - @State private var test = false - - var view: Body { - StatusPage( - "Swift Adwaita Demo", - icon: .default(icon: .gnomeAdwaita1Demo), - description: "This is a collection of examples for the Swift Adwaita package." - ) - .topToolbar { - HeaderBar.empty() - } - } - -} - -// swiftlint:enable missing_docs diff --git a/Tests/WindowsDemo.swift b/Tests/WindowsDemo.swift index bb25f41..262eb3f 100644 --- a/Tests/WindowsDemo.swift +++ b/Tests/WindowsDemo.swift @@ -14,19 +14,39 @@ struct WindowsDemo: View { var app: GTUIApp! var view: Body { - VStack { - Button("Show Window") { - app.showWindow("content") + HStack { + VStack { + Button("Show Window") { + app.showWindow("content") + } + .hexpand() + Button("Add Window") { + app.addWindow("main") + } + .hexpand() } + .valign(.center) + .style("linked") .padding() - Button("Add Window") { - app.addWindow("main") - } - .padding(10, .horizontal.add(.bottom)) } - .topToolbar { - HeaderBar.empty() + .frame(maxSize: 100) + } + + struct WindowContent: View { + + var window: GTUIWindow + + var view: Body { + Text("This window exists at most once.") + .padding() + .topToolbar { + HeaderBar.empty() + } + .onAppear { + window.setDefaultSize(width: 400, height: 250) + } } + } }