diff --git a/Sources/Core/View/Label.swift b/Sources/Core/View/Label.swift index 47331e6..5ae4084 100644 --- a/Sources/Core/View/Label.swift +++ b/Sources/Core/View/Label.swift @@ -30,10 +30,10 @@ public struct Label: SwiftUIWidget { public static func view(properties: Self) -> some SwiftUI.View { SwiftUI.Label { SwiftUI.Text(properties.label) + SwiftUI.Spacer() } icon: { properties.icon.image } - } } diff --git a/Sources/Core/View/NavigationStack.swift b/Sources/Core/View/NavigationStack.swift new file mode 100644 index 0000000..bf569db --- /dev/null +++ b/Sources/Core/View/NavigationStack.swift @@ -0,0 +1,120 @@ +// +// NavigationStack.swift +// MacBackend +// +// Created by david-swift on 14.12.2024. +// + +import SwiftUI + +/// The navigation stack view. +public struct NavigationStack: SwiftUIWidget where Component: CustomStringConvertible { + + /// The current navigation path. + @Meta.Binding var path: NavigationPath + /// The content. + var content: (Component) -> Body + /// The initial view. + var initialView: Body + + /// The wrapped views. + public var wrappedViews: [String: Meta.AnyView] { + if let current = path.current { + _ = current + return [.mainContent: initialView, current.description: content(current)] + } else { + return [.mainContent: initialView] + } + } + + /// Initialize the navigation stack. + /// - Parameters: + /// - path: The path. + /// - content: The content. + /// - initialView: The initial view. + public init( + path: Meta.Binding, + @Meta.ViewBuilder content: @escaping (Component) -> Body, + @Meta.ViewBuilder initialView: () -> Body + ) { + self._path = path + self.content = content + self.initialView = initialView() + } + + /// The navigation path. + public struct NavigationPath { + + /// The path. + var path: [Component] = [] + /// The current component. + var current: Component? { path.last } + + /// The path as an array of strings. + var hashable: [String] { + get { + path.map { $0.description } + } + set { + if newValue.count < hashable.count { + _ = path.popLast() + } + } + } + + /// Initialize a navigation stack. + public init() { } + + /// Pop the last element. + public mutating func pop() { + _ = path.popLast() + } + + /// Add a new element. + /// - Parameter component: The element. + public mutating func push(_ component: Component) { + path.append(component) + } + + } + + /// Receive the SwiftUI view. + /// - Parameter properties: The properties. + /// - Returns: The SwiftUI view. + public static func view(properties: Self) -> some SwiftUI.View { + SwiftUINavigationStack(path: properties.$path.swiftUI) + } + +} + +/// The SwiftUI navigation stack. +struct SwiftUINavigationStack: SwiftUI.View where Component: CustomStringConvertible { + + /// The path. + @SwiftUI.Binding var path: NavigationPath + /// The views. + @SwiftUI.Environment(\.views) + var views + + /// The view's body. + var body: some SwiftUI.View { + SwiftUI.NavigationStack(path: $path.hashable) { + let view = MacBackendView(.mainContent) + if path.current != nil { + view + .navigationDestination(for: String.self) { string in + SwiftUI.VStack { + MacBackendView(string) + .environment(\.views, views) + } + } + } else { + view + } + } + } + +} + +/// The navigation path. +public typealias NavigationPath = NavigationStack.NavigationPath diff --git a/Sources/Core/Window/MenuBar.swift b/Sources/Core/Window/MenuBar.swift index f91ed2f..f1f4613 100644 --- a/Sources/Core/Window/MenuBar.swift +++ b/Sources/Core/Window/MenuBar.swift @@ -59,7 +59,6 @@ public struct MenuBar: MacSceneElement { let scene = SceneStorage(id: id, pointer: app.mainMenu) { } let data = WidgetData(sceneStorage: scene, appStorage: app) let storage = MenuCollection { self.content }.getMenu(data: data, menu: app.mainMenu) - print(app.helpItem.submenu) let appStorage = MenuCollection { self.app }.getMenu(data: data, menu: app.appItem.submenu) let windowStorage = MenuCollection { self.window }.getMenu(data: data, menu: app.windowsItem.submenu) let helpStorage = MenuCollection { self.help }.getMenu(data: data, menu: app.helpItem.submenu) diff --git a/Sources/Demo/Demo.swift b/Sources/Demo/Demo.swift index 371a89c..b9ecbee 100644 --- a/Sources/Demo/Demo.swift +++ b/Sources/Demo/Demo.swift @@ -20,53 +20,38 @@ struct Demo: App { @State(blockUpdates: true) private var width = 500 @State(blockUpdates: true) - private var height = 400 + private var height = 450 @State private var elements: [Element] = [.init()] @State private var selectedElement: String? - @State private var alert = false - @State private var presentMenu: Signal = .init() @State private var selection = "Hello" + @State private var path: NavigationPath = .init() var scene: Scene { - Window(id: "secondary") { _ in - Text(selection) - .centeredToolbar { - Picker("Test", items: ["Hello", "World"], selection: $selection) - .segmented() - } - } - .frame(width: .constant(800), height: .constant(600)) Window("Main", id: "main") { _ in NavigationSplitView { List(elements, selection: $selectedElement) { element in Label(element.id, icon: .system(name: "play.fill")) } } detail: { - VStack { - Button(selectedElement ?? "World") { - let element = Element() - elements.append(element) - selectedElement = element.id + NavigationStack(path: $path) { navigation in + Button(navigation.description) { + selectedElement = nil } - Button(alert.description) { - presentMenu.signal() - } - .menu(present: presentMenu) { - MenuButton(alert.description) { - print("Hi") + } initialView: { + VStack { + Button(selectedElement ?? "World") { + let element = Element() + elements.append(element) + selectedElement = element.id + } + Button("Navigate") { + path.push(.test) } } - } - .alert("Hello", isPresented: $alert) - .cancelButton("Cancel") { - print("Cancel") - } - .destructiveButton("Destructive", default: true) { - print("Destructive") - } - .toolbar { - Button("World", icon: .system(name: "globe")) { - print("World") + .toolbar { + Button("World", icon: .system(name: "globe")) { + print("World") + } } } } @@ -138,4 +123,14 @@ struct Element: Identifiable { } +enum Navigation: CustomStringConvertible { + + case test + + var description: String { + "Test" + } + +} + // swiftlint:enable missing_docs no_magic_numbers closure_body_length