From 37252f7b8f0288a0434b22f5423e30c5e23febf8 Mon Sep 17 00:00:00 2001 From: david-swift Date: Sat, 30 Dec 2023 21:22:57 +0100 Subject: [PATCH] Replace EitherView by the more powerful ViewStack --- Documentation/Reference/README.md | 2 +- Documentation/Reference/structs/ViewStack.md | 42 +++++++ .../User Interface/View/ViewBuilder.swift | 8 +- Sources/Adwaita/View/EitherView.swift | 107 ------------------ Sources/Adwaita/View/ViewStack.swift | 72 ++++++++++++ user-manual/Information/Widgets.md | 2 +- 6 files changed, 120 insertions(+), 113 deletions(-) create mode 100644 Documentation/Reference/structs/ViewStack.md delete mode 100644 Sources/Adwaita/View/EitherView.swift create mode 100644 Sources/Adwaita/View/ViewStack.swift diff --git a/Documentation/Reference/README.md b/Documentation/Reference/README.md index bbaec07..6b75d5d 100644 --- a/Documentation/Reference/README.md +++ b/Documentation/Reference/README.md @@ -20,7 +20,6 @@ - [Button](structs/Button.md) - [Clamp](structs/Clamp.md) - [ContentModifier](structs/ContentModifier.md) -- [EitherView](structs/EitherView.md) - [FileDialog](structs/FileDialog.md) - [HStack](structs/HStack.md) - [HeaderBar](structs/HeaderBar.md) @@ -43,6 +42,7 @@ - [Toggle](structs/Toggle.md) - [ToolbarView](structs/ToolbarView.md) - [VStack](structs/VStack.md) +- [ViewStack](structs/ViewStack.md) - [Window](structs/Window.md) ## Classes diff --git a/Documentation/Reference/structs/ViewStack.md b/Documentation/Reference/structs/ViewStack.md new file mode 100644 index 0000000..788a197 --- /dev/null +++ b/Documentation/Reference/structs/ViewStack.md @@ -0,0 +1,42 @@ +**STRUCT** + +# `ViewStack` + +A widget holding multiple children but only displaying one. + +## Properties +### `content` + +The stack's active content. + +### `id` + +The stack's active identifier. + +## Methods +### `init(id:view:)` + +Initialize the stack. +- Parameters: + - id: The identifier of the current view. + - view: The current view. + +### `init(element:view:)` + +Initialize the stack. +- Parameters: + - element: The current identifiable element. + - view: The current view. + +### `container(modifiers:)` + +Get a stack's view storage. +- Parameter modifiers: Modify views before being updated. +- Returns: The stack's view storage. + +### `update(_:modifiers:)` + +Update a stack's view storage. +- Parameters: + - storage: The view storage. + - modifiers: Modify views before being updated. diff --git a/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift b/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift index b3853c9..1378719 100644 --- a/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift +++ b/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift @@ -58,9 +58,9 @@ public enum ViewBuilder { /// - Returns: A nonoptional component. public static func buildOptional(_ component: Component?) -> Component { if let component { - return .element(EitherView(true, { buildFinalResult(component) }, else: nil)) + return .element(ViewStack(id: true) { _ in buildFinalResult(component) }) } else { - return .element(EitherView(false, nil) { [] }) + return .element(ViewStack(id: false) { _ in [] }) } } @@ -68,14 +68,14 @@ public enum ViewBuilder { /// - Parameter component: A component. /// - Returns: The component. public static func buildEither(first component: Component) -> Component { - .element(EitherView(true, { buildFinalResult(component) }, else: nil)) + .element(ViewStack(id: true) { _ in buildFinalResult(component) }) } /// Enables support for `if`-`else` and `switch` statements. /// - Parameter component: A component. /// - Returns: The component. public static func buildEither(second component: Component) -> Component { - .element(EitherView(false, nil) { buildFinalResult(component) }) + .element(ViewStack(id: false) { _ in buildFinalResult(component) }) } /// Convert a component to an array of elements. diff --git a/Sources/Adwaita/View/EitherView.swift b/Sources/Adwaita/View/EitherView.swift deleted file mode 100644 index 0e9ecc2..0000000 --- a/Sources/Adwaita/View/EitherView.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// EitherView.swift -// Adwaita -// -// Created by david-swift on 22.08.23. -// - -import Libadwaita - -/// An equivalent to GtkStack for two views. -public struct EitherView: Widget { - - /// The view that is displayed when `isTrue` is true. - var trueView: Body? - /// The view that is displayed when `isTrue` is false. - var falseView: Body? - /// The state. - var isTrue: Bool - - /// Initialize an `EitherView`. - /// - Parameters: - /// - isTrue: The state. - /// - _: The view that is presented if `isTrue` is true. - /// - else: The view that is presented if `isTrue` is false. - public init( - _ isTrue: Bool, - @ViewBuilder _ trueView: @escaping () -> Body, - @ViewBuilder else falseView: @escaping () -> Body - ) { - self.init(isTrue, .some(trueView), else: .some(falseView)) - } - - /// Initialize an `EitherView`. - /// - Parameters: - /// - isTrue: The state. - /// - trueView: The view that is presented if `isTrue` is true. - /// - falseView: The view that is presented if `isTrue` is false. - init(_ isTrue: Bool, _ trueView: (() -> Body)? = nil, else falseView: (() -> Body)? = nil) { - self.trueView = trueView?() - self.falseView = falseView?() - self.isTrue = isTrue - } - - /// Update an `EitherView`'s storage. - /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) { - let stack = storage.view as? Stack - updateContent(storage, state: true, stack: stack, modifiers: modifiers) - updateContent(storage, state: false, stack: stack, modifiers: modifiers) - if isTrue, let trueView = storage.content["\(true)"]?.last?.view { - setVisible(stack, view: trueView) - } else if !isTrue, let falseView = storage.content["\(false)"]?.last?.view { - setVisible(stack, view: falseView) - } - } - - /// Update the content of a view in the view storage. - /// - Parameters: - /// - storage: The view storage. - /// - state: Whether it is the true or false view. - /// - stack: The stack. - /// - modifiers: Modify views before being updated. - private func updateContent(_ storage: ViewStorage, state: Bool, stack: Stack?, modifiers: [(View) -> View]) { - let activeView = (state ? trueView : falseView)?.widget(modifiers: modifiers) - if let view = storage.content["\(state)"]?[safe: 0] { - activeView?.updateStorage(view, modifiers: modifiers) - } else if let view = activeView?.storage(modifiers: modifiers) { - _ = stack?.append(view.view) - storage.content["\(state)"] = [view] - } - } - - /// Set the visible content page. - /// - Parameters: - /// - stack: The stack. - /// - view: The visible view. - private func setVisible(_ stack: Stack?, view: NativeWidgetPeer) { - stack?.setVisible(view, transition: view.fields[.transition] as? Transition) - } - - /// Get a GtkStack view storage. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { - let stack = Stack() - var content: [String: [ViewStorage]] = [:] - if let trueView { - let view = trueView.widget(modifiers: modifiers).storage(modifiers: modifiers) - _ = stack.append(view.view) - content["\(true)"] = [view] - } - if let falseView { - let view = falseView.widget(modifiers: modifiers).storage(modifiers: modifiers) - _ = stack.append(view.view) - content["\(false)"] = [view] - } - if isTrue, let trueView = content["\(true)"]?.last?.view { - stack.setVisible(trueView) - } else if let falseView = content["\(false)"]?.last?.view { - stack.setVisible(falseView) - } - return .init(stack, content: content) - } - -} diff --git a/Sources/Adwaita/View/ViewStack.swift b/Sources/Adwaita/View/ViewStack.swift new file mode 100644 index 0000000..745d45a --- /dev/null +++ b/Sources/Adwaita/View/ViewStack.swift @@ -0,0 +1,72 @@ +// +// ViewStack.swift +// Adwaita +// +// Created by david-swift on 30.12.23. +// + +import Libadwaita + +/// A widget holding multiple children but only displaying one. +public struct ViewStack: Widget { + + /// The stack's active content. + var content: Body + /// The stack's active identifier. + var id: CustomStringConvertible + + /// Initialize the stack. + /// - Parameters: + /// - id: The identifier of the current view. + /// - view: The current view. + public init( + id: Identifier, + @ViewBuilder view: @escaping (Identifier) -> Body + ) where Identifier: CustomStringConvertible { + content = view(id) + self.id = id + } + + /// Initialize the stack. + /// - Parameters: + /// - element: The current identifiable element. + /// - view: The current view. + public init( + element: Element, + @ViewBuilder view: @escaping (Element) -> Body + ) where Element: Identifiable, Element.ID: CustomStringConvertible { + content = view(element) + self.id = element.id + } + + /// Get a stack's view storage. + /// - Parameter modifiers: Modify views before being updated. + /// - Returns: The stack's view storage. + public func container(modifiers: [(View) -> View]) -> ViewStorage { + let stack = Stack() + let storage = ViewStorage(stack) + update(storage, modifiers: modifiers) + return storage + } + + /// Update a stack's view storage. + /// - Parameters: + /// - storage: The view storage. + /// - modifiers: Modify views before being updated. + public func update(_ storage: ViewStorage, modifiers: [(View) -> View]) { + if let stack = storage.view as? Stack { + let widget = content.widget(modifiers: modifiers) + if let view = storage.content[id.description]?.first { + widget.updateStorage(view, modifiers: modifiers) + } else { + let view = widget.storage(modifiers: modifiers) + _ = stack.append(view.view) + storage.content[id.description] = [view] + } + if let visibleView = storage.content[id.description]?.first?.view { + _ = stack.setVisible(visibleView, transition: visibleView.fields[.transition] as? Transition) + } + } + } + +} diff --git a/user-manual/Information/Widgets.md b/user-manual/Information/Widgets.md index e944418..a850108 100644 --- a/user-manual/Information/Widgets.md +++ b/user-manual/Information/Widgets.md @@ -5,7 +5,7 @@ This is an overview of the available widgets and other components in _Adwaita_. | Name | Description | Widget | | -------------------- | ----------------------------------------------------------------- | ---------------------- | | Button | A widget that triggers a function when being clicked. | GtkButton | -| EitherView | A widget that displays one of its child views based on a boolean. | GtkStack | +| ViewStack | A widget that displays one of its child views based on an id. | GtkStack | | HeaderBar | A widget for creating custom title bars for windows. | GtkHeaderBar | | Text | A widget for displaying a small amount of text. | GtkLabel | | VStack | A widget which arranges child widgets into a single column. | GtkBox |