Add navigation stack
This commit is contained in:
parent
7f53c267d2
commit
8391997848
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
120
Sources/Core/View/NavigationStack.swift
Normal file
120
Sources/Core/View/NavigationStack.swift
Normal file
@ -0,0 +1,120 @@
|
||||
//
|
||||
// NavigationStack.swift
|
||||
// MacBackend
|
||||
//
|
||||
// Created by david-swift on 14.12.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/// The navigation stack view.
|
||||
public struct NavigationStack<Component>: 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<NavigationPath>,
|
||||
@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<Component>: SwiftUI.View where Component: CustomStringConvertible {
|
||||
|
||||
/// The path.
|
||||
@SwiftUI.Binding var path: NavigationPath<Component>
|
||||
/// 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<Component: CustomStringConvertible> = NavigationStack<Component>.NavigationPath
|
||||
@ -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)
|
||||
|
||||
@ -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<Navigation> = .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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user