diff --git a/Documentation/Reference/README.md b/Documentation/Reference/README.md index da594e4..9f9d57d 100644 --- a/Documentation/Reference/README.md +++ b/Documentation/Reference/README.md @@ -12,6 +12,7 @@ - [WindowScene](protocols/WindowScene.md) - [WindowSceneGroup](protocols/WindowSceneGroup.md) - [WindowType](protocols/WindowType.md) +- [WindowView](protocols/WindowView.md) ## Structs diff --git a/Documentation/Reference/extensions/View.md b/Documentation/Reference/extensions/View.md index 90ac9de..a0a9ab9 100644 --- a/Documentation/Reference/extensions/View.md +++ b/Documentation/Reference/extensions/View.md @@ -171,6 +171,12 @@ Bind a signal that focuses the view. - Parameter focus: Whether the view is focused. - Returns: A view. +### `tooltip(_:)` + +Add a tooltip to the widget. +- Parameter tooltip: The tooltip text. +- Returns: A view. + ### `stopModifiers()` Remove all of the content modifiers for the wrapped views. diff --git a/Documentation/Reference/protocols/WindowView.md b/Documentation/Reference/protocols/WindowView.md new file mode 100644 index 0000000..267719d --- /dev/null +++ b/Documentation/Reference/protocols/WindowView.md @@ -0,0 +1,13 @@ +**PROTOCOL** + +# `WindowView` + +A special view that can access the window of the current instance +if located as the first view directly inside a window. + +## Methods +### `window(_:)` + +Modify the window. +- Parameter window: The window. +- Returns: The window. diff --git a/Documentation/Reference/structs/Window.md b/Documentation/Reference/structs/Window.md index e54b48d..ef0fbce 100644 --- a/Documentation/Reference/structs/Window.md +++ b/Documentation/Reference/structs/Window.md @@ -31,10 +31,6 @@ The keyboard shortcuts. The keyboard shortcuts on the app level. -### `defaultSize` - -The default window size. - ### `title` The window's title. @@ -51,6 +47,18 @@ Whether the window is deletable. The signals for the importers and exporters. +### `width` + +The binding for the window's width. + +### `height` + +The binding for the window's height. + +### `setDefaultSize` + +Whether to update the default size. + ## Methods ### `init(id:open:content:)` @@ -86,10 +94,25 @@ Update a window storage's content. - app: The GTUI app. - force: Whether to force update all the views. -### `setProperties(window:)` +### `getTemplate(content:)` + +Get the actual window template. +- Parameter content: The content view.s +- Returns: The window. + +### `setProperties(window:template:)` Set some general propreties of the window. -- Parameter window: The window. +- Parameters: + - window: The window. + - template: The window template. + +### `updateSize(window:template:)` + +Update the window's size. +- Parameters: + - window: The window. + - template: The window template. ### `overlay(windows:)` @@ -126,10 +149,10 @@ Add a keyboard shortcut. - action: The closure to execute when the keyboard shortcut is pressed. - Returns: The window. -### `updateShortcuts(window:)` +### `updateShortcuts(window:template:)` Update the keyboard shortcuts. -- Parameter window: The application window. +- Parameters: window: The application window. ### `closeShortcut()` @@ -161,3 +184,11 @@ Set whether the window is resizable. Set whether the window is deletable. - Parameter resizable: The deletability. - Returns: The window. + +### `size(width:height:)` + +Add a tooltip to the widget. +- Parameters: + - width: The window's actual width. + - height: The window's actual height. +- Returns: The window. diff --git a/Sources/Adwaita/Model/User Interface/View/WindowView.swift b/Sources/Adwaita/Model/User Interface/View/WindowView.swift new file mode 100644 index 0000000..e2e5300 --- /dev/null +++ b/Sources/Adwaita/Model/User Interface/View/WindowView.swift @@ -0,0 +1,17 @@ +// +// WindowView.swift +// Adwaita +// +// Created by david-swift on 26.02.24. +// + +/// A special view that can access the window of the current instance +/// if located as the first view directly inside a window. +public protocol WindowView: View { + + /// Modify the window. + /// - Parameter window: The window. + /// - Returns: The window. + func window(_ window: Window) -> Window + +} diff --git a/Sources/Adwaita/Window/Window.swift b/Sources/Adwaita/Window/Window.swift index bce0bc4..7780dfb 100644 --- a/Sources/Adwaita/Window/Window.swift +++ b/Sources/Adwaita/Window/Window.swift @@ -7,6 +7,7 @@ // swiftlint:disable discouraged_optional_collection +import CAdw import Foundation /// A structure representing an application window type. @@ -26,8 +27,6 @@ public struct Window: WindowScene { var shortcuts: [String: (GTUIApplicationWindow) -> Void] = [:] /// The keyboard shortcuts on the app level. public var appShortcuts: [String: (GTUIApp) -> Void] = [:] - /// The default window size. - var defaultSize: (Int, Int)? /// The window's title. var title: String? /// Whether the window is resizable. @@ -36,6 +35,12 @@ public struct Window: WindowScene { var deletable = true /// The signals for the importers and exporters. var signals: [Signal] = [] + /// The binding for the window's width. + var width: Binding? + /// The binding for the window's height. + var height: Binding? + /// Whether to update the default size. + var setDefaultSize = false /// Create a window type with a certain identifier and user interface. /// - Parameters: @@ -77,11 +82,12 @@ public struct Window: WindowScene { /// - Returns: The storage of the content of the window. func getViewStorage(window: GTUIApplicationWindow) -> ViewStorage { let content = content(window) + let template = getTemplate(content: content) let storage = content.widget(modifiers: []).container(modifiers: []) window.setChild(storage.pointer) - window.setDefaultSize(width: defaultSize?.0, height: defaultSize?.1) - setProperties(window: window) - updateShortcuts(window: window) + setProperties(window: window, template: template) + updateShortcuts(window: window, template: template) + window.setDefaultSize(width: template.width?.wrappedValue, height: template.height?.wrappedValue) return storage } @@ -93,11 +99,12 @@ public struct Window: WindowScene { public func update(_ storage: WindowStorage, app: GTUIApp, force: Bool) { if let window = storage.window as? GTUIApplicationWindow { let content = content(window) + let template = getTemplate(content: content) if let view = storage.view { content.widget(modifiers: []).updateStorage(view, modifiers: [], updateProperties: force) } - setProperties(window: window) - updateShortcuts(window: window) + setProperties(window: window, template: template) + updateShortcuts(window: window, template: template) updateAppShortcuts(app: app) } for signal in signals where signal.update { @@ -107,14 +114,61 @@ public struct Window: WindowScene { } } + /// Get the actual window template. + /// - Parameter content: The content view.s + /// - Returns: The window. + func getTemplate(content: Body) -> Self { + var windowTemplate = self + if let view = content.first as? WindowView { + windowTemplate = view.window(windowTemplate) + } + return windowTemplate + } + /// Set some general propreties of the window. - /// - Parameter window: The window. - func setProperties(window: GTUIApplicationWindow) { - if let title { + /// - Parameters: + /// - window: The window. + /// - template: The window template. + func setProperties(window: GTUIApplicationWindow, template: Self) { + if let title = template.title { window.setTitle(title) } - window.setResizability(resizable) - window.setDeletability(deletable) + window.setResizability(template.resizable) + window.setDeletability(template.deletable) + var storage = window.fields["storage"] as? ViewStorage + if storage == nil { + storage = .init(window.pointer?.opaque()) + window.fields["storage"] = storage + } + if template.setDefaultSize { + window.setDefaultSize(width: template.width?.wrappedValue, height: template.height?.wrappedValue) + } + if template.width != nil { + storage?.notify(name: "default-width") { + updateSize(window: window, template: template) + } + } + if template.height != nil { + storage?.notify(name: "default-height") { + updateSize(window: window, template: template) + } + } + } + + /// Update the window's size. + /// - Parameters: + /// - window: The window. + /// - template: The window template. + func updateSize(window: GTUIApplicationWindow, template: Self) { + var width: Int32 = 0 + var height: Int32 = 0 + gtk_window_get_default_size(window.pointer, &width, &height) + if width != template.width?.wrappedValue ?? 0 { + template.width?.wrappedValue = .init(width) + } + if height != template.height?.wrappedValue ?? 0 { + template.height?.wrappedValue = .init(height) + } } /// Add windows that overlay the last instance of this window if presented. @@ -197,9 +251,9 @@ public struct Window: WindowScene { } /// Update the keyboard shortcuts. - /// - Parameter window: The application window. - func updateShortcuts(window: GTUIApplicationWindow) { - for shortcut in shortcuts { + /// - Parameters: window: The application window. + func updateShortcuts(window: GTUIApplicationWindow, template: Self) { + for shortcut in template.shortcuts { window.addKeyboardShortcut(shortcut.key, id: shortcut.key) { shortcut.value(window) } } } @@ -217,7 +271,9 @@ public struct Window: WindowScene { /// - Returns: The window. public func defaultSize(width: Int, height: Int) -> Self { var newSelf = self - newSelf.defaultSize = (width, height) + newSelf.width = .constant(width) + newSelf.height = .constant(height) + newSelf.setDefaultSize = true return newSelf } @@ -248,6 +304,18 @@ public struct Window: WindowScene { return newSelf } + /// Add a tooltip to the widget. + /// - Parameters: + /// - width: The window's actual width. + /// - height: The window's actual height. + /// - Returns: The window. + public func size(width: Binding? = nil, height: Binding? = nil) -> Self { + var newSelf = self + newSelf.width = width + newSelf.height = height + return newSelf + } + } // swiftlint:enable discouraged_optional_collection diff --git a/Tests/Demo.swift b/Tests/Demo.swift index 6b55dd4..7c45b70 100644 --- a/Tests/Demo.swift +++ b/Tests/Demo.swift @@ -19,7 +19,6 @@ struct Demo: App { Window(id: "main") { window in DemoContent(window: window, app: app) } - .defaultSize(width: 650, height: 450) .overlay { AboutWindow(id: "about", appName: "Demo", developer: "david-swift", version: "Test") .icon(.default(icon: .applicationXExecutable)) @@ -71,13 +70,17 @@ struct Demo: App { } - struct DemoContent: View { + struct DemoContent: WindowView { @State("selection") private var selection: Page = .welcome @State private var toast: Signal = .init() @State("sidebar-visible") private var sidebarVisible = true + @State("width") + private var width = 650 + @State("height") + private var height = 450 var window: GTUIApplicationWindow var app: GTUIApp! @@ -152,6 +155,11 @@ struct Demo: App { .tooltip("Main Menu") } + func window(_ window: Window) -> Window { + window + .size(width: $width, height: $height) + } + } }