This commit is contained in:
parent
3cee2ee2b2
commit
e18797a8d9
@ -16,8 +16,6 @@ opt_in_rules:
|
||||
- convenience_type
|
||||
- discouraged_none_name
|
||||
- discouraged_object_literal
|
||||
- discouraged_optional_boolean
|
||||
- discouraged_optional_collection
|
||||
- empty_collection_literal
|
||||
- empty_count
|
||||
- empty_string
|
||||
|
||||
BIN
AppInformation.o
BIN
AppInformation.o
Binary file not shown.
Binary file not shown.
@ -5,6 +5,7 @@
|
||||
// Created by david-swift on 06.11.24.
|
||||
//
|
||||
|
||||
import Core
|
||||
import Foundation
|
||||
|
||||
/// Information about the app.
|
||||
@ -27,4 +28,26 @@ struct AppInformation {
|
||||
/// The settings label.
|
||||
var settings: String
|
||||
|
||||
#if ADWAITA
|
||||
/// The main menu for GNOME.
|
||||
/// - Parameter showAbout: Whether to show the about dialog.
|
||||
/// - Returns: The view.
|
||||
func menu(showAbout: Binding<Bool>) -> AnyView {
|
||||
AboutDialog(
|
||||
visible: showAbout,
|
||||
child: Menu(icon: .default(icon: .openMenu)) {
|
||||
MenuButton(about) {
|
||||
showAbout.wrappedValue.toggle()
|
||||
}
|
||||
},
|
||||
appName: name,
|
||||
developer: developer,
|
||||
version: version,
|
||||
icon: icon?.nativeIcon,
|
||||
website: website,
|
||||
issues: issues
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@ -24,6 +24,8 @@ public struct Window: AparokshaSceneElement {
|
||||
var width: Binding<Int>?
|
||||
/// The window's height.
|
||||
var height: Binding<Int>?
|
||||
/// The toolbar items.
|
||||
var toolbarItems: [ToolbarItem] = []
|
||||
|
||||
/// Initialize a window.
|
||||
/// - Parameters:
|
||||
@ -98,6 +100,44 @@ public struct Window: AparokshaSceneElement {
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Add a leading toolbar item.
|
||||
/// - Parameters:
|
||||
/// - title: The item's label.
|
||||
/// - icon: The item's icon.
|
||||
/// - disabled: Whether the item is disabled.
|
||||
/// - action: The item's action.
|
||||
public func leadingToolbarItem(
|
||||
_ title: String,
|
||||
icon: Icon,
|
||||
disabled: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.toolbarItems.append(
|
||||
.init(title: title, icon: icon, action: action, leftAlign: true, active: !disabled)
|
||||
)
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Add a trailing toolbar item.
|
||||
/// - Parameters:
|
||||
/// - title: The item's label.
|
||||
/// - icon: The item's icon.
|
||||
/// - disabled: Whether the item is disabled.
|
||||
/// - action: The item's action.
|
||||
public func trailingToolbarItem(
|
||||
_ title: String,
|
||||
icon: Icon,
|
||||
disabled: Bool = false,
|
||||
action: @escaping () -> Void
|
||||
) -> Self {
|
||||
var newSelf = self
|
||||
newSelf.toolbarItems.append(
|
||||
.init(title: title, icon: icon, action: action, leftAlign: false, active: !disabled)
|
||||
)
|
||||
return newSelf
|
||||
}
|
||||
|
||||
/// Set and observe the window's width and height.
|
||||
/// - Parameters:
|
||||
/// - width: The window's width.
|
||||
@ -123,7 +163,7 @@ public struct Window: AparokshaSceneElement {
|
||||
/// The native window.
|
||||
func nativeWindow(app: Aparoksha) -> Core.Window {
|
||||
.init(id: id, open: open) { _ in
|
||||
contentView(app: app)
|
||||
WindowMainView(content: content, defaultTitlebar: defaultTitlebar, app: app, toolbarItems: toolbarItems)
|
||||
}
|
||||
.title(title)
|
||||
.size(width: width, height: height)
|
||||
@ -132,16 +172,35 @@ public struct Window: AparokshaSceneElement {
|
||||
}
|
||||
#endif
|
||||
|
||||
/// The full content, including the correct environment data.
|
||||
/// - Parameter app: The app.
|
||||
/// - Returns: The content.
|
||||
func fullContent(app: Aparoksha) -> AnyView {
|
||||
content
|
||||
.environment("info", data: app.appInformation)
|
||||
}
|
||||
}
|
||||
|
||||
/// The content view.
|
||||
func contentView(app: Aparoksha) -> AnyView {
|
||||
/// The window's content.
|
||||
struct WindowMainView: View {
|
||||
|
||||
/// Whether to show the about dialog on GNOME.
|
||||
@State private var showAbout = false
|
||||
/// The window's content.
|
||||
var content: Body
|
||||
/// Whether to show the default title bar.
|
||||
var defaultTitlebar: Bool
|
||||
/// The app.
|
||||
var app: Aparoksha
|
||||
/// The toolbar items.
|
||||
var toolbarItems: [ToolbarItem]
|
||||
|
||||
#if ADWAITA
|
||||
/// The toolbar buttons.
|
||||
var buttons: [(AnyView, left: Bool)] {
|
||||
var body: [(AnyView, left: Bool)] = []
|
||||
for button in toolbarItems {
|
||||
body.append((button.button, left: button.leftAlign))
|
||||
}
|
||||
return body
|
||||
}
|
||||
#endif
|
||||
|
||||
/// The main view.
|
||||
var view: Body {
|
||||
#if WINUI
|
||||
fullContent(app: app)
|
||||
#else
|
||||
@ -153,10 +212,51 @@ public struct Window: AparokshaSceneElement {
|
||||
fullContent(app: app)
|
||||
}
|
||||
.top {
|
||||
HeaderBar.empty()
|
||||
HeaderBar {
|
||||
buttons.filter { $0.left }.map { $0.0 }
|
||||
} end: {
|
||||
app.appInformation.menu(showAbout: $showAbout)
|
||||
buttons.filter { !$0.left }.map { $0.0 }.reversed()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// The full content, including the correct environment data.
|
||||
/// - Parameter app: The app.
|
||||
/// - Returns: The content.
|
||||
func fullContent(app: Aparoksha) -> AnyView {
|
||||
content
|
||||
.environment("info", data: app.appInformation)
|
||||
.environment("toolbar", data: toolbarItems)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// The toolbar item.
|
||||
struct ToolbarItem {
|
||||
|
||||
/// The item's title.
|
||||
var title: String
|
||||
/// The item's icon.
|
||||
var icon: Icon
|
||||
/// The item's action.
|
||||
var action: () -> Void
|
||||
/// Whether the item is left aligned.
|
||||
var leftAlign: Bool
|
||||
/// Whether the item is active.
|
||||
var active: Bool
|
||||
|
||||
#if ADWAITA
|
||||
/// The Adwaita button.
|
||||
var button: AnyView {
|
||||
ModifierWrapper(
|
||||
content: Core.Button(icon: icon.nativeIcon, handler: action),
|
||||
insensitive: !active,
|
||||
tooltip: title
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@ -29,68 +29,34 @@ public struct FlatNavigation<Item>: View where Item: FlatNavigationItem {
|
||||
var content: Body
|
||||
/// Whether to force the sidebar layout.
|
||||
var forceSidebar = false
|
||||
// swiftlint:disable large_tuple
|
||||
/// The primary action.
|
||||
var primaryActionProperties: (label: String, icon: Icon, closure: () -> Void)?
|
||||
// swiftlint:enable large_tuple
|
||||
var primaryActionProperties: ToolbarItem?
|
||||
|
||||
/// Whether to use the view switcher layout.
|
||||
var useViewSwitcher: Bool {
|
||||
!forceSidebar && items.count <= 5
|
||||
}
|
||||
|
||||
#if ADWAITA
|
||||
/// The main menu for GNOME.
|
||||
var menu: AnyView {
|
||||
AboutDialog(
|
||||
visible: $showAbout,
|
||||
child: Menu(icon: .default(icon: .openMenu)) {
|
||||
MenuButton(info?.about ?? "About") {
|
||||
showAbout.toggle()
|
||||
}
|
||||
},
|
||||
appName: info?.name,
|
||||
developer: info?.developer,
|
||||
version: info?.version,
|
||||
icon: info?.icon?.nativeIcon,
|
||||
website: info?.website,
|
||||
issues: info?.issues
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// The view.
|
||||
public var view: Body {
|
||||
#if WINUI
|
||||
winNavigationView
|
||||
#else
|
||||
let primaryActionView: Body = if let primaryActionProperties {
|
||||
[
|
||||
ModifierWrapper(
|
||||
content: Core.Button(
|
||||
icon: primaryActionProperties.icon.nativeIcon
|
||||
) { primaryActionProperties.closure() },
|
||||
tooltip: primaryActionProperties.label
|
||||
)
|
||||
]
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
if useViewSwitcher {
|
||||
AdwViewSwitcher(
|
||||
selectedItem: $selectedItem,
|
||||
content: content,
|
||||
items: items,
|
||||
menu: menu,
|
||||
primaryAction: primaryActionView
|
||||
menu: info?.menu(showAbout: $showAbout) ?? [],
|
||||
primaryAction: primaryActionProperties?.button ?? []
|
||||
)
|
||||
} else {
|
||||
AdwNavigationSplitView(
|
||||
selectedItem: $selectedItem,
|
||||
content: content,
|
||||
items: items,
|
||||
menu: menu,
|
||||
primaryAction: primaryActionView
|
||||
menu: info?.menu(showAbout: $showAbout) ?? [],
|
||||
primaryAction: primaryActionProperties?.button ?? []
|
||||
)
|
||||
}
|
||||
#endif
|
||||
@ -158,14 +124,27 @@ public struct FlatNavigation<Item>: View where Item: FlatNavigationItem {
|
||||
}
|
||||
|
||||
/// The primary action, displayed in the top-left corner.
|
||||
/// - Parameter content: The view.
|
||||
/// - Parameters:
|
||||
/// - label: The button's label.
|
||||
/// - icon: The button's icon.
|
||||
/// - disabled: Whether the button is inactive.
|
||||
/// - closure: The closure.
|
||||
/// - Returns: The flat navigation view.
|
||||
public func primaryAction(
|
||||
_ label: String,
|
||||
icon: Icon,
|
||||
disabled: Bool = false,
|
||||
closure: @escaping () -> Void
|
||||
) -> Self {
|
||||
modify { $0.primaryActionProperties = (label: label, icon: icon, closure: closure) }
|
||||
modify { navigation in
|
||||
navigation.primaryActionProperties = .init(
|
||||
title: label,
|
||||
icon: icon,
|
||||
action: closure,
|
||||
leftAlign: true,
|
||||
active: !disabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -208,7 +187,7 @@ struct AdwViewSwitcher<Item>: View where Item: FlatNavigationItem {
|
||||
/// The main menu.
|
||||
var menu: AnyView
|
||||
/// The primary action view.
|
||||
var primaryAction: Body
|
||||
var primaryAction: AnyView
|
||||
|
||||
/// The view body.
|
||||
var view: Body {
|
||||
@ -273,6 +252,9 @@ struct AdwNavigationSplitView<Item>: View where Item: FlatNavigationItem {
|
||||
@State private var width = 0
|
||||
/// The currently selected item.
|
||||
@Binding var selectedItem: Item
|
||||
/// The toolbar items.
|
||||
@Environment("toolbar")
|
||||
var toolbarItems: [ToolbarItem]?
|
||||
/// The content view.
|
||||
var content: Body
|
||||
/// The navigation items.
|
||||
@ -280,21 +262,30 @@ struct AdwNavigationSplitView<Item>: View where Item: FlatNavigationItem {
|
||||
/// The menu.
|
||||
var menu: AnyView
|
||||
/// The primary action.
|
||||
var primaryAction: Body
|
||||
var primaryAction: AnyView
|
||||
|
||||
/// The minimum width before toggling to the mobile layout.
|
||||
var minWidth: Int {
|
||||
width < 200 ? 450 : width + 250
|
||||
}
|
||||
|
||||
#if ADWAITA
|
||||
/// The Adwaita toolbar buttons.
|
||||
var buttons: [(AnyView, left: Bool)] {
|
||||
var body: [(AnyView, left: Bool)] = []
|
||||
for button in toolbarItems ?? [] {
|
||||
body.append((button.button, left: button.leftAlign))
|
||||
}
|
||||
return body
|
||||
}
|
||||
#endif
|
||||
|
||||
/// The view body.
|
||||
var view: Body {
|
||||
BreakpointBin(condition: .minWidth(minWidth), matches: $wide) {
|
||||
NavigationSplitView {
|
||||
ToolbarView()
|
||||
.content {
|
||||
sidebar
|
||||
}
|
||||
.content { sidebar }
|
||||
.top {
|
||||
HeaderBar {
|
||||
primaryAction
|
||||
@ -309,8 +300,11 @@ struct AdwNavigationSplitView<Item>: View where Item: FlatNavigationItem {
|
||||
content
|
||||
}
|
||||
.top {
|
||||
HeaderBar
|
||||
.empty()
|
||||
HeaderBar {
|
||||
buttons.filter { $0.left }.map { $0.0 }
|
||||
} end: {
|
||||
buttons.filter { !$0.left }.map { $0.0 }.reversed()
|
||||
}
|
||||
}
|
||||
.inspectOnAppear { storage in
|
||||
inspectContent(storage: storage)
|
||||
|
||||
@ -31,6 +31,9 @@ struct Demo: App {
|
||||
ContentView()
|
||||
}
|
||||
.hideDefaultTitlebar()
|
||||
.leadingToolbarItem("Airplane", icon: .airplane) {
|
||||
print("Airplane")
|
||||
}
|
||||
.frame(width: $width, height: $height)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user