From 9f83b2309fa01c9d1d1df833cdc726b6681e442e Mon Sep 17 00:00:00 2001 From: david-swift Date: Tue, 13 Aug 2024 08:33:53 +0200 Subject: [PATCH] Migrate to Meta backend --- .swiftlint.yml | 4 +- Contributors.md | 8 - Package.swift | 13 +- Sources/Adwaita/Menu/MenuButton.swift | 69 +++- Sources/Adwaita/Menu/MenuCollection.swift | 85 ++++ Sources/Adwaita/Menu/MenuContext.swift | 21 + Sources/Adwaita/Menu/MenuEitherView.swift | 23 ++ Sources/Adwaita/Menu/MenuSection.swift | 53 ++- Sources/Adwaita/Menu/Submenu.swift | 57 ++- Sources/Adwaita/Model/AdwaitaApp.swift | 114 ++++++ Sources/Adwaita/Model/AdwaitaMainView.swift | 18 + .../Adwaita/Model/AdwaitaSceneElement.swift | 9 + Sources/Adwaita/Model/AdwaitaWidget.swift | 9 + Sources/Adwaita/Model/ArrayBuilder.swift | 113 ------ Sources/Adwaita/Model/Data Flow/Binding.swift | 148 ------- Sources/Adwaita/Model/Data Flow/Signal.swift | 30 -- Sources/Adwaita/Model/Data Flow/State.swift | 251 ------------ .../Model/Data Flow/StateProtocol.swift | 14 - .../Model/Data Flow/UpdateManager.swift | 24 -- Sources/Adwaita/Model/Extensions/Array.swift | 95 ----- Sources/Adwaita/Model/Extensions/String.swift | 6 +- .../Model/Extensions/ViewStorage.swift | 24 ++ .../Adwaita/Model/{Data Flow => }/Idle.swift | 2 +- .../Adwaita/Model/Signals/SignalData.swift | 101 +++++ Sources/Adwaita/Model/Storage.swift | 82 ++++ .../Model/User Interface/App/App.swift | 72 ---- .../Model/User Interface/App/GTUIApp.swift | 160 -------- .../Model/User Interface/Menu/MenuItem.swift | 27 -- .../User Interface/Menu/MenuItemGroup.swift | 37 -- .../Model/User Interface/View/View.swift | 85 ---- .../User Interface/View/ViewBuilder.swift | 99 ----- .../User Interface/View/ViewStorage.swift | 197 --------- .../Model/User Interface/View/Widget.swift | 28 -- .../Window/GTUIAboutWindow.swift | 53 --- .../Window/GTUIApplicationWindow.swift | 46 --- .../Window/GTUIFileDialog.swift | 138 ------- .../User Interface/Window/GTUIWindow.swift | 97 ----- .../User Interface/Window/WindowScene.swift | 62 --- .../Window/WindowSceneGroup.swift | 50 --- .../User Interface/Window/WindowStorage.swift | 33 -- .../User Interface/Window/WindowType.swift | 19 - .../View => }/WindowView.swift | 2 +- Sources/Adwaita/View/Button+.swift | 26 +- Sources/Adwaita/View/CheckButton+.swift | 2 +- .../Adwaita/View/Dialogs/AboutDialog.swift | 82 ++-- .../Adwaita/View/Dialogs/AlertDialog.swift | 55 +-- Sources/Adwaita/View/Dialogs/Dialog.swift | 80 ++-- Sources/Adwaita/View/Dialogs/FileDialog.swift | 225 +++++++++++ Sources/Adwaita/View/EitherView.swift | 44 ++ Sources/Adwaita/View/Fixed+.swift | 18 +- Sources/Adwaita/View/FlowBox+.swift | 22 +- Sources/Adwaita/View/ForEach.swift | 55 ++- Sources/Adwaita/View/Forms/ActionRow+.swift | 2 +- Sources/Adwaita/View/Forms/ComboRow+.swift | 2 +- Sources/Adwaita/View/Forms/EntryRow+.swift | 15 +- Sources/Adwaita/View/Forms/Form.swift | 2 +- .../View/Forms/PasswordEntryRow+.swift | 14 +- Sources/Adwaita/View/Forms/SpinRow+.swift | 4 +- .../Adwaita/View/Generated/ActionRow.swift | 85 ++-- .../Adwaita/View/Generated/AspectFrame.swift | 50 ++- Sources/Adwaita/View/Generated/Avatar.swift | 42 +- Sources/Adwaita/View/Generated/Banner.swift | 42 +- Sources/Adwaita/View/Generated/Bin.swift | 42 +- Sources/Adwaita/View/Generated/Box.swift | 54 +-- Sources/Adwaita/View/Generated/Button.swift | 54 +-- .../View/Generated/ButtonContent.swift | 42 +- Sources/Adwaita/View/Generated/Carousel.swift | 56 +-- .../Adwaita/View/Generated/CenterBox.swift | 60 +-- .../Adwaita/View/Generated/CheckButton.swift | 56 +-- Sources/Adwaita/View/Generated/Clamp.swift | 46 ++- Sources/Adwaita/View/Generated/ComboRow.swift | 89 ++-- Sources/Adwaita/View/Generated/EntryRow.swift | 62 +-- .../Adwaita/View/Generated/ExpanderRow.swift | 96 +++-- Sources/Adwaita/View/Generated/Fixed.swift | 34 +- Sources/Adwaita/View/Generated/FlowBox.swift | 56 +-- .../Adwaita/View/Generated/HeaderBar.swift | 66 +-- Sources/Adwaita/View/Generated/Label.swift | 64 +-- Sources/Adwaita/View/Generated/LevelBar.swift | 42 +- .../Adwaita/View/Generated/LinkButton.swift | 58 +-- Sources/Adwaita/View/Generated/ListBox.swift | 48 ++- Sources/Adwaita/View/Generated/Menu.swift | 80 ++-- .../View/Generated/NavigationView.swift | 38 +- Sources/Adwaita/View/Generated/Overlay.swift | 49 ++- .../View/Generated/OverlaySplitView.swift | 70 ++-- .../View/Generated/PasswordEntryRow.swift | 56 +-- Sources/Adwaita/View/Generated/Picture.swift | 55 ++- Sources/Adwaita/View/Generated/Popover.swift | 58 +-- .../View/Generated/PreferencesGroup.swift | 53 +-- .../View/Generated/PreferencesPage.swift | 51 ++- .../View/Generated/PreferencesRow.swift | 42 +- .../Adwaita/View/Generated/ProgressBar.swift | 44 +- .../View/Generated/ScrolledWindow.swift | 60 +-- .../Adwaita/View/Generated/SearchBar.swift | 52 +-- .../Adwaita/View/Generated/SearchEntry.swift | 52 +-- .../Adwaita/View/Generated/Separator.swift | 34 +- Sources/Adwaita/View/Generated/SpinRow.swift | 95 ++--- Sources/Adwaita/View/Generated/Spinner.swift | 36 +- .../Adwaita/View/Generated/SplitButton.swift | 70 ++-- .../Adwaita/View/Generated/StatusPage.swift | 48 ++- .../Adwaita/View/Generated/SwitchRow.swift | 85 ++-- .../Adwaita/View/Generated/ToastOverlay.swift | 42 +- .../Adwaita/View/Generated/ToggleButton.swift | 60 +-- .../Adwaita/View/Generated/ToolbarView.swift | 64 +-- .../Adwaita/View/Generated/WindowTitle.swift | 38 +- Sources/Adwaita/View/HStack.swift | 2 +- Sources/Adwaita/View/List.swift | 28 +- Sources/Adwaita/View/Menu+.swift | 15 +- .../Adwaita/View/Modifiers/AnyView++.swift | 129 ++++++ ...{InspectorWrapper.swift => AnyView+.swift} | 270 ++++++------- .../View/Modifiers/AppearObserver.swift | 86 ---- Sources/Adwaita/View/Modifiers/Clamp+.swift | 12 +- .../View/Modifiers/ContentModifier.swift | 59 --- Sources/Adwaita/View/Modifiers/Freeze.swift | 46 --- .../View/Modifiers/ModifierStopper.swift | 41 -- Sources/Adwaita/View/Modifiers/Popover+.swift | 23 +- .../View/Modifiers/ToastOverlay+.swift | 13 +- Sources/Adwaita/View/Modifiers/View+.swift | 65 --- .../Adwaita/View/NavigationSplitView.swift | 69 ++-- Sources/Adwaita/View/NavigationView+.swift | 103 +++-- Sources/Adwaita/View/OverlaySplitView+.swift | 2 +- Sources/Adwaita/View/Picture+.swift | 26 +- Sources/Adwaita/View/StateWrapper.swift | 62 --- Sources/Adwaita/View/StatusPage+.swift | 2 +- Sources/Adwaita/View/Text.swift | 4 +- Sources/Adwaita/View/VStack.swift | 54 ++- Sources/Adwaita/View/ViewStack.swift | 62 ++- Sources/Adwaita/View/ViewSwitcher.swift | 67 ++-- Sources/Adwaita/Window/AboutWindow.swift | 132 ------ Sources/Adwaita/Window/FileDialog.swift | 122 ------ Sources/Adwaita/Window/Window.swift | 379 +++++++++--------- Sources/CAdw/shim.h | 3 +- {Tests => Sources/Demo}/AlertDialogDemo.swift | 0 {Tests => Sources/Demo}/CarouselDemo.swift | 0 {Tests => Sources/Demo}/CounterDemo.swift | 3 +- Sources/Demo/Demo.swift | 170 ++++++++ {Tests => Sources/Demo}/DialogDemo.swift | 0 {Tests => Sources/Demo}/DiceDemo.swift | 0 {Tests => Sources/Demo}/FixedDemo.swift | 0 {Tests => Sources/Demo}/FlowBoxDemo.swift | 0 {Tests => Sources/Demo}/FormDemo.swift | 12 +- {Tests => Sources/Demo}/IdleDemo.swift | 10 +- {Tests => Sources/Demo}/ListDemo.swift | 0 .../Demo}/NavigationViewDemo.swift | 2 +- {Tests => Sources/Demo}/Page.swift | 4 +- .../Demo}/PasswordCheckerDemo.swift | 8 +- {Tests => Sources/Demo}/PictureDemo.swift | 12 +- {Tests => Sources/Demo}/PopoverDemo.swift | 0 {Tests => Sources/Demo}/ToastDemo.swift | 0 {Tests => Sources/Demo}/ToolbarDemo.swift | 2 +- {Tests => Sources/Demo}/TransitionDemo.swift | 0 .../Demo}/ViewSwitcherDemo.swift | 4 +- {Tests => Sources/Demo}/WindowsDemo.swift | 2 +- Sources/Generation/GIR/Class+.swift | 13 +- Sources/Generation/GIR/Class.swift | 30 +- Sources/Generation/GIR/ClassLike.swift | 8 +- Sources/Generation/GIR/Property.swift | 50 +-- Tests/Demo.swift | 182 --------- 157 files changed, 3515 insertions(+), 4675 deletions(-) delete mode 100644 Contributors.md create mode 100644 Sources/Adwaita/Menu/MenuCollection.swift create mode 100644 Sources/Adwaita/Menu/MenuContext.swift create mode 100644 Sources/Adwaita/Menu/MenuEitherView.swift create mode 100644 Sources/Adwaita/Model/AdwaitaApp.swift create mode 100644 Sources/Adwaita/Model/AdwaitaMainView.swift create mode 100644 Sources/Adwaita/Model/AdwaitaSceneElement.swift create mode 100644 Sources/Adwaita/Model/AdwaitaWidget.swift delete mode 100644 Sources/Adwaita/Model/ArrayBuilder.swift delete mode 100644 Sources/Adwaita/Model/Data Flow/Binding.swift delete mode 100644 Sources/Adwaita/Model/Data Flow/Signal.swift delete mode 100644 Sources/Adwaita/Model/Data Flow/State.swift delete mode 100644 Sources/Adwaita/Model/Data Flow/StateProtocol.swift delete mode 100644 Sources/Adwaita/Model/Data Flow/UpdateManager.swift create mode 100644 Sources/Adwaita/Model/Extensions/ViewStorage.swift rename Sources/Adwaita/Model/{Data Flow => }/Idle.swift (99%) create mode 100644 Sources/Adwaita/Model/Signals/SignalData.swift create mode 100644 Sources/Adwaita/Model/Storage.swift delete mode 100644 Sources/Adwaita/Model/User Interface/App/App.swift delete mode 100644 Sources/Adwaita/Model/User Interface/App/GTUIApp.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift delete mode 100644 Sources/Adwaita/Model/User Interface/View/View.swift delete mode 100644 Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift delete mode 100644 Sources/Adwaita/Model/User Interface/View/ViewStorage.swift delete mode 100644 Sources/Adwaita/Model/User Interface/View/Widget.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/WindowScene.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/WindowSceneGroup.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift delete mode 100644 Sources/Adwaita/Model/User Interface/Window/WindowType.swift rename Sources/Adwaita/Model/{User Interface/View => }/WindowView.swift (89%) create mode 100644 Sources/Adwaita/View/Dialogs/FileDialog.swift create mode 100644 Sources/Adwaita/View/EitherView.swift create mode 100644 Sources/Adwaita/View/Modifiers/AnyView++.swift rename Sources/Adwaita/View/Modifiers/{InspectorWrapper.swift => AnyView+.swift} (50%) delete mode 100644 Sources/Adwaita/View/Modifiers/AppearObserver.swift delete mode 100644 Sources/Adwaita/View/Modifiers/ContentModifier.swift delete mode 100644 Sources/Adwaita/View/Modifiers/Freeze.swift delete mode 100644 Sources/Adwaita/View/Modifiers/ModifierStopper.swift delete mode 100644 Sources/Adwaita/View/Modifiers/View+.swift delete mode 100644 Sources/Adwaita/View/StateWrapper.swift delete mode 100644 Sources/Adwaita/Window/AboutWindow.swift delete mode 100644 Sources/Adwaita/Window/FileDialog.swift rename {Tests => Sources/Demo}/AlertDialogDemo.swift (100%) rename {Tests => Sources/Demo}/CarouselDemo.swift (100%) rename {Tests => Sources/Demo}/CounterDemo.swift (91%) create mode 100644 Sources/Demo/Demo.swift rename {Tests => Sources/Demo}/DialogDemo.swift (100%) rename {Tests => Sources/Demo}/DiceDemo.swift (100%) rename {Tests => Sources/Demo}/FixedDemo.swift (100%) rename {Tests => Sources/Demo}/FlowBoxDemo.swift (100%) rename {Tests => Sources/Demo}/FormDemo.swift (91%) rename {Tests => Sources/Demo}/IdleDemo.swift (82%) rename {Tests => Sources/Demo}/ListDemo.swift (100%) rename {Tests => Sources/Demo}/NavigationViewDemo.swift (98%) rename {Tests => Sources/Demo}/Page.swift (96%) rename {Tests => Sources/Demo}/PasswordCheckerDemo.swift (97%) rename {Tests => Sources/Demo}/PictureDemo.swift (59%) rename {Tests => Sources/Demo}/PopoverDemo.swift (100%) rename {Tests => Sources/Demo}/ToastDemo.swift (100%) rename {Tests => Sources/Demo}/ToolbarDemo.swift (98%) rename {Tests => Sources/Demo}/TransitionDemo.swift (100%) rename {Tests => Sources/Demo}/ViewSwitcherDemo.swift (97%) rename {Tests => Sources/Demo}/WindowsDemo.swift (97%) delete mode 100644 Tests/Demo.swift diff --git a/.swiftlint.yml b/.swiftlint.yml index 4a37d11..00935e2 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -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 @@ -51,7 +49,6 @@ opt_in_rules: - multiline_parameters_brackets - no_extension_access_modifier - no_grouping_extension - - no_magic_numbers - number_separator - operator_usage_whitespace - optional_enum_case_matching @@ -164,3 +161,4 @@ type_contents_order: excluded: - Sources/Adwaita/View/Generated/ - Sources/Adwaita/Adwaita.docc/ + - .build/ \ No newline at end of file diff --git a/Contributors.md b/Contributors.md deleted file mode 100644 index a97940e..0000000 --- a/Contributors.md +++ /dev/null @@ -1,8 +0,0 @@ -# Contributors - -- [david-swift](https://github.com/david-swift) -- [Greg Cotten](https://github.com/gregcotten) -- [Zev Eisenberg](https://github.com/ZevEisenberg) -- [Jay Wren](https://github.com/jrwren) -- [Amzd](https://github.com/amzd) -- [lambdaclan](https://github.com/lambdaclan) diff --git a/Package.swift b/Package.swift index c6950ce..be5cc63 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.8 +// swift-tools-version: 5.9 // // Package.swift // Adwaita @@ -11,9 +11,7 @@ import PackageDescription /// The Adwaita package. let package = Package( name: "Adwaita", - platforms: [ - .macOS(.v10_15) - ], + platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)], products: [ .library( name: "Adwaita", @@ -25,6 +23,7 @@ let package = Package( ) ], dependencies: [ + .package(url: "https://github.com/AparokshaUI/Meta", branch: "main"), .package( url: "https://github.com/david-swift/LevenshteinTransformations", from: "0.1.1" @@ -40,7 +39,8 @@ let package = Package( name: "Adwaita", dependencies: [ "CAdw", - .product(name: "LevenshteinTransformations", package: "LevenshteinTransformations") + .product(name: "LevenshteinTransformations", package: "LevenshteinTransformations"), + .product(name: "Meta", package: "Meta") ] ), .executableTarget( @@ -51,8 +51,7 @@ let package = Package( ), .executableTarget( name: "Demo", - dependencies: ["Adwaita"], - path: "Tests" + dependencies: ["Adwaita"] ) ] ) diff --git a/Sources/Adwaita/Menu/MenuButton.swift b/Sources/Adwaita/Menu/MenuButton.swift index 9b005f5..e697e7b 100644 --- a/Sources/Adwaita/Menu/MenuButton.swift +++ b/Sources/Adwaita/Menu/MenuButton.swift @@ -8,7 +8,7 @@ import CAdw /// A button widget for menus. -public struct MenuButton: MenuItem { +public struct MenuButton: MenuWidget { /// The button's label. var label: String @@ -16,8 +16,6 @@ public struct MenuButton: MenuItem { var handler: () -> Void /// The keyboard shortcut. var shortcut = "" - /// Whether the keyboard shortcut is currently available. - var active = true /// Whether to prefer adding the action to the application window. var preferApplicationWindow: Bool @@ -35,33 +33,62 @@ public struct MenuButton: MenuItem { self.handler = handler } - /// Add the button to a menu. + /// The view storage. /// - Parameters: - /// - menu: The menu. - /// - app: The application containing the menu. - /// - window: The application window containing the menu. - public func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) { - if let window, preferApplicationWindow { - window.addKeyboardShortcut(active ? shortcut : "", id: filteredLabel, handler: handler) - g_menu_append(menu, label, "win." + filteredLabel) + /// - modifiers: Modify the views before updating. + /// - type: The type of the views. + /// - Returns: The view storage. + public func container( + modifiers: [(any AnyView) -> any AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { + let storage = ViewStorage(nil) + let getItem: (AdwaitaApp, AdwaitaWindow?) -> OpaquePointer? = { app, window in + var label = filteredLabel + storage.fields["app"] = app + if let window, preferApplicationWindow { + app.addKeyboardShortcut(shortcut, id: filteredLabel, window: window, handler: handler) + label = "win." + label + storage.fields["window"] = window + } else { + app.addKeyboardShortcut(shortcut, id: filteredLabel, handler: handler) + label = "app." + label + } + return g_menu_item_new(self.label, label) + } + storage.pointer = getItem + return storage + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - modifiers: Modify the views before updating. + /// - updateProperties: Whether to update the properties. + /// - type: The type of the views. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + guard let app = storage.fields["app"] as? AdwaitaApp else { + return + } + if let window = storage.fields["window"] as? AdwaitaWindow, preferApplicationWindow { + app.addKeyboardShortcut(shortcut, id: filteredLabel, window: window, handler: handler) } else { - app.addKeyboardShortcut(active ? shortcut : "", id: filteredLabel, handler: handler) - g_menu_append(menu, label, "app." + filteredLabel) + app.addKeyboardShortcut(shortcut, id: filteredLabel, handler: handler) } } /// Create a keyboard shortcut for an application from a button. /// /// Note that the keyboard shortcut is available after the view has been visible for the first time. - /// - Parameters: - /// - shortcut: The keyboard shortcut. - /// - active: Whether the keyboard shortcut is currently available. + /// - Parameter shortcut: The keyboard shortcut. /// - Returns: The button. - public func keyboardShortcut(_ shortcut: String, active: Bool = true) -> Self { - var newSelf = self - newSelf.shortcut = shortcut - newSelf.active = active - return newSelf + public func keyboardShortcut(_ shortcut: String) -> Self { + modify { $0.shortcut = shortcut } } } diff --git a/Sources/Adwaita/Menu/MenuCollection.swift b/Sources/Adwaita/Menu/MenuCollection.swift new file mode 100644 index 0000000..23079a3 --- /dev/null +++ b/Sources/Adwaita/Menu/MenuCollection.swift @@ -0,0 +1,85 @@ +// +// MenuCollection.swift +// Adwaita +// +// Created by david-swift on 02.08.2024. +// + +import CAdw +import Foundation + +/// A collection of menus. +public struct MenuCollection: MenuWidget, Wrapper { + + /// The content of the collection. + var content: Body + + /// Initialize a menu. + /// - Parameter content: The content of the collection. + public init(@ViewBuilder content: @escaping () -> Body) { + self.content = content() + } + + /// The view storage. + /// - Parameters: + /// - modifiers: Modify the views before updating. + /// - type: The type of the views. + /// - Returns: The view storage. + public func container( + modifiers: [(any AnyView) -> any AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { + let storages = content.storages(modifiers: modifiers, type: type) + return .init(nil, content: [.mainContent: storages]) + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - modifiers: Modify the views before updating. + /// - updateProperties: Whether to update the properties. + /// - type: The type of the views. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + guard let storages = storage.content[.mainContent] else { + return + } + content.update(storages, modifiers: modifiers, updateProperties: updateProperties, type: type) + } + + /// Render the collection as a menu. + /// - Parameters: + /// - app: The app object. + /// - window: The window object. + /// - Returns: The view storage with the GMenu as the pointer. + public func getMenu(app: AdwaitaApp, window: AdwaitaWindow?) -> ViewStorage { + let menu = g_menu_new() + let storage = container(modifiers: [], type: MenuContext.self) + initializeMenu(menu: menu, storage: storage, app: app, window: window) + storage.pointer = menu + return storage + } + + /// Initialize a menu. + /// - Parameters: + /// - menu: The pointer to the GMenu. + /// - storage: The storage for the menu's content. + /// - app: The app object. + /// - window: The window object. + func initializeMenu(menu: OpaquePointer?, storage: ViewStorage, app: AdwaitaApp, window: AdwaitaWindow?) { + if storage.pointer == nil { + for element in storage.content[.mainContent] ?? [] { + initializeMenu(menu: menu, storage: element, app: app, window: window) + } + } else if let item = (storage.pointer as? (AdwaitaApp, AdwaitaWindow?) -> OpaquePointer?) { + let item = item(app, window) + g_menu_append_item(menu, item) + storage.pointer = item + } + } + +} diff --git a/Sources/Adwaita/Menu/MenuContext.swift b/Sources/Adwaita/Menu/MenuContext.swift new file mode 100644 index 0000000..e103dcf --- /dev/null +++ b/Sources/Adwaita/Menu/MenuContext.swift @@ -0,0 +1,21 @@ +// +// MenuContext.swift +// Adwaita +// +// Created by david-swift on 01.08.24. +// + +/// The menu items view context. +public enum MenuContext: ViewRenderData { + + /// The type of the widgets. + public typealias WidgetType = MenuWidget + /// The wrapper type. + public typealias WrapperType = MenuCollection + /// The either view type. + public typealias EitherViewType = MenuEitherView + +} + +/// The type of the widgets. +public protocol MenuWidget: Meta.Widget { } diff --git a/Sources/Adwaita/Menu/MenuEitherView.swift b/Sources/Adwaita/Menu/MenuEitherView.swift new file mode 100644 index 0000000..33df630 --- /dev/null +++ b/Sources/Adwaita/Menu/MenuEitherView.swift @@ -0,0 +1,23 @@ +// +// MenuEitherView.swift +// Adwaita +// +// Created by david-swift on 06.08.2024. +// + +/// Show one of two views depending on a condition. +public struct MenuEitherView: Meta.EitherView, SimpleView { + + /// The view. + public var view: Body + + /// Initialize an either view. + /// - Parameters: + /// - condition: The condition. + /// - view1: The first view. + /// - view2: The second view. + public init(_ condition: Bool, view1: () -> Body, else view2: () -> Body) { + self.view = condition ? view1() : view2() + } + +} diff --git a/Sources/Adwaita/Menu/MenuSection.swift b/Sources/Adwaita/Menu/MenuSection.swift index e4d48b1..4cf19dc 100644 --- a/Sources/Adwaita/Menu/MenuSection.swift +++ b/Sources/Adwaita/Menu/MenuSection.swift @@ -1,5 +1,5 @@ // -// Submenu.swift +// MenuButton.swift // Adwaita // // Created by david-swift on 22.10.23. @@ -7,29 +7,54 @@ import CAdw -/// A section for menus. -public struct MenuSection: MenuItem { +/// A button widget for menus. +public struct MenuSection: MenuWidget { /// The content of the section. - var sectionContent: MenuContent + var sectionContent: Body /// Initialize a section for menus. /// - Parameter content: The content of the section. - public init(@MenuBuilder content: () -> MenuContent) { + public init(@ViewBuilder content: () -> Body) { self.sectionContent = content() } - /// Add the section to a menu. + /// The view storage. /// - Parameters: - /// - menu: The menu. - /// - app: The application containing the menu. - /// - window: The application window containing the menu. - public func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) { - let section = g_menu_new() - g_menu_append_section(menu, nil, section?.cast()) - for element in sectionContent { - element.addMenuItems(menu: section, app: app, window: window) + /// - modifiers: Modify the views before updating. + /// - type: The type of the views. + /// - Returns: The view storage. + public func container( + modifiers: [(any AnyView) -> any AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { + let storage = ViewStorage(nil) + let getItem: (AdwaitaApp, AdwaitaWindow?) -> OpaquePointer? = { app, window in + let childStorage = MenuCollection { sectionContent }.getMenu(app: app, window: window) + storage.content[.mainContent] = [childStorage] + return g_menu_item_new_section(nil, childStorage.opaquePointer?.cast()) } + storage.pointer = getItem + return storage + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - modifiers: Modify the views before updating. + /// - updateProperties: Whether to update the properties. + /// - type: The type of the views. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + guard let content = storage.content[.mainContent]?.first else { + return + } + MenuCollection { sectionContent } + .updateStorage(content, modifiers: [], updateProperties: updateProperties, type: MenuContext.self) } } diff --git a/Sources/Adwaita/Menu/Submenu.swift b/Sources/Adwaita/Menu/Submenu.swift index 5cede15..2cae642 100644 --- a/Sources/Adwaita/Menu/Submenu.swift +++ b/Sources/Adwaita/Menu/Submenu.swift @@ -8,33 +8,58 @@ import CAdw /// A submenu widget. -public struct Submenu: MenuItem { +public struct Submenu: MenuWidget { - /// The submenu's label. + /// The label of the submenu. var label: String /// The content of the submenu. - var submenuContent: MenuContent + var content: Body /// Initialize a submenu. /// - Parameters: - /// - label: The submenu's label. - /// - content: The content of the submenu. - public init(_ label: String, @MenuBuilder content: () -> MenuContent) { + /// - label: The submenu's label. + /// - content: The content of the section. + public init(_ label: String, @ViewBuilder content: () -> Body) { self.label = label - self.submenuContent = content() + self.content = content() } - /// Add the submenu to a menu. + /// The view storage. /// - Parameters: - /// - menu: The menu. - /// - app: The application containing the menu. - /// - window: The application window containing the menu. - public func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) { - let submenu = g_menu_new() - g_menu_append_submenu(menu, label, submenu?.cast()) - for element in submenuContent { - element.addMenuItems(menu: submenu, app: app, window: window) + /// - modifiers: Modify the views before updating. + /// - type: The type of the views. + /// - Returns: The view storage. + public func container( + modifiers: [(any AnyView) -> any AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { + let storage = ViewStorage(nil) + let getItem: (AdwaitaApp, AdwaitaWindow?) -> OpaquePointer? = { app, window in + let childStorage = MenuCollection { content }.getMenu(app: app, window: window) + storage.content[.mainContent] = [childStorage] + return g_menu_item_new_submenu(label, childStorage.opaquePointer?.cast()) } + storage.pointer = getItem + return storage + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - modifiers: Modify the views before updating. + /// - updateProperties: Whether to update the properties. + /// - type: The type of the views. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + guard let content = storage.content[.mainContent]?.first else { + return + } + MenuCollection { self.content } + .updateStorage(content, modifiers: [], updateProperties: updateProperties, type: MenuContext.self) } } diff --git a/Sources/Adwaita/Model/AdwaitaApp.swift b/Sources/Adwaita/Model/AdwaitaApp.swift new file mode 100644 index 0000000..1718db8 --- /dev/null +++ b/Sources/Adwaita/Model/AdwaitaApp.swift @@ -0,0 +1,114 @@ +// +// AdwaitaApp.swift +// Adwaita +// +// Created by david-swift on 31.07.2024. +// + +import CAdw +@_exported import Meta + +/// The Meta app storage for the Adwaita backend. +public class AdwaitaApp: AppStorage { + + /// The scene element type of the Adwaita backend. + public typealias SceneElementType = AdwaitaSceneElement + /// The widget type of the Adwaita backend. + public typealias WidgetType = AdwaitaWidget + /// The wrapper type of the Adwaita backend. + public typealias WrapperType = VStack + + /// The app storage. + public var storage: StandardAppStorage = .init() + /// The signal data for running. + var runSignal: SignalData? + /// The application's pointer. + var pointer: UnsafeMutablePointer? + /// Fields for storing closure data. + var signals: [String: SignalData] = [:] + + /// Initialize the app storage. + /// - Parameter id: The identifier. + public required init(id: String) { + pointer = adw_application_new(id, G_APPLICATION_DEFAULT_FLAGS)?.cast() + } + + /// Copy a string to the clipboard. + public static func copy(_ text: String) { + let clipboard = gdk_display_get_clipboard(gdk_display_get_default()) + gdk_clipboard_set_text(clipboard, text) + } + + /// Execute the app. + /// - Parameter setup: Set the scene elements up. + public func run(setup: @escaping () -> Void) { + let data = SignalData { + setup() + } + runSignal = data + data.connect(pointer: pointer, signal: "activate") + g_application_run(pointer?.cast(), 0, nil) + } + + /// Quit the app. + public func quit() { + g_application_quit(pointer?.cast()) + } + + /// Add a keyboard shortcut to the application. + /// - Parameters: + /// - shortcut: The keyboard shortcut. + /// - id: The action's id. + /// - window: Optionally an application window. + /// - handler: The action's handler. + func addKeyboardShortcut( + _ shortcut: String, + id: String, + window: AdwaitaWindow? = nil, + handler: @escaping () -> Void + ) { + if window == nil, let data = signals[id] { + data.closure = { _ in handler() } + return + } else if let data = window?.signals[id] { + data.closure = { _ in handler() } + return + } + let action = g_simple_action_new(id, nil) + let data = SignalData(closure: handler) + g_signal_connect_data( + action?.cast(), + "activate", + unsafeBitCast(data.threeParamsHandler, to: GCallback.self), + Unmanaged.passUnretained(data).toOpaque().cast(), + nil, + G_CONNECT_AFTER + ) + if let window { + g_action_map_add_action(.init(window.pointer), action) + window.signals[id] = data + } else { + g_action_map_add_action(.init(pointer), action) + signals[id] = data + } + gtk_application_set_accels_for_action(pointer, (window == nil ? "app." : "win.") + id, [shortcut].cArray) + } + + /// Remove a keyboard shortcut from the application. + /// - Parameters: + /// - id: The keyboard shortcut's id. + /// - window: Optionally an application window. + func removeKeyboardShortcut( + id: String, + window: AdwaitaWindow? = nil + ) { + if let window { + g_action_map_remove_action(.init(window.pointer), id) + window.signals.removeValue(forKey: id) + } else { + g_action_map_remove_action(.init(pointer), id) + signals.removeValue(forKey: id) + } + } + +} diff --git a/Sources/Adwaita/Model/AdwaitaMainView.swift b/Sources/Adwaita/Model/AdwaitaMainView.swift new file mode 100644 index 0000000..d803a98 --- /dev/null +++ b/Sources/Adwaita/Model/AdwaitaMainView.swift @@ -0,0 +1,18 @@ +// +// AdwaitaMainView.swift +// Adwaita +// +// Created by david-swift on 31.07.2024. +// + +/// The type of widgets of the Adwaita backend. +public enum AdwaitaMainView: ViewRenderData { + + /// The type of the widgets. + public typealias WidgetType = AdwaitaWidget + /// The wrapper type. + public typealias WrapperType = VStackWrapper + /// The either view type. + public typealias EitherViewType = EitherView + +} diff --git a/Sources/Adwaita/Model/AdwaitaSceneElement.swift b/Sources/Adwaita/Model/AdwaitaSceneElement.swift new file mode 100644 index 0000000..c97790d --- /dev/null +++ b/Sources/Adwaita/Model/AdwaitaSceneElement.swift @@ -0,0 +1,9 @@ +// +// AdwaitaSceneElement.swift +// Adwaita +// +// Created by david-swift on 31.07.2024. +// + +/// The type of scene elements of the Adwaita backend. +public protocol AdwaitaSceneElement: SceneElement { } diff --git a/Sources/Adwaita/Model/AdwaitaWidget.swift b/Sources/Adwaita/Model/AdwaitaWidget.swift new file mode 100644 index 0000000..8606e49 --- /dev/null +++ b/Sources/Adwaita/Model/AdwaitaWidget.swift @@ -0,0 +1,9 @@ +// +// AdwaitaWidget.swift +// Adwaita +// +// Created by david-swift on 31.07.2024. +// + +/// The type of widgets of the Adwaita backend. +public protocol AdwaitaWidget: Widget { } diff --git a/Sources/Adwaita/Model/ArrayBuilder.swift b/Sources/Adwaita/Model/ArrayBuilder.swift deleted file mode 100644 index 2adf197..0000000 --- a/Sources/Adwaita/Model/ArrayBuilder.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// ArrayBuilder.swift -// Adwaita -// -// Created by david-swift on 05.08.23. -// -// Thanks to Jaden Geller for the GitHub Gist: -// "ArrayBuilder.swift" -// https://gist.github.com/JadenGeller/c375fc15ad5900a0ddac4ed8ba8307a9 -// - -/// The ``ArrayBuilder`` is a simple result builder that outputs an array of any type. -/// -/// You can define any array using Swift's DSL: -/// ```swift -/// @ArrayBuilder var string: [String] { -/// "Hello, " -/// if bool { -/// "world!" -/// } else { -/// "colibri!" -/// } -/// for x in 0...10 { -/// "\nIteration Number \(x)" -/// } -/// } -/// ``` -@resultBuilder -public enum ArrayBuilder { - - /// A component used in the ``ArrayBuilder``. - public enum Component { - - /// An element as a component. - case element(_: Element) - /// An array of components as a component. - case components(_: [Self]) - - } - - /// Build combined results from statement blocks. - /// - Parameter components: The components. - /// - Returns: The components in a component. - public static func buildBlock(_ elements: Component...) -> Component { - .components(elements) - } - - /// Translate an element into an ``ArrayBuilder.Component``. - /// - Parameter element: The element to translate. - /// - Returns: A component created from the element. - public static func buildExpression(_ element: Element) -> Component { - .element(element) - } - - /// Translate an array of elements into an ``ArrayBuilder.Component``. - /// - Parameter elements: The elements to translate. - /// - Returns: A component created from the element. - public static func buildExpression(_ elements: [Element]) -> Component { - var components: [Component] = [] - for element in elements { - components.append(.element(element)) - } - return .components(components) - } - - /// Fetch a component. - /// - Parameter component: A component. - /// - Returns: The component. - public static func buildExpression(_ component: Component) -> Component { - component - } - - /// Enables support for `if` statements without an `else`. - /// - Parameter component: An optional component. - /// - Returns: A nonoptional component. - public static func buildOptional(_ component: Component?) -> Component { - component ?? .components([]) - } - - /// Enables support for `if`-`else` and `switch` statements. - /// - Parameter component: A component. - /// - Returns: The component. - public static func buildEither(first component: Component) -> Component { - component - } - - /// Enables support for `if`-`else` and `switch` statements. - /// - Parameter component: A component. - /// - Returns: The component. - public static func buildEither(second component: Component) -> Component { - component - } - - /// Enables support for `for..in` loops. - /// - Parameter components: The components as a two dimensional array. - /// - Returns: The components as a one dimensional array. - public static func buildArray(_ components: [Component]) -> Component { - .components(components) - } - - /// Convert a component to an array of elements. - /// - Parameter component: The component to convert. - /// - Returns: The generated array of elements. - public static func buildFinalResult(_ component: Component) -> [Element] { - switch component { - case let .element(element): - return [element] - case let .components(components): - return components.flatMap { buildFinalResult($0) } - } - } - -} diff --git a/Sources/Adwaita/Model/Data Flow/Binding.swift b/Sources/Adwaita/Model/Data Flow/Binding.swift deleted file mode 100644 index e05d1ac..0000000 --- a/Sources/Adwaita/Model/Data Flow/Binding.swift +++ /dev/null @@ -1,148 +0,0 @@ -// -// Binding.swift -// Adwaita -// -// Created by david-swift on 06.08.23. -// - -/// A property wrapper for a property of a view that binds the property of the parent view. -/// -/// ```swift -/// struct Grandparent: View { -/// -/// @State private var state = false -/// -/// var view: Body { -/// Parent(value: $state) -/// } -/// } -/// -/// struct Parent: View { -/// -/// @Binding var value: Bool -/// -/// var view: Body { -/// Child(value: $value) -/// } -/// -/// } -/// -/// struct Child: View { -/// -/// @Binding var value: Bool -/// -/// var view: Body { -/// // ... -/// } -/// -/// } -/// ``` -@propertyWrapper -@dynamicMemberLookup -public struct Binding { - - /// The value. - public var wrappedValue: Value { - get { - getValue() - } - nonmutating set { - setValue(newValue) - for handler in handlers { - handler(newValue) - } - } - } - - /// Get the value as a binding using the `$` prefix. - public var projectedValue: Binding { - .init { - wrappedValue - } set: { newValue in - wrappedValue = newValue - } - } - - /// The closure for getting the value. - private let getValue: () -> Value - /// The closure for settings the value. - private let setValue: (Value) -> Void - /// Handlers observing whether the binding changes. - private var handlers: [(Value) -> Void] = [] - - /// Get a property of any content of a `Binding` as a `Binding`. - /// - Parameter dynamicMember: The path to the member. - /// - Returns: The binding. - public subscript(dynamicMember keyPath: WritableKeyPath) -> Binding { - .init { - wrappedValue[keyPath: keyPath] - } set: { newValue in - wrappedValue[keyPath: keyPath] = newValue - } - } - - /// Initialize a property that is bound from a parent view. - /// - Parameters: - /// - get: The closure for getting the value. - /// - set: The closure for setting the value. - public init(get: @escaping () -> Value, set: @escaping (Value) -> Void) { - self.getValue = get - self.setValue = set - } - - /// Initialize a property that does not react to changes in the child view. - /// - Parameters: - /// - value: The constant value. - /// - Returns: The binding. - public static func constant(_ value: Value) -> Binding { - .init { - value - } set: { _ in - } - } - - /// Observe whether data is changed over this binding. - /// - Parameter handler: The handler. - /// - Returns: The binding. - public func onSet(_ handler: @escaping (Value) -> Void) -> Self { - var newSelf = self - newSelf.handlers.append(handler) - return newSelf - } - -} - -extension Binding where Value: MutableCollection { - - /// Get a child at a certain index of the array as a binding. - /// - Parameters: - /// - index: The child's index. - /// - defaultValue: The value used if the index is out of range does not exist. - /// - Returns: The child as a binding. - public subscript(safe index: Value.Index?, default defaultValue: Value.Element) -> Binding { - .init { - if let index, wrappedValue.indices.contains(index) { - return wrappedValue[index] - } - return defaultValue - } set: { newValue in - if let index, wrappedValue.indices.contains(index) { - wrappedValue[index] = newValue - } - } - } - -} - -extension Binding where Value: MutableCollection, Value.Element: Identifiable { - - /// Get a child of the array with a certain id as a binding. - /// - Parameters: - /// - id: The child's id. - /// - defaultValue: The value used if the index is out of range does not exist. - /// - Returns: The child as a binding. - public subscript(id id: Value.Element.ID?, default defaultValue: Value.Element) -> Binding { - self[safe: wrappedValue.firstIndex { $0.id == id }, default: defaultValue] - } - -} diff --git a/Sources/Adwaita/Model/Data Flow/Signal.swift b/Sources/Adwaita/Model/Data Flow/Signal.swift deleted file mode 100644 index 920d8c2..0000000 --- a/Sources/Adwaita/Model/Data Flow/Signal.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// Signal.swift -// Adwaita -// -// Created by david-swift on 30.11.23. -// - -import Foundation - -/// A type that signalizes an action. -public struct Signal { - - /// An action is signalized by toggling a boolean to `true` and back to `false`. - @State var boolean = false - /// A signal has a unique identifier. - public let id: UUID = .init() - - /// Whether the action has caused an update. - public var update: Bool { boolean } - - /// Initialize a signal. - public init() { } - - /// Activate a signal. - public func signal() { - boolean = true - boolean = false - } - -} diff --git a/Sources/Adwaita/Model/Data Flow/State.swift b/Sources/Adwaita/Model/Data Flow/State.swift deleted file mode 100644 index 842b9b2..0000000 --- a/Sources/Adwaita/Model/Data Flow/State.swift +++ /dev/null @@ -1,251 +0,0 @@ -// -// State.swift -// Adwaita -// -// Created by david-swift on 06.08.23. -// - -import CAdw -import Foundation - -/// A property wrapper for properties in a view that should be stored throughout view updates. -@propertyWrapper -public struct State: StateProtocol { - - /// Access the stored value. This updates the views when being changed. - public var wrappedValue: Value { - get { - rawValue - } - nonmutating set { - rawValue = newValue - content.storage.update = true - Self.updateViews(force: forceUpdates) - } - } - - /// Get the value as a binding using the `$` prefix. - public var projectedValue: Binding { - .init { - wrappedValue - } set: { newValue in - self.wrappedValue = newValue - } - } - - // swiftlint:disable force_cast - /// Get and set the value without updating the views. - public var rawValue: Value { - get { - if let readValue, !content.fetched { - let newValue = readValue() - content.storage.value = newValue - content.fetched = true - return newValue - } - return content.storage.value as! Value - } - nonmutating set { - content.storage.value = newValue - writeValue?() - } - } - // swiftlint:enable force_cast - - /// The stored value. - public let content: State.Content - - /// Whether to force update the views when the value changes. - public var forceUpdates: Bool - - /// The function for updating the value in the settings file. - private var writeValue: (() -> Void)? - /// The function for reading the value in the settings file. - var readValue: (() -> Value)? - - /// The value with an erased type. - public var value: Any { - get { - wrappedValue - } - nonmutating set { - if let newValue = newValue as? Value { - content.storage.value = newValue - } - } - } - - /// Initialize a property representing a state in the view. - /// - Parameters: - /// - wrappedValue: The wrapped value. - /// - forceUpdates: Whether to force update all available views when the property gets modified. - public init(wrappedValue: Value, forceUpdates: Bool = false) { - content = .init(initialValue: wrappedValue) - self.forceUpdates = forceUpdates - } - - /// A class storing the state's content. - public class Content { - - /// The storage. - public var storage: Storage { - get { - if let internalStorage { - return internalStorage - } - let storage = Storage(value: initialValue) - internalStorage = storage - return storage - } - set { - internalStorage = newValue - } - } - /// The internal storage. - var internalStorage: Storage? - /// The initial value. - private var initialValue: Value - /// Whether the value has already been fetched from the file stored on the system. - var fetched = false - - /// Initialize the content. - /// - Parameter storage: The storage. - @available(*, deprecated, message: "Use a safer initializer instead") - public init(storage: Storage) { - // swiftlint:disable force_cast - let initialValue = storage.value as! Value - // swiftlint:enable force_cast - self.initialValue = initialValue - self.storage = storage - } - - /// Initialize the content. - /// - Parameter initialValue: The initial value. - public init(initialValue: Value) { - self.initialValue = initialValue - } - - } - - /// A class storing the value. - public class Storage { - - /// The stored value. - public var value: Any - /// The storage key. - public var key: String? - /// The folder path. - public var folder: String? - /// Whether to update the affected views. - public var update = false - - /// Initialize the storage. - /// - Parameters: - /// - value: The value. - public init(value: Any) { - self.value = value - } - - } - - /// Update all of the views. - /// - Parameter force: Whether to force all views to update. - public static func updateViews(force: Bool = false) { - UpdateManager.updateViews(force: force) - } - - /// The directory used for storing user data. - /// - Returns: The URL. - public static func userDataDir() -> URL { - .init(fileURLWithPath: .init(cString: g_get_user_data_dir())) - } - - /// Copy a text to the clipboard. - /// - Parameter text: The text. - public static func copy(_ text: String) { - let clipboard = gdk_display_get_clipboard(gdk_display_get_default()) - gdk_clipboard_set_text(clipboard, text) - } - - /// Get the settings directory path. - /// - Returns: The path. - private func dirPath() -> URL? { - guard let folder = content.storage.folder ?? GTUIApp.appID else { - return nil - } - return Self.userDataDir() - .appendingPathComponent(folder, isDirectory: true) - } - - /// Get the settings file path. - /// - Returns: The path. - private func filePath() -> URL? { - dirPath()?.appendingPathComponent("\(content.storage.key ?? "temporary").json") - } - -} - -extension State where Value: Codable { - - /// Initialize a property representing a state in the view. - /// - Parameters: - /// - wrappedValue: The wrapped value. - /// - key: The unique storage key of the property. - /// - folder: The path to the folder containing the JSON file. - /// - forceUpdates: Whether to force update all available views when the property gets modified. - /// - /// The folder path will be appended to the XDG data home directory. - public init(wrappedValue: Value, _ key: String, folder: String? = nil, forceUpdates: Bool = false) { - content = .init(initialValue: wrappedValue) - self.forceUpdates = forceUpdates - content.storage.key = key - content.storage.folder = folder - let value = self - self.readValue = { - value.checkFile() - return value.readActualValue() ?? wrappedValue - } - self.writeValue = writeCodableValue - } - - /// Check whether the settings file exists, and, if not, create it. - private func checkFile() { - let fileManager = FileManager.default - guard let dirPath = dirPath(), let filePath = filePath() else { - print("Stored state value accessed before initializing app id.") - return - } - if !fileManager.fileExists(atPath: dirPath.path) { - try? fileManager.createDirectory( - at: .init(fileURLWithPath: dirPath.path), - withIntermediateDirectories: true, - attributes: nil - ) - } - if !fileManager.fileExists(atPath: filePath.path) { - fileManager.createFile(atPath: filePath.path, contents: .init(), attributes: nil) - } - } - - /// Update the local value with the value from the file. - private func readActualValue() -> Value? { - if let filePath = filePath(), - let data = try? Data(contentsOf: filePath), - let value = try? JSONDecoder().decode(Value.self, from: data) { - return value - } - return nil - } - - /// Update the value on the file with the local value. - private func writeCodableValue() { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - let data = try? encoder.encode(rawValue) - guard let filePath = filePath() else { - return - } - try? data?.write(to: filePath) - } - -} diff --git a/Sources/Adwaita/Model/Data Flow/StateProtocol.swift b/Sources/Adwaita/Model/Data Flow/StateProtocol.swift deleted file mode 100644 index f407c6c..0000000 --- a/Sources/Adwaita/Model/Data Flow/StateProtocol.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// StateProtocol.swift -// Adwaita -// -// Created by david-swift on 09.09.23. -// - -/// An interface for accessing `State` without specifying the generic type. -public protocol StateProtocol { - - /// The class storing the value. - var content: State.Content { get } - -} diff --git a/Sources/Adwaita/Model/Data Flow/UpdateManager.swift b/Sources/Adwaita/Model/Data Flow/UpdateManager.swift deleted file mode 100644 index 5d4e576..0000000 --- a/Sources/Adwaita/Model/Data Flow/UpdateManager.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// UpdateManager.swift -// Adwaita -// -// Created by david-swift on 17.02.23. -// - -/// This type manages view updates. -enum UpdateManager { - - /// The class storing the value. - static var blockUpdates = false - - /// Update all of the views. - /// - Parameter force: Whether to force all views to update. - static func updateViews(force: Bool = false) { - if !blockUpdates { - for handler in GTUIApp.updateHandlers { - handler(force) - } - } - } - -} diff --git a/Sources/Adwaita/Model/Extensions/Array.swift b/Sources/Adwaita/Model/Extensions/Array.swift index e819577..3dceff3 100644 --- a/Sources/Adwaita/Model/Extensions/Array.swift +++ b/Sources/Adwaita/Model/Extensions/Array.swift @@ -5,57 +5,6 @@ // Created by david-swift on 06.08.23. // -import Foundation - -extension Array: View where Element == View { - - /// The array's view body is the array itself. - public var view: Body { self } - - /// Get a widget from a collection of views. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: A widget. - public func widget(modifiers: [(View) -> View]) -> Widget { - if count == 1, let widget = self[safe: 0]?.widget(modifiers: modifiers) { - return widget - } else { - var modified = self - for (index, view) in modified.enumerated() { - for modifier in modifiers { - modified[safe: index] = modifier(view) - } - } - return VStack { modified } - } - } - - /// Update a collection of views with a collection of view storages. - /// - Parameters: - /// - storage: The collection of view storages. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - public func update(_ storage: [ViewStorage], modifiers: [(View) -> View], updateProperties: Bool) { - for (index, element) in enumerated() { - if let storage = storage[safe: index] { - element - .widget(modifiers: modifiers) - .updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties) - } - } - } - -} - -extension Array where Element == WindowSceneGroup { - - /// Get the content of an array of window scene groups. - /// - Returns: The array of windows. - public func windows() -> [WindowScene] { - flatMap { $0.windows() } - } - -} - extension Array where Element == String { /// Get the C version of the array. @@ -77,47 +26,3 @@ extension Array where Element == String { } } - -extension Array { - - /// Accesses the element at the specified position safely. - /// - Parameters: - /// - index: The position of the element to access. - /// - /// Access and set elements the safe way: - /// ```swift - /// var array = ["Hello", "World"] - /// print(array[safe: 2] ?? "Out of range") - /// ``` - public subscript(safe index: Int?) -> Element? { - get { - if let index, indices.contains(index) { - return self[index] - } - return nil - } - set { - if let index, let value = newValue, indices.contains(index) { - self[index] = value - } - } - } - -} - -extension Array where Element: Identifiable { - - /// Accesses the element with a certain id safely. - /// - Parameters: - /// - id: The child's id. - /// - /// Access and set elements the safe way: - /// ```swift - /// var array = ["Hello", "World"] - /// print(array[safe: 2] ?? "Out of range") - /// ``` - public subscript(id id: Element.ID) -> Element? { - self[safe: firstIndex { $0.id == id }] - } - -} diff --git a/Sources/Adwaita/Model/Extensions/String.swift b/Sources/Adwaita/Model/Extensions/String.swift index 1f76efe..2d135d8 100644 --- a/Sources/Adwaita/Model/Extensions/String.swift +++ b/Sources/Adwaita/Model/Extensions/String.swift @@ -7,11 +7,9 @@ extension String { - /// A label for main content in a view storage. - static var mainContent: Self { "main" } - /// A label for the transition data in a GTUI widget's fields. + /// A label for the transition data in a widget's fields. static var transition: Self { "transition" } - /// A label for the navigation label in a GTUI widget's fields. + /// A label for the navigation label in a widget's fields. static var navigationLabel: Self { "navigation-label" } /// Add the Ctrl key to a shortcut. diff --git a/Sources/Adwaita/Model/Extensions/ViewStorage.swift b/Sources/Adwaita/Model/Extensions/ViewStorage.swift new file mode 100644 index 0000000..6df4a74 --- /dev/null +++ b/Sources/Adwaita/Model/Extensions/ViewStorage.swift @@ -0,0 +1,24 @@ +// +// ViewStorage.swift +// Adwaita +// +// Created by david-swift on 01.08.24. +// + +extension ViewStorage { + + /// Modify the view. + /// - Parameter modify: The modification function. + public func modify(_ modify: (OpaquePointer?) -> Void) { + modify(opaquePointer) + } + + /// Convert the pointer to a pointer of a certain type and modify the view. + /// - Parameters: + /// - type: The pointer's type. + /// - modify: The modification function. + public func modify(_ type: T.Type, _ modify: (UnsafeMutablePointer?) -> Void) { + modify(opaquePointer?.cast()) + } + +} diff --git a/Sources/Adwaita/Model/Data Flow/Idle.swift b/Sources/Adwaita/Model/Idle.swift similarity index 99% rename from Sources/Adwaita/Model/Data Flow/Idle.swift rename to Sources/Adwaita/Model/Idle.swift index e922c23..c28d98d 100644 --- a/Sources/Adwaita/Model/Data Flow/Idle.swift +++ b/Sources/Adwaita/Model/Idle.swift @@ -2,7 +2,7 @@ // Idle.swift // Adwaita // -// Created by david-swift on 02.05.24. +// Created by david-swift on 12.08.24. // import CAdw diff --git a/Sources/Adwaita/Model/Signals/SignalData.swift b/Sources/Adwaita/Model/Signals/SignalData.swift new file mode 100644 index 0000000..03c359a --- /dev/null +++ b/Sources/Adwaita/Model/Signals/SignalData.swift @@ -0,0 +1,101 @@ +// +// SignalData.swift +// Adwaita +// +// Created by david-swift on 31.07.24. +// + +import CAdw + +/// Data to pass to signal handlers. +public class SignalData { + + /// The closure. + public var closure: ([Any?]) -> Void + + /// The closure as a C handler. + var handler: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void { + { _, data in + let data = unsafeBitCast(data, to: SignalData.self) + data.closure([]) + } + } + + /// The closure as a C handler with three parameters. + var threeParamsHandler: @convention(c) ( + UnsafeMutableRawPointer, + UnsafeRawPointer?, + UnsafeMutableRawPointer + ) -> Void { + { _, arg1, data in + let data = unsafeBitCast(data, to: SignalData.self) + data.closure([arg1]) + } + } + + /// The closure as a C handler with four parameters. + var fourParamsHandler: @convention(c) ( + UnsafeMutableRawPointer, + UnsafeRawPointer?, + UnsafeRawPointer?, + UnsafeMutableRawPointer + ) -> Void { + { _, arg1, arg2, data in + let data = unsafeBitCast(data, to: SignalData.self) + data.closure([arg1, arg2]) + } + } + + /// The closure as a C handler with five parameters. + var fiveParamsHandler: @convention(c) ( + UnsafeMutableRawPointer, + UnsafeRawPointer?, + Double, + Double, + UnsafeMutableRawPointer + ) -> Void { + { _, arg1, arg2, arg3, data in + let data = unsafeBitCast(data, to: SignalData.self) + data.closure([arg1, arg2, arg3]) + } + } + + /// Initialize the signal data. + /// - Parameter closure: The signal's closure. + public convenience init(closure: @escaping () -> Void) { + self.init { _ in closure() } + } + + /// Initialize the signal data. + /// - Parameter closure: The signal's closure. + public init(closure: @escaping ([Any]) -> Void) { + self.closure = closure + } + + /// Connect the signal data to a signal. + /// - Parameters: + /// - pointer: The pointer to the object which holds the signal. + /// - signal: The signal's name. + /// - argCount: The number of arguments. + public func connect(pointer: UnsafeMutableRawPointer?, signal: String, argCount: Int = 0) { + let callback: GCallback + if argCount >= 3 { + callback = unsafeBitCast(fiveParamsHandler, to: GCallback.self) + } else if argCount == 2 { + callback = unsafeBitCast(fourParamsHandler, to: GCallback.self) + } else if argCount == 1 { + callback = unsafeBitCast(threeParamsHandler, to: GCallback.self) + } else { + callback = unsafeBitCast(handler, to: GCallback.self) + } + g_signal_connect_data( + pointer, + signal, + callback, + Unmanaged.passUnretained(self).toOpaque().cast(), + nil, + G_CONNECT_AFTER + ) + } + +} diff --git a/Sources/Adwaita/Model/Storage.swift b/Sources/Adwaita/Model/Storage.swift new file mode 100644 index 0000000..ad1c125 --- /dev/null +++ b/Sources/Adwaita/Model/Storage.swift @@ -0,0 +1,82 @@ +// +// WindowView.swift +// Adwaita +// +// Created by david-swift on 06.08.24. +// + +/// A storage type is a view storage or a scene storage. +public protocol Storage: AnyObject { + + /// The pointer. + var opaquePointer: OpaquePointer? { get } + /// Additional fields. + var fields: [String: Any] { get set } + +} + +extension Storage { + + /// Connect a handler to the observer of a property. + /// - Parameters: + /// - name: The property's name. + /// - id: The handlers id to separate form others connecting to the signal. + /// - pointer: A custom pointer instead of the stored one. + /// - handler: The signal's handler. + public func notify( + name: String, + id: String = "", + pointer: OpaquePointer? = nil, + handler: @escaping () -> Void + ) { + let name = "notify::" + name + connectSignal(name: name, id: id, argCount: 1, pointer: pointer, handler: handler) + } + + /// Connect a handler to a signal. + /// - Parameters: + /// - name: The signal's name. + /// - id: The handlers id to separate form others connecting to the signal. + /// - connectFlags: The GConnectFlags. + /// - argCount: The number of additional arguments (without the first and the last one). + /// - pointer: A custom pointer instead of the stored one. + /// - handler: The signal's handler. + public func connectSignal( + name: String, + id: String = "", + argCount: Int = 0, + pointer: OpaquePointer? = nil, + handler: @escaping () -> Void + ) { + connectSignal(name: name, id: id, argCount: argCount, pointer: pointer) { _ in + handler() + } + } + + /// Connect a handler to a signal. + /// - Parameters: + /// - name: The signal's name. + /// - id: The handlers id to separate form others connecting to the signal. + /// - argCount: The number of additional arguments (without the first and the last one). + /// - pointer: A custom pointer instead of the stored one. + /// - handler: The signal's handler. + public func connectSignal( + name: String, + id: String = "", + argCount: Int = 0, + pointer: OpaquePointer? = nil, + handler: @escaping ([Any]) -> Void + ) { + if let data = fields[name + id] as? SignalData { + data.closure = handler + } else { + let data = SignalData(closure: handler) + fields[name + id] = data + data.connect(pointer: (pointer ?? opaquePointer)?.cast(), signal: name, argCount: argCount) + } + } + +} + +extension ViewStorage: Storage { } +extension SceneStorage: Storage { } diff --git a/Sources/Adwaita/Model/User Interface/App/App.swift b/Sources/Adwaita/Model/User Interface/App/App.swift deleted file mode 100644 index 208d503..0000000 --- a/Sources/Adwaita/Model/User Interface/App/App.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// App.swift -// Adwaita -// -// Created by david-swift on 05.08.23. -// - -/// A structure conforming to `App` is the entry point of your app. -/// -/// ```swift -/// @main -/// struct Test: App { -/// -/// let id = "io.github.AparokshaUI.TestApp" -/// var app: GTUIApp! -/// -/// var scene: Scene { -/// WindowScene() -/// } -/// -/// } -/// ``` -/// -public protocol App { - - /// The app's application ID. - var id: String { get } - - /// The app's windows. - @SceneBuilder var scene: Scene { get } - // swiftlint:disable implicitly_unwrapped_optional - /// The app. - var app: GTUIApp! { get set } - // swiftlint:enable implicitly_unwrapped_optional - - /// An app has to have an `init()` initializer. - init() - -} - -extension App { - - /// The application's entry point. - public static func main() { - let app = setupApp() - app.run() - } - - /// Initialize and get the GTUI app. - /// - /// To run the app, call the ``GTUIApp/run(automaticSetup:manualSetup:)`` function. - public static func setupApp() -> GTUIApp { - var appInstance = self.init() - appInstance.app = GTUIApp(appInstance.id) { appInstance } - GTUIApp.updateHandlers.append { force in - var removeIndices: [Int] = [] - for (index, window) in appInstance.app.sceneStorage.enumerated() { - if window.destroy { - removeIndices.insert(index, at: 0) - } else if let scene = appInstance.scene.windows().first(where: { $0.id == window.id }) { - scene.update(window, app: appInstance.app, force: force) - } - } - for index in removeIndices { - appInstance.app.sceneStorage.remove(at: index) - } - } - GTUIApp.appID = appInstance.id - return appInstance.app - } - -} diff --git a/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift b/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift deleted file mode 100644 index 1007d1b..0000000 --- a/Sources/Adwaita/Model/User Interface/App/GTUIApp.swift +++ /dev/null @@ -1,160 +0,0 @@ -// -// GTUIApp.swift -// Adwaita -// -// Created by david-swift on 05.08.23. -// - -import CAdw - -/// The GTUI application. -public class GTUIApp { - - /// The handlers which are called when a state changes. - static var updateHandlers: [(Bool) -> Void] = [] - /// The app's id for the file name for storing the data. - static var appID: String? - /// The pointer to the application. - public var pointer: UnsafeMutablePointer? - /// Fields for additional information. - public var fields: [String: Any] = [:] - - /// The app's content. - var body: () -> App - /// The scenes that are displayed. - var sceneStorage: [WindowStorage] = [] - /// A string signaling that the parent should not be overwritten. - let overwriteParentID = "overwrite-parent" - - /// Initialize the GTUI application. - /// - Parameters: - /// - id: The application id. - /// - body: The application's content. - init(_ id: String, body: @escaping () -> App) { - self.body = body - self.pointer = adw_application_new(id, G_APPLICATION_DEFAULT_FLAGS)?.cast() - } - - /// The entry point of the application. - public func onActivate() { - let body = body() - for windowScene in body.scene.windows() { - for _ in 0.. Void = { }) { - let data = ViewStorage.SignalData { - if automaticSetup { - self.onActivate() - } - manualSetup() - } - fields["run"] = data - g_signal_connect_data( - pointer?.cast(), - "activate", - unsafeBitCast(data.handler, to: GCallback.self), - Unmanaged.passUnretained(data).toOpaque().cast(), - nil, - G_CONNECT_AFTER - ) - g_application_run(pointer?.cast(), 0, nil) - } - - /// Add a keyboard shortcut to the application. - /// - Parameters: - /// - shortcut: The keyboard shortcut. - /// - id: The action's id. - /// - window: Optionally an application window. - /// - handler: The action's handler. - public func addKeyboardShortcut( - _ shortcut: String, - id: String, - window: GTUIApplicationWindow? = nil, - handler: @escaping () -> Void - ) { - let action = g_simple_action_new(id, nil) - let data = ViewStorage.SignalData(closure: handler) - g_signal_connect_data( - action?.cast(), - "activate", - unsafeBitCast(data.threeParamsHandler, to: GCallback.self), - Unmanaged.passUnretained(data).toOpaque().cast(), - nil, - G_CONNECT_AFTER - ) - if let window { - g_action_map_add_action(.init(window.pointer), action) - window.fields[id] = data - } else { - g_action_map_add_action(.init(pointer), action) - fields[id] = data - } - gtk_application_set_accels_for_action(pointer, (window == nil ? "app." : "win.") + id, [shortcut].cArray) - } - - /// Remove a keyboard shortcut from the application. - /// - Parameters: - /// - id: The keyboard shortcut's id. - /// - window: Optionally an application window. - public func removeKeyboardShortcut( - id: String, - window: GTUIApplicationWindow? = nil - ) { - if let window { - g_action_map_remove_action(.init(window.pointer), id) - window.fields.removeValue(forKey: id) - } else { - g_action_map_remove_action(.init(pointer), id) - fields.removeValue(forKey: id) - } - } - - /// Focus the window with a certain id. Create the window if it doesn't already exist. - /// - Parameters: - /// - id: The window's id. - public func showWindow(_ id: String) { - sceneStorage.last { $0.id == id && !$0.destroy }?.window.show() ?? addWindow(id) - } - - /// Add a new window with the content of the window with a certain id. - /// - Parameters: - /// - id: The window's id. - /// - parent: The parent window. - public func addWindow(_ id: String, parent: GTUIWindow? = nil) { - State.updateViews() - if let window = body().scene.windows().last(where: { $0.id == id }) { - let window = window.createWindow(app: self) - sceneStorage.append(window) - if let parent { - window.window.setParentWindow(parent) - window.window.fields[overwriteParentID] = true - } - setParentWindows() - showWindow(id) - } - } - - /// Set the parents of every window having a parent window. - func setParentWindows() { - for window in sceneStorage where !(window.window.fields[overwriteParentID] as? Bool ?? false) { - if let parent = sceneStorage.first(where: { $0.id == window.parentID }) { - window.window.setParentWindow(parent.window) - } - } - } - - /// Terminate the application. - public func quit() { - g_application_quit(pointer?.cast()) - } - -} diff --git a/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift b/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift deleted file mode 100644 index 06e44ee..0000000 --- a/Sources/Adwaita/Model/User Interface/Menu/MenuItem.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// MenuItem.swift -// Adwaita -// -// Created by david-swift on 22.10.23. -// - -import CAdw - -/// A structure representing the content for a certain menu item type. -public protocol MenuItem: MenuItemGroup { - - /// Add the menu item to a certain menu. - /// - Parameters: - /// - menu: The menu. - /// - app: The application containing the menu. - /// - window: The application window containing the menu. - func addMenuItem(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) - -} - -extension MenuItem { - - /// The menu item's content is itself. - @MenuBuilder public var content: MenuContent { self } - -} diff --git a/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift b/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift deleted file mode 100644 index 8125e1d..0000000 --- a/Sources/Adwaita/Model/User Interface/Menu/MenuItemGroup.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// MenuItemGroup.swift -// Adwaita -// -// Created by david-swift on 22.10.23. -// - -import CAdw - -/// A structure conforming to `MenuItemGroup` can be added to the content accepting a menu. -public protocol MenuItemGroup { - - /// The menu's content. - @MenuBuilder var content: MenuContent { get } - -} - -extension MenuItemGroup { - - /// Add the menu items described by the group to a menu. - /// - Parameter menu: The menu. - func addMenuItems(menu: OpaquePointer?, app: GTUIApp, window: GTUIApplicationWindow?) { - for element in content { - if let item = element as? MenuItem { - item.addMenuItem(menu: menu, app: app, window: window) - } else { - element.addMenuItems(menu: menu, app: app, window: window) - } - } - } - -} - -/// `MenuContent` is an array of menu item groups. -public typealias MenuContent = [MenuItemGroup] -/// A builder for the `MenuContent` -public typealias MenuBuilder = ArrayBuilder diff --git a/Sources/Adwaita/Model/User Interface/View/View.swift b/Sources/Adwaita/Model/User Interface/View/View.swift deleted file mode 100644 index cf117f4..0000000 --- a/Sources/Adwaita/Model/User Interface/View/View.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// View.swift -// Adwaita -// -// Created by david-swift on 05.08.23. -// - -/// A structure conforming to `View` is referred to as a view. -/// It can be part of a body. -/// -/// ```swift -/// struct Test: View { -/// -/// var view: Body { -/// AnotherView() -/// } -/// -/// } -/// ``` -/// -public protocol View { - - /// The view's content. - @ViewBuilder var view: Body { get } - -} - -extension View { - - /// Wrap the view into a widget. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The widget. - public func widget(modifiers: [(View) -> View]) -> Widget { - let modified = getModified(modifiers: modifiers) - if let peer = modified as? Widget { - return peer - } else { - return StateWrapper(content: { view }, state: getState()) - } - } - - /// Update a storage to a view. - /// - Parameters: - /// - storage: The storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - public func updateStorage(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - let modified = getModified(modifiers: modifiers) - if let widget = modified as? Widget { - widget.update(storage, modifiers: modifiers, updateProperties: updateProperties) - } else { - StateWrapper(content: { view }, state: getState()) - .update(storage, modifiers: modifiers, updateProperties: updateProperties) - } - } - - func getState() -> [String: StateProtocol] { - var state: [String: StateProtocol] = [:] - for property in Mirror(reflecting: self).children { - if let label = property.label, let value = property.value as? StateProtocol { - state[label] = value - } - } - return state - } - - /// Get a storage. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The storage. - public func storage(modifiers: [(View) -> View]) -> ViewStorage { - widget(modifiers: modifiers).container(modifiers: modifiers) - } - - func getModified(modifiers: [(View) -> View]) -> View { - var modified: View = self - for modifier in modifiers { - modified = modifier(modified) - } - return modified - } - -} - -/// `Body` is an array of views. -public typealias Body = [View] diff --git a/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift b/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift deleted file mode 100644 index b1f570d..0000000 --- a/Sources/Adwaita/Model/User Interface/View/ViewBuilder.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// ViewBuilder.swift -// Adwaita -// -// Created by david-swift on 05.08.23. -// - -import Foundation - -/// The ``ViewBuilder`` is a result builder for views. -@resultBuilder -public enum ViewBuilder { - - /// A component used in the ``ArrayBuilder``. - public enum Component { - - /// A view as a component. - case element(_: View) - /// An array of components as a component. - case components(_: [Self]) - - } - - /// Build combined results from statement blocks. - /// - Parameter components: The components. - /// - Returns: The components in a component. - public static func buildBlock(_ elements: Component...) -> Component { - .components(elements) - } - - /// Translate an element into a ``ViewBuilder.Component``. - /// - Parameter element: The element to translate. - /// - Returns: A component created from the element. - public static func buildExpression(_ element: View) -> Component { - .element(element) - } - - /// Translate an array of elements into a ``ViewBuilder.Component``. - /// - Parameter elements: The elements to translate. - /// - Returns: A component created from the element. - public static func buildExpression(_ elements: [View]) -> Component { - var components: [Component] = [] - for element in elements { - components.append(.element(element)) - } - return .components(components) - } - - /// Fetch a component. - /// - Parameter component: A component. - /// - Returns: The component. - public static func buildExpression(_ component: Component) -> Component { - component - } - - /// Enables support for `if` statements without an `else`. - /// - Parameter component: An optional component. - /// - Returns: A nonoptional component. - public static func buildOptional(_ component: Component?) -> Component { - .element( - Bin() - .child { - if let component { - buildFinalResult(component) - } else { - [] - } - } - .visible(component != nil) - ) - } - - /// Enables support for `if`-`else` and `switch` statements. - /// - Parameter component: A component. - /// - Returns: The component. - public static func buildEither(first component: Component) -> Component { - .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(ViewStack(id: false) { _ in buildFinalResult(component) }) - } - - /// Convert a component to an array of elements. - /// - Parameter component: The component to convert. - /// - Returns: The generated array of elements. - public static func buildFinalResult(_ component: Component) -> [View] { - switch component { - case let .element(element): - return [element] - case let .components(components): - return components.flatMap { buildFinalResult($0) } - } - } - -} diff --git a/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift b/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift deleted file mode 100644 index 5b3aeb7..0000000 --- a/Sources/Adwaita/Model/User Interface/View/ViewStorage.swift +++ /dev/null @@ -1,197 +0,0 @@ -// -// ViewStorage.swift -// Adwaita -// -// Created by david-swift on 31.08.23. -// - -import CAdw - -/// Store a rendered view in a view storage. -public class ViewStorage { - - /// The pointer. - public var pointer: OpaquePointer? - /// The view's content. - public var content: [String: [ViewStorage]] - /// The view's state (used in `StateWrapper`). - public var state: [String: StateProtocol] - /// The signal handlers. - public var handlers: [String: SignalData] = [:] - /// Other properties. - public var fields: [String: Any] = [:] - - /// Initialize a view storage. - /// - Parameters: - /// - pointer: The pointer to the Gtk widget. - /// - content: The view's content. - /// - state: The view's state. - public init( - _ pointer: OpaquePointer?, - content: [String: [ViewStorage]] = [:], - state: [String: StateProtocol] = [:] - ) { - self.pointer = pointer - self.content = content - self.state = state - } - - /// Data to pass to signal handlers. - public class SignalData { - - /// The closure. - public var closure: ([Any]) -> Void - - /// The closure as a C handler. - var handler: @convention(c) (UnsafeMutableRawPointer, UnsafeMutableRawPointer) -> Void { - { _, data in - let data = unsafeBitCast(data, to: SignalData.self) - data.closure([]) - } - } - - /// The closure as a C handler with three parameters. - var threeParamsHandler: @convention(c) ( - UnsafeMutableRawPointer, - UnsafeRawPointer?, - UnsafeMutableRawPointer - ) -> Void { - { _, arg1, data in - let data = unsafeBitCast(data, to: SignalData.self) - data.closure([arg1]) - } - } - - /// The closure as a C handler with four parameters. - var fourParamsHandler: @convention(c) ( - UnsafeMutableRawPointer, - UnsafeRawPointer?, - UnsafeRawPointer?, - UnsafeMutableRawPointer - ) -> Void { - { _, arg1, arg2, data in - let data = unsafeBitCast(data, to: SignalData.self) - data.closure([arg1, arg2]) - } - } - - /// The closure as a C handler with five parameters. - var fiveParamsHandler: @convention(c) ( - UnsafeMutableRawPointer, - UnsafeRawPointer?, - Double, - Double, - UnsafeMutableRawPointer - ) -> Void { - { _, arg1, arg2, arg3, data in - let data = unsafeBitCast(data, to: SignalData.self) - data.closure([arg1, arg2, arg3]) - } - } - - /// Initialize the signal data. - /// - Parameter closure: The signal's closure. - public convenience init(closure: @escaping () -> Void) { - self.init { _ in closure() } - } - - /// Initialize the signal data. - /// - Parameter closure: The signal's closure. - public init(closure: @escaping ([Any]) -> Void) { - self.closure = closure - } - - } - - /// Connect a handler to the observer of a property. - /// - Parameters: - /// - name: The property's name. - /// - id: The handlers id to separate form others connecting to the signal. - /// - connectFlags: The GConnectFlags. - /// - handler: The signal's handler. - public func notify( - name: String, - id: String = "", - connectFlags: GConnectFlags = G_CONNECT_AFTER, - handler: @escaping () -> Void - ) { - let name = "notify::" + name - connectSignal(name: name, id: id, connectFlags: connectFlags, argCount: 1, handler: handler) - } - - /// Connect a handler to a signal. - /// - Parameters: - /// - name: The signal's name. - /// - id: The handlers id to separate form others connecting to the signal. - /// - connectFlags: The GConnectFlags. - /// - argCount: The number of additional arguments (without the first and the last one). - /// - handler: The signal's handler. - public func connectSignal( - name: String, - id: String = "", - connectFlags: GConnectFlags = G_CONNECT_AFTER, - argCount: Int = 0, - handler: @escaping () -> Void - ) { - connectSignal(name: name, id: id, connectFlags: connectFlags, argCount: argCount) { _ in - handler() - } - } - - /// Connect a handler to a signal. - /// - Parameters: - /// - name: The signal's name. - /// - id: The handlers id to separate form others connecting to the signal. - /// - connectFlags: The GConnectFlags. - /// - argCount: The number of additional arguments (without the first and the last one). - /// - handler: The signal's handler. - public func connectSignal( - name: String, - id: String = "", - connectFlags: GConnectFlags = G_CONNECT_AFTER, - argCount: Int = 0, - handler: @escaping ([Any]) -> Void - ) { - if let data = handlers[name + id] { - data.closure = handler - } else { - let data = SignalData(closure: handler) - handlers[name + id] = data - let callback: GCallback - let three = 3 - let two = 2 - if argCount >= three { - callback = unsafeBitCast(data.fiveParamsHandler, to: GCallback.self) - } else if argCount == two { - callback = unsafeBitCast(data.fourParamsHandler, to: GCallback.self) - } else if argCount == 1 { - callback = unsafeBitCast(data.threeParamsHandler, to: GCallback.self) - } else { - callback = unsafeBitCast(data.handler, to: GCallback.self) - } - g_signal_connect_data( - pointer?.cast(), - name, - callback, - Unmanaged.passUnretained(data).toOpaque().cast(), - nil, - connectFlags - ) - } - } - - /// Modify the view. - /// - Parameter modify: The modification function. - public func modify(_ modify: (OpaquePointer?) -> Void) { - modify(pointer) - } - - /// Convert the pointer to a pointer of a certain type and modify the view. - /// - Parameters: - /// - type: The pointer's type. - /// - modify: The modification function. - public func modify(_ type: T.Type, _ modify: (UnsafeMutablePointer?) -> Void) { - modify(pointer?.cast()) - } - -} diff --git a/Sources/Adwaita/Model/User Interface/View/Widget.swift b/Sources/Adwaita/Model/User Interface/View/Widget.swift deleted file mode 100644 index a631658..0000000 --- a/Sources/Adwaita/Model/User Interface/View/Widget.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// Widget.swift -// Adwaita -// -// Created by david-swift on 16.08.23. -// - -/// A widget is a view that know about its GTUI widget. -public protocol Widget: View { - - /// The view storage. - /// - Parameter modifiers: Modify views before being updated. - func container(modifiers: [(View) -> View]) -> ViewStorage - /// Update the stored content. - /// - Parameters: - /// - storage: The storage to update. - /// - modifiers: Modify views before being updated - /// - updateProperties: Whether to update the view's properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) - -} - -extension Widget { - - /// A widget's view is empty. - public var view: Body { [] } - -} diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift deleted file mode 100644 index 1a223c1..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/GTUIAboutWindow.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// GTUIAboutWindow.swift -// Adwaita -// -// Created by david-swift on 21.01.24. -// - -import CAdw - -/// A GTUI about window. -public class GTUIAboutWindow: GTUIWindow { - - /// Initialize an about window using the AppStream metadata. - /// - Parameter filePath: The path. - public init(filePath: String? = nil) { - super.init(fields: [:]) - if let filePath { - #if os(Windows) - pointer = adw_about_window_new()?.cast() - #else - pointer = adw_about_window_new_from_appdata(filePath, nil)?.cast() - #endif - } else { - pointer = adw_about_window_new()?.cast() - } - } - - /// Set the general data. - /// - Parameters: - /// - title: The app name. - /// - icon: The app icon. - /// - developer: The app's developer. - /// - version: The app's version. - public func generalData(title: String, icon: Icon, developer: String, version: String) { - adw_about_window_set_application_name(.init(pointer), title) - adw_about_window_set_application_icon(.init(pointer), icon.string) - adw_about_window_set_developer_name(.init(pointer), developer) - adw_about_window_set_version(.init(pointer), version) - } - - /// Set the website. - /// - Parameter url: The website. - public func website(url: String) { - adw_about_window_set_website(.init(pointer), url) - } - - /// Set the URL for issues. - /// - Parameter issues: The issues website. - public func issues(url: String) { - adw_about_window_set_issue_url(.init(pointer), url) - } - -} diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift deleted file mode 100644 index 0ce7222..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/GTUIApplicationWindow.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// GTUIApplicationWindow.swift -// Adwaita -// -// Created by david-swift on 19.10.23. -// - -import CAdw - -/// A GTUI application window. -public class GTUIApplicationWindow: GTUIWindow { - - /// The window's parent app. - public var app: GTUIApp - - /// Initialize the application window. - /// - Parameter app: The application. - public init(app: GTUIApp) { - self.app = app - super.init(fields: [:]) - pointer = adw_application_window_new(app.pointer)?.cast() - } - - /// Add a keyboard shortcut. - /// - Parameters: - /// - shortcut: The keyboard shortcut. - /// - id: The action's id. - /// - handler: The action's handler. - public func addKeyboardShortcut(_ shortcut: String, id: String, handler: @escaping () -> Void) { - app.addKeyboardShortcut(shortcut, id: id, window: self, handler: handler) - } - - /// Remove a keyboard shortcut. - /// - Parameters: - /// - id: The action's id. - public func removeKeyboardShortcut(id: String) { - app.removeKeyboardShortcut(id: id, window: self) - } - - /// Set the window's child. - /// - Parameter child: The child. - override public func setChild(_ child: OpaquePointer?) { - adw_application_window_set_content(pointer?.cast(), child?.cast()) - } - -} diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift deleted file mode 100644 index b17a813..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/GTUIFileDialog.swift +++ /dev/null @@ -1,138 +0,0 @@ -// -// GTUIFileDialog.swift -// Adwaita -// -// Created by david-swift on 09.12.23. -// - -import CAdw -import Foundation - -/// A GTUI file dialog window. -public class GTUIFileDialog: WindowType { - - /// The file dialog's pointer. - public var pointer: OpaquePointer? - /// Fields for additional data. - public var fields: [String: Any] = [:] - /// A link to the file dialog. - var selfAddr: UInt64 { - unsafeBitCast(self, to: UInt64.self) - } - /// The parent window. - var parent: OpaquePointer? - /// Whether the file dialog is an importer. - var isImporter = false - /// The selected folder in the file dialog. - var folder: URL? - /// A closure triggered on selecting a file in the dialog. - var onResult: (URL) -> Void = { _ in } - /// A closure triggered when the dialog is canceled. - var onCancel: () -> Void = { } - - /// Initialize the window. - public init() { - pointer = gtk_file_dialog_new() - } - - /// Set the window's parent window. - /// - Parameter parent: The parent window. - public func setParentWindow(_ parent: WindowType) { - if let window = parent as? GTUIWindow { - self.parent = .init(window.pointer) - } - } - - /// Set the initial name. - /// - Parameter name: The parent window. - public func setInitialName(_ name: String) { - gtk_file_dialog_set_initial_name(pointer, name) - } - - // swiftlint:disable discouraged_optional_collection - /// Set the allowed file extensions. - /// - Parameters: - /// - extensions: The file extensions. - public func setExtensions(_ extensions: [String]?) { - if let extensions { - let filter = gtk_file_filter_new() - for name in extensions { - gtk_file_filter_add_suffix(filter, name) - } - gtk_file_dialog_set_default_filter(pointer, filter) - } else { - gtk_file_dialog_set_default_filter(pointer, nil) - } - } - // swiftlint:enable discouraged_optional_collection - - /// Display the file dialog. - public func show() { - if let folder { - gtk_file_dialog_set_initial_folder(pointer, g_file_new_for_path(folder.absoluteString)) - } - if isImporter { - gtui_filedialog_open(UInt64(Int(bitPattern: pointer)), selfAddr, UInt64(Int(bitPattern: parent))) - } else { - gtui_filedialog_save(UInt64(Int(bitPattern: pointer)), selfAddr, UInt64(Int(bitPattern: parent))) - } - } - - /// Run this when a file gets opened. - /// - Parameter path: The file path. - func onOpen(_ path: String) { - let url = URL(fileURLWithPath: path) - onResult(url) - } - - /// Run this when a file gets saved. - /// - Parameter path: The file path. - func onSave(_ path: String) { - let url = URL(fileURLWithPath: path) - onResult(url) - } - - /// Run this when the user cancels the action. - func onClose() { - onCancel() - } - -} - -/// Run when a file should be opened. -/// - Parameters: -/// - ptr: The pointer. -/// - file: The path to the file. -/// - userData: The file dialog data. -@_cdecl("filedialog_on_open_cb") -func filedialog_on_open_cb( - ptr: UnsafeMutableRawPointer, - file: UnsafePointer?, - userData: UnsafeMutableRawPointer -) { - let dialog = Unmanaged.fromOpaque(userData).takeUnretainedValue() - if let file { - dialog.onOpen(.init(cString: file)) - } else { - dialog.onClose() - } -} - -/// Run when a file should be saved. -/// - Parameters: -/// - ptr: The pointer. -/// - file: The path to the file. -/// - userData: The file dialog data. -@_cdecl("filedialog_on_save_cb") -func filedialog_on_save_cb( - ptr: UnsafeMutableRawPointer, - file: UnsafePointer?, - userData: UnsafeMutableRawPointer -) { - let dialog = Unmanaged.fromOpaque(userData).takeUnretainedValue() - if let file { - dialog.onSave(.init(cString: file)) - } else { - dialog.onClose() - } -} diff --git a/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift b/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift deleted file mode 100644 index 9ad8715..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/GTUIWindow.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// GTUIWindow.swift -// Adwaita -// -// Created by david-swift on 12.10.23. -// - -import CAdw - -/// A GTUI window. -public class GTUIWindow: WindowType { - - /// The window's pointer. - public var pointer: UnsafeMutablePointer? - /// Fields for additional information. - public var fields: [String: Any] = [:] - - /// Initialize the window. - public init() { - pointer = adw_window_new()?.cast() - } - - /// Initialize the window, but not the pointer. - /// - Parameter fields: The fields. - init(fields: [String: Any]) { - self.fields = fields - } - - /// Set the default window size. - /// - Parameters: - /// - width: The width. - /// - height: The height. - public func setDefaultSize(width: Int?, height: Int?) { - gtk_window_set_default_size(pointer, width?.cInt ?? -1, height?.cInt ?? -1) - } - - /// Set the resizability. - /// - Parameter resizable: Whether the window is resizable. - public func setResizability(_ resizable: Bool) { - gtk_window_set_resizable(pointer, resizable.cBool) - } - - /// Set the deletability. - /// - Parameter deletable: Whether the window is deletable. - public func setDeletability(_ deletable: Bool) { - gtk_window_set_deletable(pointer, deletable.cBool) - } - - /// Set the window title. - /// - Parameter title: The window's title. - public func setTitle(_ title: String) { - gtk_window_set_title(pointer, title) - } - - /// Set the window's child. - /// - Parameter child: The child. - public func setChild(_ child: OpaquePointer?) { - gtk_window_set_child(pointer, child?.cast()) - } - - /// Present the window. - public func show() { - gtk_window_present(pointer) - } - - /// Observe when the window is being closed. - /// - Parameter observer: The signal closure. - public func observeHide(observer: @escaping () -> Void) { - let hideObserver = ViewStorage.SignalData(closure: observer) - self.fields["observe-hide"] = hideObserver - g_signal_connect_data( - pointer?.cast(), - "destroy", - unsafeBitCast(hideObserver.handler, to: GCallback.self), - Unmanaged.passUnretained(hideObserver).toOpaque().cast(), - nil, - G_CONNECT_AFTER - ) - } - - /// Close the window. - public func close() { - gtk_window_close(pointer) - } - - /// Set the window's parent window. - /// - Parameter parent: The parent window. - public func setParentWindow(_ parent: WindowType) { - // swiftlint:disable prefer_self_in_static_references - if let window = parent as? GTUIWindow { - gtk_window_set_modal(pointer, 1) - gtk_window_set_transient_for(pointer, window.pointer) - } - // swiftlint:enable prefer_self_in_static_references - } - -} diff --git a/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift b/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift deleted file mode 100644 index aa329a1..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/WindowScene.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// WindowScene.swift -// Adwaita -// -// Created by david-swift on 05.08.23. -// - -/// A structure representing the content for a certain window type. -public protocol WindowScene: WindowSceneGroup { - - /// The window type's identifier. - var id: String { get } - /// The identifier of the window's parent window. - var parentID: String? { get set } - /// The number of instances of the window at the startup. - var `open`: Int { get } - /// The keyboard shortcuts on the application's level. - var appShortcuts: [String: (GTUIApp) -> Void] { get set } - /// Get the storage for the window. - /// - Parameter app: The application. - /// - Returns: The storage. - func createWindow(app: GTUIApp) -> WindowStorage - /// Update a window storage's content. - /// - Parameters: - /// - storage: The storage to update. - /// - app: The application. - /// - force: Whether to force update all the views. - func update(_ storage: WindowStorage, app: GTUIApp, force: Bool) - -} - -extension WindowScene { - - /// The window scene's body is itself. - @SceneBuilder public var scene: Scene { self } - - /// Add a keyboard shortcut that is available for the whole app. - /// - Parameters: - /// - shortcut: The keyboard shortcut. - /// - The closure to execute. - public func appKeyboardShortcut(_ shortcut: String, action: @escaping (GTUIApp) -> Void) -> Self { - var newSelf = self - newSelf.appShortcuts[shortcut] = action - return newSelf - } - - /// Update the app shortcuts. - /// - /// Call this function in types of window scene. - public func updateAppShortcuts(app: GTUIApp) { - for shortcut in appShortcuts { - app.addKeyboardShortcut(shortcut.key, id: shortcut.key) { shortcut.value(app) } - } - } - - /// Add the shortcut "q" which terminates the application. - /// - Returns: The app. - public func quitShortcut() -> Self { - appKeyboardShortcut("q".ctrl()) { $0.quit() } - } - -} diff --git a/Sources/Adwaita/Model/User Interface/Window/WindowSceneGroup.swift b/Sources/Adwaita/Model/User Interface/Window/WindowSceneGroup.swift deleted file mode 100644 index 209af7d..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/WindowSceneGroup.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// WindowSceneGroup.swift -// Adwaita -// -// Created by david-swift on 14.09.23. -// - -/// A structure conforming to `WindowScene` can be added to an app's `scene`. -public protocol WindowSceneGroup { - - /// The group's content. - @SceneBuilder var scene: Scene { get } - -} - -extension WindowSceneGroup { - - /// Get the windows described by the group. - /// - Returns: The windows. - func windows() -> [WindowScene] { - var content: [WindowScene] = [] - for element in scene { - if let window = element as? WindowScene { - content.append(window) - } else { - content += element.windows() - } - } - return content - } - - /// Update the windows described by the group. - /// - Parameters: - /// - storage: The window's storage. - /// - app: The application. - /// - force: Whether to force update all the views. - func update(_ storage: [WindowStorage], app: GTUIApp, force: Bool) { - for (index, window) in windows().enumerated() { - if let storage = storage[safe: index] { - window.update(storage, app: app, force: force) - } - } - } - -} - -/// `Scene` is an array of windows. -public typealias Scene = [WindowSceneGroup] -/// A builder for the `Scene` -public typealias SceneBuilder = ArrayBuilder diff --git a/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift b/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift deleted file mode 100644 index 4cd6cee..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/WindowStorage.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// SceneStorage.swift -// Adwaita -// -// Created by david-swift on 31.08.23. -// - -/// A storage for an app's window. -public class WindowStorage { - - /// The window's identifier. - public var id: String - /// The identifier of the window's parent window. - public var parentID: String? - /// Whether the reference to the window should disappear in the next update. - public var destroy = false - /// The window. - public var window: WindowType - /// The content's storage. - public var view: ViewStorage? - - /// Initialize a window storage. - /// - Parameters: - /// - id: The window's identifier. - /// - window: The window. - /// - view: The content's storage. - public init(id: String, window: WindowType, view: ViewStorage?) { - self.id = id - self.window = window - self.view = view - } - -} diff --git a/Sources/Adwaita/Model/User Interface/Window/WindowType.swift b/Sources/Adwaita/Model/User Interface/Window/WindowType.swift deleted file mode 100644 index e2b3204..0000000 --- a/Sources/Adwaita/Model/User Interface/Window/WindowType.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// GTUIWindowRepresentable.swift -// Adwaita -// -// Created by david-swift on 09.12.23. -// - -/// A window type. -public protocol WindowType { - - /// A dictionary for custom data. - var fields: [String: Any] { get set } - /// Set a parent window. - /// - Parameter parent: The parent window. - func setParentWindow(_ parent: WindowType) - /// Show the window. - func show() - -} diff --git a/Sources/Adwaita/Model/User Interface/View/WindowView.swift b/Sources/Adwaita/Model/WindowView.swift similarity index 89% rename from Sources/Adwaita/Model/User Interface/View/WindowView.swift rename to Sources/Adwaita/Model/WindowView.swift index e2e5300..5bcfefc 100644 --- a/Sources/Adwaita/Model/User Interface/View/WindowView.swift +++ b/Sources/Adwaita/Model/WindowView.swift @@ -2,7 +2,7 @@ // WindowView.swift // Adwaita // -// Created by david-swift on 26.02.24. +// Created by david-swift on 06.08.24. // /// A special view that can access the window of the current instance diff --git a/Sources/Adwaita/View/Button+.swift b/Sources/Adwaita/View/Button+.swift index 29645bd..3f68952 100644 --- a/Sources/Adwaita/View/Button+.swift +++ b/Sources/Adwaita/View/Button+.swift @@ -45,13 +45,14 @@ extension Button { /// - window: The application window. /// - active: Whether the keyboard shortcut is active. /// - Returns: The button. - public func keyboardShortcut(_ shortcut: String, window: GTUIApplicationWindow, active: Bool = true) -> Self { - if active { - window.addKeyboardShortcut(shortcut, id: shortcut) { self.clicked?() } - } else { - window.removeKeyboardShortcut(id: shortcut) + public func keyboardShortcut(_ shortcut: String, window: AdwaitaWindow, active: Bool = true) -> AnyView { + onUpdate { + if active { + window.app.addKeyboardShortcut(shortcut, id: shortcut, window: window) { self.clicked?() } + } else { + window.app.removeKeyboardShortcut(id: shortcut, window: window) + } } - return self } /// Create a keyboard shortcut for an application from a button. @@ -62,13 +63,14 @@ extension Button { /// - window: The application. /// - active: Whether the keyboard shortcut is active. /// - Returns: The button. - public func keyboardShortcut(_ shortcut: String, app: GTUIApp, active: Bool = true) -> Self { - if active { - app.addKeyboardShortcut(shortcut, id: shortcut) { self.clicked?() } - } else { - app.removeKeyboardShortcut(id: shortcut) + public func keyboardShortcut(_ shortcut: String, app: AdwaitaApp, active: Bool = true) -> AnyView { + onUpdate { + if active { + app.addKeyboardShortcut(shortcut, id: shortcut) { self.clicked?() } + } else { + app.removeKeyboardShortcut(id: shortcut) + } } - return self } } diff --git a/Sources/Adwaita/View/CheckButton+.swift b/Sources/Adwaita/View/CheckButton+.swift index 14bddb8..dea873a 100644 --- a/Sources/Adwaita/View/CheckButton+.swift +++ b/Sources/Adwaita/View/CheckButton+.swift @@ -11,7 +11,7 @@ extension CheckButton { /// Apply the selection mode style class. /// - Parameter active: Whether it is applied. /// - Returns: A view. - public func selectionMode(_ active: Bool = true) -> View { + public func selectionMode(_ active: Bool = true) -> AnyView { style("selection-mode", active: active) } diff --git a/Sources/Adwaita/View/Dialogs/AboutDialog.swift b/Sources/Adwaita/View/Dialogs/AboutDialog.swift index 7def381..533b722 100644 --- a/Sources/Adwaita/View/Dialogs/AboutDialog.swift +++ b/Sources/Adwaita/View/Dialogs/AboutDialog.swift @@ -9,12 +9,12 @@ import CAdw import Foundation /// The about dialog widget. -struct AboutDialog: Widget { +struct AboutDialog: AdwaitaWidget { /// Whether the dialog is visible. @Binding var visible: Bool /// The wrapped view. - var child: View + var child: AnyView /// The app's name. var appName: String? @@ -32,50 +32,72 @@ struct AboutDialog: Widget { /// The ID for the dialog's storage. let dialogID = "dialog" - /// Get the container of the child. - /// - Parameter modifiers: Modify views before being updated. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = child.storage(modifiers: modifiers) - update(storage, modifiers: modifiers, updateProperties: true) + func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { + let storage = child.storage(modifiers: modifiers, type: type) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the view storage of the child, dialog, and dialog content. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - child.widget(modifiers: modifiers).update(storage, modifiers: modifiers, updateProperties: updateProperties) - guard updateProperties else { + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + child.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) + guard updateProperties, (storage.previousState as? Self)?.visible != visible else { return } if visible { if storage.content[dialogID]?.first == nil { - createDialog(storage: storage, modifiers: modifiers) - adw_dialog_present(storage.content[dialogID]?.first?.pointer?.cast(), storage.pointer?.cast()) + createDialog(storage: storage) + adw_dialog_present( + storage.content[dialogID]?.first?.opaquePointer?.cast(), + storage.opaquePointer?.cast() + ) + } + let dialog = storage.content[dialogID]?.first?.opaquePointer + if let appName { + adw_about_dialog_set_application_name(dialog, appName) + } + if let developer { + adw_about_dialog_set_developer_name(dialog, developer) + } + if let version { + adw_about_dialog_set_version(dialog, version) + } + if let icon { + adw_about_dialog_set_application_icon(dialog, icon.string) + } + if let website { + adw_about_dialog_set_website(dialog, website.absoluteString) + } + if let issues { + adw_about_dialog_set_issue_url(dialog, issues.absoluteString) } - let dialog = storage.content[dialogID]?.first?.pointer - adw_about_dialog_set_application_name(dialog, appName) - adw_about_dialog_set_developer_name(dialog, developer) - adw_about_dialog_set_version(dialog, version) - adw_about_dialog_set_application_icon(dialog, icon?.string) - adw_about_dialog_set_website(dialog, website?.absoluteString) - adw_about_dialog_set_support_url(dialog, issues?.absoluteString) adw_dialog_set_content_height(dialog?.cast(), -1) } else { if storage.content[dialogID]?.first != nil { - adw_dialog_close(storage.content[dialogID]?.first?.pointer?.cast()) + adw_dialog_close(storage.content[dialogID]?.first?.opaquePointer?.cast()) } } + storage.previousState = self } /// Create a new instance of the dialog. - /// - Parameters: - /// - storage: The wrapped view's storage. - /// - modifiers: The view modifiers. - func createDialog(storage: ViewStorage, modifiers: [(View) -> View]) { + /// - Parameter storage: The wrapped view's storage. + func createDialog(storage: ViewStorage) { let pointer = adw_about_dialog_new() let dialog = ViewStorage(pointer?.opaque()) storage.content[dialogID] = [dialog] @@ -89,7 +111,7 @@ struct AboutDialog: Widget { } -extension View { +extension AnyView { /// Add an about dialog to the parent window. /// - Parameters: @@ -108,7 +130,7 @@ extension View { icon: Icon? = nil, website: URL? = nil, issues: URL? = nil - ) -> View { + ) -> AnyView { AboutDialog( visible: visible, child: self, diff --git a/Sources/Adwaita/View/Dialogs/AlertDialog.swift b/Sources/Adwaita/View/Dialogs/AlertDialog.swift index 66830ab..902ab4f 100644 --- a/Sources/Adwaita/View/Dialogs/AlertDialog.swift +++ b/Sources/Adwaita/View/Dialogs/AlertDialog.swift @@ -9,7 +9,7 @@ import CAdw import LevenshteinTransformations /// The message dialog widget. -public struct AlertDialog: Widget { +public struct AlertDialog: AdwaitaWidget { /// The ID for the dialog's storage. static let dialogID = "alert-dialog" @@ -31,7 +31,7 @@ public struct AlertDialog: Widget { /// The available responses. var responses: [Response] = [] /// The child view. - var child: View + var child: AnyView /// Information about a response. struct Response: Identifiable { @@ -71,33 +71,44 @@ public struct AlertDialog: Widget { } - /// Get the container of the child. - /// - Parameter modifiers: Modify views before being updated. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = child.storage(modifiers: modifiers) - storage.fields[Self.visibleID + id] = _visible - update(storage, modifiers: modifiers, updateProperties: true) + public func container( + modifiers: [(AnyView) -> AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { + let storage = child.storage(modifiers: modifiers, type: type) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the view storage of the child, dialog, and dialog content. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - child.widget(modifiers: modifiers).update(storage, modifiers: modifiers, updateProperties: updateProperties) + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + storage.fields[Self.visibleID + id] = _visible + child.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) guard updateProperties else { return } if visible { var present = false if storage.content[Self.dialogID + id]?.first == nil { - createDialog(storage: storage, modifiers: modifiers) + createDialog(storage: storage) present = true } - let pointer = storage.content[Self.dialogID + id]?.first?.pointer + let pointer = storage.content[Self.dialogID + id]?.first?.opaquePointer adw_alert_dialog_set_heading(pointer?.cast(), heading) adw_alert_dialog_set_body(pointer?.cast(), body) let old = storage.fields[Self.responsesID + id] as? [Response] ?? [] @@ -123,12 +134,12 @@ public struct AlertDialog: Widget { gtui_alertdialog_choose( .init(Int(bitPattern: pointer)), unsafeBitCast(storage, to: UInt64.self), - .init(Int(bitPattern: storage.pointer)) + .init(Int(bitPattern: storage.opaquePointer)) ) } } else { if storage.content[Self.dialogID + id]?.first != nil { - adw_dialog_close(storage.content[Self.dialogID + id]?.first?.pointer?.cast()) + adw_dialog_close(storage.content[Self.dialogID + id]?.first?.opaquePointer?.cast()) } } } @@ -167,10 +178,8 @@ public struct AlertDialog: Widget { } /// Create a new instance of the dialog. - /// - Parameters: - /// - storage: The wrapped view's storage. - /// - modifiers: The view modifiers. - func createDialog(storage: ViewStorage, modifiers: [(View) -> View]) { + /// - Parameter storage: The wrapped view's storage. + func createDialog(storage: ViewStorage) { let pointer = adw_alert_dialog_new(nil, nil) let dialog = ViewStorage(pointer?.opaque()) storage.content[Self.dialogID + id] = [dialog] @@ -205,7 +214,7 @@ public struct AlertDialog: Widget { } -extension View { +extension AnyView { /// Add an alert dialog to the parent window. /// - Parameters: diff --git a/Sources/Adwaita/View/Dialogs/Dialog.swift b/Sources/Adwaita/View/Dialogs/Dialog.swift index 63d7f8f..292ba9b 100644 --- a/Sources/Adwaita/View/Dialogs/Dialog.swift +++ b/Sources/Adwaita/View/Dialogs/Dialog.swift @@ -8,7 +8,7 @@ import CAdw /// The dialog widget. -struct Dialog: Widget { +struct Dialog: AdwaitaWidget { /// Whether the dialog is visible. @Binding var visible: Bool @@ -17,7 +17,7 @@ struct Dialog: Widget { /// The dialog's title. var title: String? /// The wrapped view. - var child: View + var child: AnyView /// The content of the dialog. var content: Body /// The dialog's width. @@ -30,42 +30,61 @@ struct Dialog: Widget { /// The ID for the content's storage. let contentID = "content" - /// Get the container of the child. - /// - Parameter modifiers: Modify views before being updated. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = child.storage(modifiers: modifiers) - update(storage, modifiers: modifiers, updateProperties: true) + func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { + let child = child.storage(modifiers: modifiers, type: type) + let storage = ViewStorage(child.opaquePointer, content: [.mainContent: [child]]) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the view storage of the child, dialog, and dialog content. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - child.widget(modifiers: modifiers).update(storage, modifiers: modifiers, updateProperties: updateProperties) - if let storage = storage.content[contentID + id]?.first as? ViewStorage { + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + if let storage = storage.content[.mainContent]?.first { + child.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) + } + if let storage = storage.content[contentID + id]?.first { content - .widget(modifiers: modifiers) - .update(storage, modifiers: modifiers, updateProperties: updateProperties) + .updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) } guard updateProperties else { return } if visible { if storage.content[dialogID + id]?.first == nil { - createDialog(storage: storage, modifiers: modifiers) - adw_dialog_present(storage.content[dialogID + id]?.first?.pointer?.cast(), storage.pointer?.cast()) + createDialog(storage: storage, modifiers: modifiers, type: type) + adw_dialog_present( + storage.content[dialogID + id]?.first?.opaquePointer?.cast(), + storage.opaquePointer?.cast() + ) + } + let pointer = storage.content[dialogID + id]?.first?.opaquePointer + if let title { + adw_dialog_set_title(pointer?.cast(), title) + } + if let width { + adw_dialog_set_content_width(pointer?.cast(), width.cInt) + } + if let height { + adw_dialog_set_content_height(pointer?.cast(), height.cInt) } - let pointer = storage.content[dialogID + id]?.first?.pointer - adw_dialog_set_title(pointer?.cast(), title ?? "") - adw_dialog_set_content_width(pointer?.cast(), width?.cInt ?? -1) - adw_dialog_set_content_height(pointer?.cast(), height?.cInt ?? -1) } else { if storage.content[dialogID + id]?.first != nil { - adw_dialog_close(storage.content[dialogID + id]?.first?.pointer?.cast()) + adw_dialog_close(storage.content[dialogID + id]?.first?.opaquePointer?.cast()) } } } @@ -74,12 +93,17 @@ struct Dialog: Widget { /// - Parameters: /// - storage: The wrapped view's storage. /// - modifiers: The view modifiers. - func createDialog(storage: ViewStorage, modifiers: [(View) -> View]) { + /// - type: The view render data type. + func createDialog( + storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + type: Data.Type + ) where Data: ViewRenderData { let pointer = adw_dialog_new() let dialog = ViewStorage(pointer?.opaque()) storage.content[dialogID + id] = [dialog] - let contentStorage = content.widget(modifiers: modifiers).storage(modifiers: modifiers) - adw_dialog_set_child(pointer, contentStorage.pointer?.cast()) + let contentStorage = content.storage(modifiers: modifiers, type: type) + adw_dialog_set_child(pointer, contentStorage.opaquePointer?.cast()) storage.content[contentID + id] = [contentStorage] dialog.connectSignal(name: "closed") { storage.content[dialogID + id] = [] @@ -92,7 +116,7 @@ struct Dialog: Widget { } -extension View { +extension AnyView { /// Add a dialog to the parent window. /// - Parameters: @@ -108,7 +132,7 @@ extension View { width: Int? = nil, height: Int? = nil, @ViewBuilder content: () -> Body - ) -> View { + ) -> AnyView { Dialog( visible: visible, id: id ?? "", diff --git a/Sources/Adwaita/View/Dialogs/FileDialog.swift b/Sources/Adwaita/View/Dialogs/FileDialog.swift new file mode 100644 index 0000000..b1dd088 --- /dev/null +++ b/Sources/Adwaita/View/Dialogs/FileDialog.swift @@ -0,0 +1,225 @@ +// +// FileDialog.swift +// Adwaita +// +// Created by david-swift on 12.08.24. +// + +import CAdw +import Foundation + +/// A structure representing a file dialog window. +struct FileDialog: AdwaitaWidget { + + /// Whether the dialog is an importer. + var importer = true + /// Whether the dialog should open. + var open: Signal + /// The dialog's child. + var child: AnyView + /// The initial folder. + var initialFolder: URL? + /// The initial file name for the file exporter. + var initialName: String? + /// The accepted extensions for the file importer. + var extensions: [String]? + /// The closure to run when the import or export is successful. + var result: (URL) -> Void + /// The closure to run when the import or export is not successful. + var cancel: () -> Void + + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. + /// - Returns: The view storage. + func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { + let child = child.storage(modifiers: modifiers, type: type) + let storage = ViewStorage(child.opaquePointer, content: [.mainContent: [child]]) + update(storage, modifiers: modifiers, updateProperties: true, type: type) + return storage + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + storage.fields["result"] = result + storage.fields["cancel"] = cancel + guard let mainStorage = storage.content[.mainContent]?.first else { + return + } + child.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) + if open.update, storage.fields["callbacks"] == nil { + let pointer = gtk_file_dialog_new() + if let initialName { + gtk_file_dialog_set_initial_name(pointer, initialName) + } + if let extensions { + let filter = gtk_file_filter_new() + for name in extensions { + gtk_file_filter_add_suffix(filter, name) + } + gtk_file_dialog_set_default_filter(pointer, filter) + } else { + gtk_file_dialog_set_default_filter(pointer, nil) + } + if let initialFolder { + gtk_file_dialog_set_initial_folder(pointer, g_file_new_for_path(initialFolder.absoluteString)) + } + let callbacks = AdwaitaFileDialog() + callbacks.onResult = { (storage.fields["result"] as? (URL) -> Void)?($0) } + callbacks.onCancel = { (storage.fields["cancel"] as? () -> Void)?() } + callbacks.reset = { storage.fields["callbacks"] = nil } + storage.fields["callbacks"] = callbacks + let ptr = UInt64(Int(bitPattern: pointer)) + let window = UInt64(Int(bitPattern: gtk_widget_get_root(mainStorage.opaquePointer?.cast()))) + if importer { + gtui_filedialog_open(ptr, unsafeBitCast(callbacks, to: UInt64.self), window) + } else { + gtui_filedialog_save(ptr, unsafeBitCast(callbacks, to: UInt64.self), window) + } + } + } + +} + +extension AnyView { + + /// Create an importer file dialog. + /// - Parameters: + /// - open: The signal to open the dialog. + /// - initialFolder: The URL to the folder open when being opened. + /// - extensions: The accepted file extensions. + /// - folders: Whether folders are accepted. + /// - onOpen: Run this when a file for importing has been chosen. + /// - onClose: Run this when the user cancelled the action. + public func fileImporter( + open: Signal, + initialFolder: URL? = nil, + extensions: [String]? = nil, + onOpen: @escaping (URL) -> Void, + onClose: @escaping () -> Void + ) -> AnyView { + FileDialog( + importer: true, + open: open, + child: self, + initialFolder: initialFolder, + initialName: nil, + extensions: extensions, + result: onOpen, + cancel: onClose + ) + } + + /// Create an exporter file dialog. + /// - Parameters: + /// - exporter: The signal to open the dialog. + /// - initialFolder: The URL to the folder open when being opened. + /// - initialName: The default file name. + /// - onSave: Run this when a path for exporting has been chosen. + /// - onClose: Run this when the user cancelled the action. + public func fileExporter( + open: Signal, + initialFolder: URL? = nil, + initialName: String? = nil, + onSave: @escaping (URL) -> Void, + onClose: @escaping () -> Void + ) -> AnyView { + FileDialog( + importer: false, + open: open, + child: self, + initialFolder: initialFolder, + initialName: initialName, + result: onSave, + cancel: onClose + ) + } + +} + +/// An Adwaita file dialog window callback. +class AdwaitaFileDialog { + + /// A closure triggered on selecting a file in the dialog. + var onResult: (URL) -> Void = { _ in } + /// A closure triggered when the dialog is canceled. + var onCancel: () -> Void = { } + /// Reset the file dialog. + var reset: () -> Void = { } + + /// Initialize the window callback. + init() { + } + + /// Run this when a file gets opened. + /// - Parameter path: The file path. + func onOpen(_ path: String) { + let url = URL(fileURLWithPath: path) + onResult(url) + reset() + } + + /// Run this when a file gets saved. + /// - Parameter path: The file path. + func onSave(_ path: String) { + let url = URL(fileURLWithPath: path) + onResult(url) + reset() + } + + /// Run this when the user cancels the action. + func onClose() { + onCancel() + reset() + } + +} + +/// Run when a file should be opened. +/// - Parameters: +/// - ptr: The pointer. +/// - file: The path to the file. +/// - userData: The file dialog data. +@_cdecl("filedialog_on_open_cb") +func filedialog_on_open_cb( + ptr: UnsafeMutableRawPointer, + file: UnsafePointer?, + userData: UnsafeMutableRawPointer +) { + let dialog = Unmanaged.fromOpaque(userData).takeUnretainedValue() + if let file { + dialog.onOpen(.init(cString: file)) + } else { + dialog.onClose() + } +} + +/// Run when a file should be saved. +/// - Parameters: +/// - ptr: The pointer. +/// - file: The path to the file. +/// - userData: The file dialog data. +@_cdecl("filedialog_on_save_cb") +func filedialog_on_save_cb( + ptr: UnsafeMutableRawPointer, + file: UnsafePointer?, + userData: UnsafeMutableRawPointer +) { + let dialog = Unmanaged.fromOpaque(userData).takeUnretainedValue() + if let file { + dialog.onSave(.init(cString: file)) + } else { + dialog.onClose() + } +} diff --git a/Sources/Adwaita/View/EitherView.swift b/Sources/Adwaita/View/EitherView.swift new file mode 100644 index 0000000..dc14f64 --- /dev/null +++ b/Sources/Adwaita/View/EitherView.swift @@ -0,0 +1,44 @@ +// +// ViewStack.swift +// Adwaita +// +// Created by david-swift on 30.12.23. +// + +import CAdw + +/// A widget showing one of two widgets based on a condition. +public struct EitherView: View, Meta.EitherView { + + /// Whether the first view is visible. + var condition: Bool + /// The first view. + var view1: Body + /// The second view. + var view2: Body + + /// The view's content. + public var view: Body { + if condition { + return [ViewStack(id: true) { _ in view1 }.homogeneous(false)] + } else { + return [ViewStack(id: false) { _ in view2 }.homogeneous(false)] + } + } + + /// Initialize the either view. + /// - Parameters: + /// - condition: Whether the first view is visible- + /// - view1: The first view, visible if true. + /// - view2: The second view, visible if false. + public init( + _ condition: Bool, + @ViewBuilder view1: () -> Body, + @ViewBuilder else view2: () -> Body + ) { + self.condition = condition + self.view1 = view1() + self.view2 = view2() + } + +} diff --git a/Sources/Adwaita/View/Fixed+.swift b/Sources/Adwaita/View/Fixed+.swift index b5fd22d..11f314a 100644 --- a/Sources/Adwaita/View/Fixed+.swift +++ b/Sources/Adwaita/View/Fixed+.swift @@ -24,10 +24,10 @@ extension Fixed { ) -> Self { var newSelf = self newSelf.appearFunctions.append { storage, modifiers in - let view = view().storage(modifiers: modifiers) + let view = view().storage(modifiers: modifiers, type: AdwaitaMainView.self) gtk_fixed_put( - storage.pointer?.cast(), - view.pointer?.cast(), + storage.opaquePointer?.cast(), + view.opaquePointer?.cast(), xCoordinate, yCoordinate ) @@ -37,11 +37,17 @@ extension Fixed { guard let content = storage.content[id]?.first else { return } - view().updateStorage(content, modifiers: modifiers, updateProperties: updateProperties) + view() + .updateStorage( + content, + modifiers: modifiers, + updateProperties: updateProperties, + type: AdwaitaMainView.self + ) if updateProperties { gtk_fixed_move( - storage.pointer?.cast(), - content.pointer?.cast(), + storage.opaquePointer?.cast(), + content.opaquePointer?.cast(), xCoordinate, yCoordinate ) diff --git a/Sources/Adwaita/View/FlowBox+.swift b/Sources/Adwaita/View/FlowBox+.swift index abd3010..603785b 100644 --- a/Sources/Adwaita/View/FlowBox+.swift +++ b/Sources/Adwaita/View/FlowBox+.swift @@ -26,32 +26,32 @@ extension FlowBox { ) { self.init(elements, content: content) let id: (ViewStorage, [Element]) -> Element.ID? = { storage, elements in - if let child = g_list_nth_data(gtk_flow_box_get_selected_children(storage.pointer), 0) { + if let child = g_list_nth_data(gtk_flow_box_get_selected_children(storage.opaquePointer), 0) { let element = gtk_flow_box_child_get_child(child.cast()) - return elements[safe: storage.content[.mainContent]?.firstIndex { $0.pointer?.cast() == element }]?.id + return elements[safe: storage.content[.mainContent]? + .firstIndex { $0.opaquePointer?.cast() == element }]?.id } return nil } if let selection { - appearFunctions.append { storage, _ in - storage.fields[Self.selectionField] = selection + updateFunctions.append { storage, _, _ in storage.connectSignal(name: "selected_children_changed", id: Self.selectionField) { - if let binding = storage.fields[Self.selectionField] as? Binding, - let elements = storage.fields[Self.elementsField] as? [Element], + if let elements = storage.fields[Self.elementsField] as? [Element], let id = id(storage, elements) { - binding.wrappedValue = id + selection.wrappedValue = id } } - } - updateFunctions.append { storage, _, _ in if selection.wrappedValue != id(storage, elements), let index = elements.firstIndex(where: { $0.id == selection.wrappedValue })?.cInt { - gtk_flow_box_select_child(storage.pointer, gtk_flow_box_get_child_at_index(storage.pointer, index)) + gtk_flow_box_select_child( + storage.opaquePointer, + gtk_flow_box_get_child_at_index(storage.opaquePointer, index) + ) } } } else { appearFunctions.append { storage, _ in - gtk_flow_box_set_selection_mode(storage.pointer, GTK_SELECTION_NONE) + gtk_flow_box_set_selection_mode(storage.opaquePointer, GTK_SELECTION_NONE) } } } diff --git a/Sources/Adwaita/View/ForEach.swift b/Sources/Adwaita/View/ForEach.swift index 8943a84..a9dd173 100644 --- a/Sources/Adwaita/View/ForEach.swift +++ b/Sources/Adwaita/View/ForEach.swift @@ -9,7 +9,7 @@ import CAdw import LevenshteinTransformations /// A dynamic list but without a list design in the user interface. -public struct ForEach: Widget where Element: Identifiable { +public struct ForEach: AdwaitaWidget where Element: Identifiable { /// The dynamic widget elements. var elements: [Element] @@ -25,47 +25,58 @@ public struct ForEach: Widget where Element: Identifiable { self.horizontal = horizontal } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container( + modifiers: [(AnyView) -> AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage( gtk_box_new(horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, 0)?.opaque() ) - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? [] let old = storage.fields["element"] as? [Element] ?? [] - let widget: UnsafeMutablePointer? = storage.pointer?.cast() + let widget: UnsafeMutablePointer? = storage.opaquePointer?.cast() old.identifiableTransform( to: elements, functions: .init { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) - gtk_box_remove(widget, contentStorage[safe: index]?.pointer?.cast()) + let child = content(element).storage(modifiers: modifiers, type: type) + gtk_box_remove(widget, contentStorage[safe: index]?.opaquePointer?.cast()) gtk_box_insert_child_after( widget, - child.pointer?.cast(), - contentStorage[safe: index - 1]?.pointer?.cast() + child.opaquePointer?.cast(), + contentStorage[safe: index - 1]?.opaquePointer?.cast() ) contentStorage.remove(at: index) contentStorage.insert(child, at: index) } delete: { index in - gtk_box_remove(widget, contentStorage[safe: index]?.pointer?.cast()) + gtk_box_remove(widget, contentStorage[safe: index]?.opaquePointer?.cast()) contentStorage.remove(at: index) } insert: { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) + let child = content(element).storage(modifiers: modifiers, type: type) gtk_box_insert_child_after( widget, - child.pointer?.cast(), - contentStorage[safe: index - 1]?.pointer?.cast() + child.opaquePointer?.cast(), + contentStorage[safe: index - 1]?.opaquePointer?.cast() ) contentStorage.insert(child, at: index) } @@ -80,8 +91,12 @@ public struct ForEach: Widget where Element: Identifiable { storage.content[.mainContent] = contentStorage for (index, element) in elements.enumerated() { content(element) - .widget(modifiers: modifiers) - .update(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties) + .updateStorage( + contentStorage[index], + modifiers: modifiers, + updateProperties: updateProperties, + type: type + ) } } diff --git a/Sources/Adwaita/View/Forms/ActionRow+.swift b/Sources/Adwaita/View/Forms/ActionRow+.swift index 97446f5..8e2c044 100644 --- a/Sources/Adwaita/View/Forms/ActionRow+.swift +++ b/Sources/Adwaita/View/Forms/ActionRow+.swift @@ -17,7 +17,7 @@ extension ActionRow { /// Deemphasize the row title and emphasize the subtitle. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func property(_ active: Bool = true) -> View { + public func property(_ active: Bool = true) -> AnyView { style("property", active: active) } diff --git a/Sources/Adwaita/View/Forms/ComboRow+.swift b/Sources/Adwaita/View/Forms/ComboRow+.swift index e6ced30..7e7f809 100644 --- a/Sources/Adwaita/View/Forms/ComboRow+.swift +++ b/Sources/Adwaita/View/Forms/ComboRow+.swift @@ -35,7 +35,7 @@ extension ComboRow { appearFunctions.append { storage, _ in let list = gtk_string_list_new(nil) storage.fields[Self.stringList] = list - adw_combo_row_set_model(storage.pointer?.cast(), list) + adw_combo_row_set_model(storage.opaquePointer?.cast(), list) Self.updateContent(storage: storage, values: values, element: Element.self) } updateFunctions.append { storage, _, _ in diff --git a/Sources/Adwaita/View/Forms/EntryRow+.swift b/Sources/Adwaita/View/Forms/EntryRow+.swift index f4c41da..0e1fa6c 100644 --- a/Sources/Adwaita/View/Forms/EntryRow+.swift +++ b/Sources/Adwaita/View/Forms/EntryRow+.swift @@ -18,18 +18,15 @@ extension EntryRow { public init(_ title: String, text: Binding) { self.init() self = self.title(title) - appearFunctions.append { storage, _ in - storage.fields[Self.textField] = text + updateFunctions.append { storage, _, _ in storage.notify(name: "text") { - let newValue = String(cString: gtk_editable_get_text(storage.pointer)) - if let binding = storage.fields[Self.textField] as? Binding, binding.wrappedValue != newValue { - binding.wrappedValue = newValue + let newValue = String(cString: gtk_editable_get_text(storage.opaquePointer)) + if text.wrappedValue != newValue { + text.wrappedValue = newValue } } - } - updateFunctions.append { storage, _, _ in - if text.wrappedValue != .init(cString: gtk_editable_get_text(storage.pointer)) { - gtk_editable_set_text(storage.pointer, text.wrappedValue) + if text.wrappedValue != .init(cString: gtk_editable_get_text(storage.opaquePointer)) { + gtk_editable_set_text(storage.opaquePointer, text.wrappedValue) } } } diff --git a/Sources/Adwaita/View/Forms/Form.swift b/Sources/Adwaita/View/Forms/Form.swift index 386436e..eccc5e3 100644 --- a/Sources/Adwaita/View/Forms/Form.swift +++ b/Sources/Adwaita/View/Forms/Form.swift @@ -8,7 +8,7 @@ import CAdw /// A list with no dynamic content styled as a boxed list. -public struct Form: View { +public struct Form: SimpleView { /// The content. var content: Body diff --git a/Sources/Adwaita/View/Forms/PasswordEntryRow+.swift b/Sources/Adwaita/View/Forms/PasswordEntryRow+.swift index a1402e4..93ecc21 100644 --- a/Sources/Adwaita/View/Forms/PasswordEntryRow+.swift +++ b/Sources/Adwaita/View/Forms/PasswordEntryRow+.swift @@ -18,17 +18,15 @@ extension PasswordEntryRow { public init(_ title: String, text: Binding) { self.init() self = self.title(title) - appearFunctions.append { storage, _ in - storage.fields[Self.textField] = text + updateFunctions.append { storage, _, _ in storage.notify(name: "text") { - if let binding = storage.fields[Self.textField] as? Binding { - binding.wrappedValue = .init(cString: gtk_editable_get_text(storage.pointer)) + let newValue = String(cString: gtk_editable_get_text(storage.opaquePointer)) + if text.wrappedValue != newValue { + text.wrappedValue = newValue } } - } - updateFunctions.append { storage, _, _ in - if text.wrappedValue != .init(cString: gtk_editable_get_text(storage.pointer)) { - gtk_editable_set_text(storage.pointer, text.wrappedValue) + if text.wrappedValue != .init(cString: gtk_editable_get_text(storage.opaquePointer)) { + gtk_editable_set_text(storage.opaquePointer, text.wrappedValue) } } } diff --git a/Sources/Adwaita/View/Forms/SpinRow+.swift b/Sources/Adwaita/View/Forms/SpinRow+.swift index 8695afb..2dd39a9 100644 --- a/Sources/Adwaita/View/Forms/SpinRow+.swift +++ b/Sources/Adwaita/View/Forms/SpinRow+.swift @@ -36,7 +36,7 @@ extension SpinRow { self = self.value(value) self = self.step(1) updateFunctions.append { storage, _, _ in - adw_spin_row_set_range(storage.pointer, min, max) + adw_spin_row_set_range(storage.opaquePointer, min, max) } } @@ -53,7 +53,7 @@ extension SpinRow { public func step(_ step: Double) -> Self { var newSelf = self newSelf.updateFunctions.append { storage, _, _ in - let adjustment = adw_spin_row_get_adjustment(storage.pointer) + let adjustment = adw_spin_row_get_adjustment(storage.opaquePointer) gtk_adjustment_set_step_increment(adjustment, step) } return newSelf diff --git a/Sources/Adwaita/View/Generated/ActionRow.swift b/Sources/Adwaita/View/Generated/ActionRow.swift index 61778e3..84cf2d4 100644 --- a/Sources/Adwaita/View/Generated/ActionRow.swift +++ b/Sources/Adwaita/View/Generated/ActionRow.swift @@ -2,7 +2,7 @@ // ActionRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -44,12 +44,12 @@ import LevenshteinTransformations /// [`.property`](style-classes.html#property-rows) style class to emphasize /// the row subtitle instead of the row title, which is useful for /// displaying read-only properties. -public struct ActionRow: Widget { +public struct ActionRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The widget to activate when the row is activated. /// @@ -60,9 +60,7 @@ public struct ActionRow: Widget { /// /// The target widget will be activated by emitting the /// [signal@Gtk.Widget::mnemonic-activate] signal on it. - var activatableWidget: (() -> Body)? - /// The icon name for this row. - var iconName: String? + var activatableWidget: (() -> Body)? /// The subtitle for this row. /// /// The subtitle is interpreted as Pango markup unless @@ -105,49 +103,52 @@ public struct ActionRow: Widget { /// The body for the widget "prefix". var prefix: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ActionRow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_action_row_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let activatableWidgetStorage = activatableWidget?().storage(modifiers: modifiers, type: type) { storage.content["activatableWidget"] = [activatableWidgetStorage] - adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast()) + adw_action_row_set_activatable_widget(storage.opaquePointer?.cast(), activatableWidgetStorage.opaquePointer?.cast()) } var suffixStorage: [ViewStorage] = [] for view in suffix() { - suffixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast()) + suffixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_suffix(storage.opaquePointer?.cast(), suffixStorage.last?.opaquePointer?.cast()) } storage.content["suffix"] = suffixStorage var prefixStorage: [ViewStorage] = [] for view in prefix() { - prefixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast()) + prefixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_prefix(storage.opaquePointer?.cast(), prefixStorage.last?.opaquePointer?.cast()) } storage.content["prefix"] = prefixStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activated { storage.connectSignal(name: "activated", argCount: 0) { activated() @@ -156,33 +157,30 @@ public struct ActionRow: Widget { storage.modify { widget in if let widget = storage.content["activatableWidget"]?.first { - activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) - } - if let iconName, updateProperties { - adw_action_row_set_icon_name(widget?.cast(), iconName) + activatableWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let subtitleLines, updateProperties { + if let subtitleLines, updateProperties, (storage.previousState as? Self)?.subtitleLines != subtitleLines { adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt) } - if let subtitleSelectable, updateProperties { + if let subtitleSelectable, updateProperties, (storage.previousState as? Self)?.subtitleSelectable != subtitleSelectable { adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool) } - if let titleLines, updateProperties { + if let titleLines, updateProperties, (storage.previousState as? Self)?.titleLines != titleLines { adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let subtitle, updateProperties { + if let subtitle, updateProperties, (storage.previousState as? Self)?.subtitle != subtitle { adw_action_row_set_subtitle(widget?.cast(), subtitle) } @@ -192,7 +190,8 @@ public struct ActionRow: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -203,7 +202,8 @@ public struct ActionRow: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -213,6 +213,9 @@ public struct ActionRow: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The widget to activate when the row is activated. @@ -231,14 +234,6 @@ public struct ActionRow: Widget { return newSelf } - /// The icon name for this row. - public func iconName(_ iconName: String?) -> Self { - var newSelf = self - newSelf.iconName = iconName - - return newSelf - } - /// The subtitle for this row. /// /// The subtitle is interpreted as Pango markup unless diff --git a/Sources/Adwaita/View/Generated/AspectFrame.swift b/Sources/Adwaita/View/Generated/AspectFrame.swift index 3422149..452c760 100644 --- a/Sources/Adwaita/View/Generated/AspectFrame.swift +++ b/Sources/Adwaita/View/Generated/AspectFrame.swift @@ -2,7 +2,7 @@ // AspectFrame.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -22,19 +22,19 @@ import LevenshteinTransformations /// Until GTK 4.10, `GtkAspectFrame` used the `GTK_ACCESSIBLE_ROLE_GROUP` role. /// /// Starting from GTK 4.12, `GtkAspectFrame` uses the `GTK_ACCESSIBLE_ROLE_GENERIC` role. -public struct AspectFrame: Widget { +public struct AspectFrame: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. var accessibleRole: String? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// Whether the `GtkAspectFrame` should use the aspect ratio of its child. var obeyChild: Bool? /// The aspect ratio to be used by the `GtkAspectFrame`. @@ -47,53 +47,56 @@ public struct AspectFrame: Widget { /// The vertical alignment of the child. var yalign: Float? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `AspectFrame`. public init(ratio: Float) { self.ratio = ratio } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_aspect_frame_new(0.5, 0.5, ratio, 0)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_aspect_frame_set_child(storage.pointer, childStorage.pointer?.cast()) + gtk_aspect_frame_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let obeyChild, updateProperties { + if let obeyChild, updateProperties, (storage.previousState as? Self)?.obeyChild != obeyChild { gtk_aspect_frame_set_obey_child(widget, obeyChild.cBool) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.ratio != ratio { gtk_aspect_frame_set_ratio(widget, ratio) } - if let xalign, updateProperties { + if let xalign, updateProperties, (storage.previousState as? Self)?.xalign != xalign { gtk_aspect_frame_set_xalign(widget, xalign) } - if let yalign, updateProperties { + if let yalign, updateProperties, (storage.previousState as? Self)?.yalign != yalign { gtk_aspect_frame_set_yalign(widget, yalign) } @@ -102,6 +105,9 @@ public struct AspectFrame: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/Avatar.swift b/Sources/Adwaita/View/Generated/Avatar.swift index 61cee84..9c4e16d 100644 --- a/Sources/Adwaita/View/Generated/Avatar.swift +++ b/Sources/Adwaita/View/Generated/Avatar.swift @@ -2,7 +2,7 @@ // Avatar.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -28,12 +28,12 @@ import LevenshteinTransformations /// ## CSS nodes /// /// `AdwAvatar` has a single CSS node with name `avatar`. -public struct Avatar: Widget { +public struct Avatar: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The name of an icon to use as a fallback. /// @@ -51,9 +51,9 @@ public struct Avatar: Widget { /// `FALSE`. var text: String? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Avatar`. public init(showInitials: Bool, size: Int) { @@ -61,37 +61,40 @@ public struct Avatar: Widget { self.size = size } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_avatar_new(size.cInt, text, showInitials.cBool)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { adw_avatar_set_icon_name(widget, iconName) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.showInitials != showInitials { adw_avatar_set_show_initials(widget, showInitials.cBool) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.size != size { adw_avatar_set_size(widget, size.cInt) } - if let text, updateProperties { + if let text, updateProperties, (storage.previousState as? Self)?.text != text { adw_avatar_set_text(widget, text) } @@ -100,6 +103,9 @@ public struct Avatar: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The name of an icon to use as a fallback. diff --git a/Sources/Adwaita/View/Generated/Banner.swift b/Sources/Adwaita/View/Generated/Banner.swift index ede715b..4f3a009 100644 --- a/Sources/Adwaita/View/Generated/Banner.swift +++ b/Sources/Adwaita/View/Generated/Banner.swift @@ -2,7 +2,7 @@ // Banner.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -27,12 +27,12 @@ import LevenshteinTransformations /// ## CSS nodes /// /// `AdwBanner` has a main CSS node with the name `banner`. -public struct Banner: Widget { +public struct Banner: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The label to show on the button. /// @@ -56,34 +56,37 @@ public struct Banner: Widget { /// It can be used as an alternative to setting an action. var buttonClicked: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Banner`. public init(title: String) { self.title = title } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_banner_new(title)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let buttonClicked { storage.connectSignal(name: "button-clicked", argCount: 0) { buttonClicked() @@ -91,16 +94,16 @@ public struct Banner: Widget { } storage.modify { widget in - if let buttonLabel, updateProperties { + if let buttonLabel, updateProperties, (storage.previousState as? Self)?.buttonLabel != buttonLabel { adw_banner_set_button_label(widget, buttonLabel) } - if let revealed, updateProperties { + if let revealed, updateProperties, (storage.previousState as? Self)?.revealed != revealed { adw_banner_set_revealed(widget, revealed.cBool) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.title != title { adw_banner_set_title(widget, title) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_banner_set_use_markup(widget, useMarkup.cBool) } @@ -109,6 +112,9 @@ public struct Banner: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The label to show on the button. diff --git a/Sources/Adwaita/View/Generated/Bin.swift b/Sources/Adwaita/View/Generated/Bin.swift index 4a5925b..9ec7935 100644 --- a/Sources/Adwaita/View/Generated/Bin.swift +++ b/Sources/Adwaita/View/Generated/Bin.swift @@ -2,7 +2,7 @@ // Bin.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -17,51 +17,54 @@ import LevenshteinTransformations /// /// It is useful for deriving subclasses, since it provides common code needed /// for handling a single child widget. -public struct Bin: Widget { +public struct Bin: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The child widget of the `AdwBin`. - var child: (() -> Body)? + var child: (() -> Body)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Bin`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_bin_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - adw_bin_set_child(storage.pointer?.cast(), childStorage.pointer?.cast()) + adw_bin_set_child(storage.opaquePointer?.cast(), childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } @@ -69,6 +72,9 @@ public struct Bin: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The child widget of the `AdwBin`. diff --git a/Sources/Adwaita/View/Generated/Box.swift b/Sources/Adwaita/View/Generated/Box.swift index 06e2002..23ea469 100644 --- a/Sources/Adwaita/View/Generated/Box.swift +++ b/Sources/Adwaita/View/Generated/Box.swift @@ -2,7 +2,7 @@ // Box.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -42,12 +42,12 @@ import LevenshteinTransformations /// Until GTK 4.10, `GtkBox` used the `GTK_ACCESSIBLE_ROLE_GROUP` role. /// /// Starting from GTK 4.12, `GtkBox` uses the `GTK_ACCESSIBLE_ROLE_GENERIC` role. -public struct Box: Widget { +public struct Box: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -64,55 +64,58 @@ public struct Box: Widget { /// The body for the widget "prepend". var prepend: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Box`. public init(spacing: Int) { self.spacing = spacing } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing.cInt)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) var appendStorage: [ViewStorage] = [] for view in append() { - appendStorage.append(view.storage(modifiers: modifiers)) - gtk_box_append(storage.pointer?.cast(), appendStorage.last?.pointer?.cast()) + appendStorage.append(view.storage(modifiers: modifiers, type: type)) + gtk_box_append(storage.opaquePointer?.cast(), appendStorage.last?.opaquePointer?.cast()) } storage.content["append"] = appendStorage var prependStorage: [ViewStorage] = [] for view in prepend() { - prependStorage.append(view.storage(modifiers: modifiers)) - gtk_box_prepend(storage.pointer?.cast(), prependStorage.last?.pointer?.cast()) + prependStorage.append(view.storage(modifiers: modifiers, type: type)) + gtk_box_prepend(storage.opaquePointer?.cast(), prependStorage.last?.opaquePointer?.cast()) } storage.content["prepend"] = prependStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let baselineChild, updateProperties { + if let baselineChild, updateProperties, (storage.previousState as? Self)?.baselineChild != baselineChild { gtk_box_set_baseline_child(widget?.cast(), baselineChild.cInt) } - if let homogeneous, updateProperties { + if let homogeneous, updateProperties, (storage.previousState as? Self)?.homogeneous != homogeneous { gtk_box_set_homogeneous(widget?.cast(), homogeneous.cBool) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.spacing != spacing { gtk_box_set_spacing(widget?.cast(), spacing.cInt) } @@ -122,7 +125,8 @@ public struct Box: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -133,7 +137,8 @@ public struct Box: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -143,6 +148,9 @@ public struct Box: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/Button.swift b/Sources/Adwaita/View/Generated/Button.swift index 5eea757..367f880 100644 --- a/Sources/Adwaita/View/Generated/Button.swift +++ b/Sources/Adwaita/View/Generated/Button.swift @@ -2,7 +2,7 @@ // Button.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -38,12 +38,12 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkButton` uses the %GTK_ACCESSIBLE_ROLE_BUTTON role. -public struct Button: Widget { +public struct Button: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -60,7 +60,7 @@ public struct Button: Widget { /// property has no effect. var canShrink: Bool? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// Whether the button has a frame. var hasFrame: Bool? /// The name of the icon used to automatically populate the button. @@ -81,37 +81,40 @@ public struct Button: Widget { /// Emitted when the button has been activated (pressed and released). var clicked: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Button`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_button_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast()) + gtk_button_set_child(storage.opaquePointer?.cast(), childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activate { storage.connectSignal(name: "activate", argCount: 0) { activate() @@ -124,25 +127,25 @@ public struct Button: Widget { } storage.modify { widget in - if let actionName, updateProperties { + if let actionName, updateProperties, (storage.previousState as? Self)?.actionName != actionName { gtk_actionable_set_action_name(widget, actionName) } - if let canShrink, updateProperties { + if let canShrink, updateProperties, (storage.previousState as? Self)?.canShrink != canShrink { gtk_button_set_can_shrink(widget?.cast(), canShrink.cBool) } if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let hasFrame, updateProperties { + if let hasFrame, updateProperties, (storage.previousState as? Self)?.hasFrame != hasFrame { gtk_button_set_has_frame(widget?.cast(), hasFrame.cBool) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { gtk_button_set_icon_name(widget?.cast(), iconName) } - if let label, storage.content["child"] == nil, updateProperties { + if let label, storage.content["child"] == nil, updateProperties, (storage.previousState as? Self)?.label != label { gtk_button_set_label(widget?.cast(), label) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { gtk_button_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -151,6 +154,9 @@ public struct Button: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/ButtonContent.swift b/Sources/Adwaita/View/Generated/ButtonContent.swift index 28f370a..d0a533a 100644 --- a/Sources/Adwaita/View/Generated/ButtonContent.swift +++ b/Sources/Adwaita/View/Generated/ButtonContent.swift @@ -2,7 +2,7 @@ // ButtonContent.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -44,12 +44,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwButtonContent` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct ButtonContent: Widget { +public struct ButtonContent: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether the button can be smaller than the natural size of its contents. /// @@ -70,45 +70,48 @@ public struct ButtonContent: Widget { /// See [property@ButtonContent:label]. var useUnderline: Bool? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ButtonContent`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_button_content_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let canShrink, updateProperties { + if let canShrink, updateProperties, (storage.previousState as? Self)?.canShrink != canShrink { adw_button_content_set_can_shrink(widget, canShrink.cBool) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { adw_button_content_set_icon_name(widget, iconName) } - if let label, updateProperties { + if let label, updateProperties, (storage.previousState as? Self)?.label != label { adw_button_content_set_label(widget, label) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_button_content_set_use_underline(widget, useUnderline.cBool) } @@ -117,6 +120,9 @@ public struct ButtonContent: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether the button can be smaller than the natural size of its contents. diff --git a/Sources/Adwaita/View/Generated/Carousel.swift b/Sources/Adwaita/View/Generated/Carousel.swift index 4d8b83c..6c06238 100644 --- a/Sources/Adwaita/View/Generated/Carousel.swift +++ b/Sources/Adwaita/View/Generated/Carousel.swift @@ -2,7 +2,7 @@ // Carousel.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -21,12 +21,12 @@ import LevenshteinTransformations /// ## CSS nodes /// /// `AdwCarousel` has a single CSS node with name `carousel`. -public struct Carousel: Widget where Element: Identifiable { +public struct Carousel: AdwaitaWidget where Element: Identifiable { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether to allow swiping for more than one page at a time. /// @@ -66,9 +66,9 @@ public struct Carousel: Widget where Element: Identifiable { /// The dynamic widget content. var content: (Element) -> Body /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Carousel`. public init(_ elements: [Element], @ViewBuilder content: @escaping (Element) -> Body) { @@ -76,25 +76,28 @@ public struct Carousel: Widget where Element: Identifiable { self.content = content } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_carousel_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let pageChanged { storage.connectSignal(name: "page-changed", argCount: 1) { pageChanged() @@ -102,22 +105,22 @@ public struct Carousel: Widget where Element: Identifiable { } storage.modify { widget in - if let allowLongSwipes, updateProperties { + if let allowLongSwipes, updateProperties, (storage.previousState as? Self)?.allowLongSwipes != allowLongSwipes { adw_carousel_set_allow_long_swipes(widget, allowLongSwipes.cBool) } - if let allowMouseDrag, updateProperties { + if let allowMouseDrag, updateProperties, (storage.previousState as? Self)?.allowMouseDrag != allowMouseDrag { adw_carousel_set_allow_mouse_drag(widget, allowMouseDrag.cBool) } - if let allowScrollWheel, updateProperties { + if let allowScrollWheel, updateProperties, (storage.previousState as? Self)?.allowScrollWheel != allowScrollWheel { adw_carousel_set_allow_scroll_wheel(widget, allowScrollWheel.cBool) } - if let interactive, updateProperties { + if let interactive, updateProperties, (storage.previousState as? Self)?.interactive != interactive { adw_carousel_set_interactive(widget, interactive.cBool) } - if let revealDuration, updateProperties { + if let revealDuration, updateProperties, (storage.previousState as? Self)?.revealDuration != revealDuration { adw_carousel_set_reveal_duration(widget, revealDuration.cInt) } - if let spacing, updateProperties { + if let spacing, updateProperties, (storage.previousState as? Self)?.spacing != spacing { adw_carousel_set_spacing(widget, spacing.cInt) } @@ -126,29 +129,32 @@ public struct Carousel: Widget where Element: Identifiable { old.identifiableTransform( to: elements, functions: .init { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) + let child = content(element).storage(modifiers: modifiers, type: type) adw_carousel_remove(widget, adw_carousel_get_nth_page(widget, UInt(index).cInt)) - adw_carousel_insert(widget, child.pointer?.cast(), index.cInt) + adw_carousel_insert(widget, child.opaquePointer?.cast(), index.cInt) contentStorage.remove(at: index) contentStorage.insert(child, at: index) } delete: { index in adw_carousel_remove(widget, adw_carousel_get_nth_page(widget, UInt(index).cInt)) contentStorage.remove(at: index) } insert: { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) - adw_carousel_insert(widget, child.pointer?.cast(), index.cInt) + let child = content(element).storage(modifiers: modifiers, type: type) + adw_carousel_insert(widget, child.opaquePointer?.cast(), index.cInt) contentStorage.insert(child, at: index) } ) storage.fields["element"] = elements storage.content[.mainContent] = contentStorage for (index, element) in elements.enumerated() { - content(element).widget(modifiers: modifiers).update(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties) + content(element).updateStorage(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties, type: type) } } for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether to allow swiping for more than one page at a time. diff --git a/Sources/Adwaita/View/Generated/CenterBox.swift b/Sources/Adwaita/View/Generated/CenterBox.swift index d8c679f..e0ccf4d 100644 --- a/Sources/Adwaita/View/Generated/CenterBox.swift +++ b/Sources/Adwaita/View/Generated/CenterBox.swift @@ -2,7 +2,7 @@ // CenterBox.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -42,25 +42,25 @@ import LevenshteinTransformations /// Until GTK 4.10, `GtkCenterBox` used the `GTK_ACCESSIBLE_ROLE_GROUP` role. /// /// Starting from GTK 4.12, `GtkCenterBox` uses the `GTK_ACCESSIBLE_ROLE_GENERIC` role. -public struct CenterBox: Widget { +public struct CenterBox: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. var accessibleRole: String? /// The widget that is placed at the center position. - var centerWidget: (() -> Body)? + var centerWidget: (() -> Body)? /// The widget that is placed at the end position. /// /// In vertical orientation, the end position is at the bottom. /// In horizontal orientation, the end position is at the trailing /// edge wrt. to the text direction. - var endWidget: (() -> Body)? + var endWidget: (() -> Body)? /// Whether to shrink the center widget after other children. /// /// By default, when there's no space to give all three children their @@ -75,60 +75,63 @@ public struct CenterBox: Widget { /// In vertical orientation, the start position is at the top. /// In horizontal orientation, the start position is at the leading /// edge wrt. to the text direction. - var startWidget: (() -> Body)? + var startWidget: (() -> Body)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `CenterBox`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_center_box_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let centerWidgetStorage = centerWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let centerWidgetStorage = centerWidget?().storage(modifiers: modifiers, type: type) { storage.content["centerWidget"] = [centerWidgetStorage] - gtk_center_box_set_center_widget(storage.pointer, centerWidgetStorage.pointer?.cast()) + gtk_center_box_set_center_widget(storage.opaquePointer, centerWidgetStorage.opaquePointer?.cast()) } - if let endWidgetStorage = endWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + if let endWidgetStorage = endWidget?().storage(modifiers: modifiers, type: type) { storage.content["endWidget"] = [endWidgetStorage] - gtk_center_box_set_end_widget(storage.pointer, endWidgetStorage.pointer?.cast()) + gtk_center_box_set_end_widget(storage.opaquePointer, endWidgetStorage.opaquePointer?.cast()) } - if let startWidgetStorage = startWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + if let startWidgetStorage = startWidget?().storage(modifiers: modifiers, type: type) { storage.content["startWidget"] = [startWidgetStorage] - gtk_center_box_set_start_widget(storage.pointer, startWidgetStorage.pointer?.cast()) + gtk_center_box_set_start_widget(storage.opaquePointer, startWidgetStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["centerWidget"]?.first { - centerWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + centerWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } if let widget = storage.content["endWidget"]?.first { - endWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + endWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let shrinkCenterLast, updateProperties { + if let shrinkCenterLast, updateProperties, (storage.previousState as? Self)?.shrinkCenterLast != shrinkCenterLast { gtk_center_box_set_shrink_center_last(widget, shrinkCenterLast.cBool) } if let widget = storage.content["startWidget"]?.first { - startWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + startWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } @@ -136,6 +139,9 @@ public struct CenterBox: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/CheckButton.swift b/Sources/Adwaita/View/Generated/CheckButton.swift index 0c3d3d9..325dbb0 100644 --- a/Sources/Adwaita/View/Generated/CheckButton.swift +++ b/Sources/Adwaita/View/Generated/CheckButton.swift @@ -2,7 +2,7 @@ // CheckButton.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -65,12 +65,12 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkCheckButton` uses the %GTK_ACCESSIBLE_ROLE_CHECKBOX role. -public struct CheckButton: Widget { +public struct CheckButton: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -84,7 +84,7 @@ public struct CheckButton: Widget { /// the check button and the indicator CSS node. var active: Binding? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// If the check button is in an “in between” state. /// /// The inconsistent state only affects visual appearance, @@ -110,37 +110,40 @@ public struct CheckButton: Widget { /// property changes. var toggled: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `CheckButton`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_check_button_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_check_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast()) + gtk_check_button_set_child(storage.opaquePointer?.cast(), childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activate { storage.connectSignal(name: "activate", argCount: 0) { activate() @@ -154,27 +157,27 @@ public struct CheckButton: Widget { storage.modify { widget in storage.notify(name: "active") { - let newValue = gtk_check_button_get_active(storage.pointer?.cast()) != 0 + let newValue = gtk_check_button_get_active(storage.opaquePointer?.cast()) != 0 if let active, newValue != active.wrappedValue { active.wrappedValue = newValue } } - if let actionName, updateProperties { + if let actionName, updateProperties, (storage.previousState as? Self)?.actionName != actionName { gtk_actionable_set_action_name(widget, actionName) } - if let active, updateProperties, (gtk_check_button_get_active(storage.pointer?.cast()) != 0) != active.wrappedValue { - gtk_check_button_set_active(storage.pointer?.cast(), active.wrappedValue.cBool) + if let active, updateProperties, (gtk_check_button_get_active(storage.opaquePointer?.cast()) != 0) != active.wrappedValue { + gtk_check_button_set_active(storage.opaquePointer?.cast(), active.wrappedValue.cBool) } if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let inconsistent, updateProperties { + if let inconsistent, updateProperties, (storage.previousState as? Self)?.inconsistent != inconsistent { gtk_check_button_set_inconsistent(widget?.cast(), inconsistent.cBool) } - if let label, storage.content["child"] == nil, updateProperties { + if let label, storage.content["child"] == nil, updateProperties, (storage.previousState as? Self)?.label != label { gtk_check_button_set_label(widget?.cast(), label) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { gtk_check_button_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -183,6 +186,9 @@ if let active, newValue != active.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/Clamp.swift b/Sources/Adwaita/View/Generated/Clamp.swift index 40d349b..c0639c4 100644 --- a/Sources/Adwaita/View/Generated/Clamp.swift +++ b/Sources/Adwaita/View/Generated/Clamp.swift @@ -2,7 +2,7 @@ // Clamp.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -26,15 +26,15 @@ import LevenshteinTransformations /// ## CSS nodes /// /// `AdwClamp` has a single CSS node with name `clamp`. -public struct Clamp: Widget { +public struct Clamp: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The child widget of the `AdwClamp`. - var child: (() -> Body)? + var child: (() -> Body)? /// The maximum size allocated to the child. /// /// It is the width if the clamp is horizontal, or the height if it is vertical. @@ -55,46 +55,49 @@ public struct Clamp: Widget { /// size makes transitions to and from the maximum size smoother when resizing. var tighteningThreshold: Int? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Clamp`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_clamp_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - adw_clamp_set_child(storage.pointer, childStorage.pointer?.cast()) + adw_clamp_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let maximumSize, updateProperties { + if let maximumSize, updateProperties, (storage.previousState as? Self)?.maximumSize != maximumSize { adw_clamp_set_maximum_size(widget, maximumSize.cInt) } - if let tighteningThreshold, updateProperties { + if let tighteningThreshold, updateProperties, (storage.previousState as? Self)?.tighteningThreshold != tighteningThreshold { adw_clamp_set_tightening_threshold(widget, tighteningThreshold.cInt) } @@ -103,6 +106,9 @@ public struct Clamp: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The child widget of the `AdwClamp`. diff --git a/Sources/Adwaita/View/Generated/ComboRow.swift b/Sources/Adwaita/View/Generated/ComboRow.swift index b07e99a..1af8125 100644 --- a/Sources/Adwaita/View/Generated/ComboRow.swift +++ b/Sources/Adwaita/View/Generated/ComboRow.swift @@ -2,7 +2,7 @@ // ComboRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -41,12 +41,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwComboRow` uses the `GTK_ACCESSIBLE_ROLE_COMBO_BOX` role. -public struct ComboRow: Widget { +public struct ComboRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The widget to activate when the row is activated. /// @@ -57,7 +57,7 @@ public struct ComboRow: Widget { /// /// The target widget will be activated by emitting the /// [signal@Gtk.Widget::mnemonic-activate] signal on it. - var activatableWidget: (() -> Body)? + var activatableWidget: (() -> Body)? /// Whether to show a search entry in the popup. /// /// If set to `TRUE`, a search entry will be shown in the popup that @@ -65,8 +65,6 @@ public struct ComboRow: Widget { /// /// Search requires [property@ComboRow:expression] to be set. var enableSearch: Bool? - /// The icon name for this row. - var iconName: String? /// The position of the selected item. /// /// If no item is selected, the property has the value @@ -124,49 +122,52 @@ public struct ComboRow: Widget { /// The body for the widget "prefix". var prefix: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ComboRow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_combo_row_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let activatableWidgetStorage = activatableWidget?().storage(modifiers: modifiers, type: type) { storage.content["activatableWidget"] = [activatableWidgetStorage] - adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast()) + adw_action_row_set_activatable_widget(storage.opaquePointer?.cast(), activatableWidgetStorage.opaquePointer?.cast()) } var suffixStorage: [ViewStorage] = [] for view in suffix() { - suffixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast()) + suffixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_suffix(storage.opaquePointer?.cast(), suffixStorage.last?.opaquePointer?.cast()) } storage.content["suffix"] = suffixStorage var prefixStorage: [ViewStorage] = [] for view in prefix() { - prefixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast()) + prefixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_prefix(storage.opaquePointer?.cast(), prefixStorage.last?.opaquePointer?.cast()) } storage.content["prefix"] = prefixStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activated { storage.connectSignal(name: "activated", argCount: 0) { activated() @@ -175,48 +176,45 @@ public struct ComboRow: Widget { storage.modify { widget in storage.notify(name: "selected") { - let newValue = UInt(adw_combo_row_get_selected(storage.pointer?.cast())) + let newValue = UInt(adw_combo_row_get_selected(storage.opaquePointer?.cast())) if let selected, newValue != selected.wrappedValue { selected.wrappedValue = newValue } } if let widget = storage.content["activatableWidget"]?.first { - activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + activatableWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let enableSearch, updateProperties { + if let enableSearch, updateProperties, (storage.previousState as? Self)?.enableSearch != enableSearch { adw_combo_row_set_enable_search(widget?.cast(), enableSearch.cBool) } - if let iconName, updateProperties { - adw_action_row_set_icon_name(widget?.cast(), iconName) - } - if let selected, updateProperties, (UInt(adw_combo_row_get_selected(storage.pointer?.cast()))) != selected.wrappedValue { - adw_combo_row_set_selected(storage.pointer?.cast(), selected.wrappedValue.cInt) + if let selected, updateProperties, (UInt(adw_combo_row_get_selected(storage.opaquePointer?.cast()))) != selected.wrappedValue { + adw_combo_row_set_selected(storage.opaquePointer?.cast(), selected.wrappedValue.cInt) } - if let subtitle, updateProperties { + if let subtitle, updateProperties, (storage.previousState as? Self)?.subtitle != subtitle { adw_action_row_set_subtitle(widget?.cast(), subtitle) } - if let subtitleLines, updateProperties { + if let subtitleLines, updateProperties, (storage.previousState as? Self)?.subtitleLines != subtitleLines { adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt) } - if let subtitleSelectable, updateProperties { + if let subtitleSelectable, updateProperties, (storage.previousState as? Self)?.subtitleSelectable != subtitleSelectable { adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let titleLines, updateProperties { + if let titleLines, updateProperties, (storage.previousState as? Self)?.titleLines != titleLines { adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useSubtitle, updateProperties { + if let useSubtitle, updateProperties, (storage.previousState as? Self)?.useSubtitle != useSubtitle { adw_combo_row_set_use_subtitle(widget?.cast(), useSubtitle.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -225,6 +223,9 @@ if let selected, newValue != selected.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The widget to activate when the row is activated. @@ -256,14 +257,6 @@ if let selected, newValue != selected.wrappedValue { return newSelf } - /// The icon name for this row. - public func iconName(_ iconName: String?) -> Self { - var newSelf = self - newSelf.iconName = iconName - - return newSelf - } - /// The position of the selected item. /// /// If no item is selected, the property has the value diff --git a/Sources/Adwaita/View/Generated/EntryRow.swift b/Sources/Adwaita/View/Generated/EntryRow.swift index 98716a7..d25ad59 100644 --- a/Sources/Adwaita/View/Generated/EntryRow.swift +++ b/Sources/Adwaita/View/Generated/EntryRow.swift @@ -2,7 +2,7 @@ // EntryRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -39,12 +39,12 @@ import LevenshteinTransformations /// /// `AdwEntryRow` has a single CSS node with name `row` and the `.entry` style /// class. -public struct EntryRow: Widget { +public struct EntryRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether activating the embedded entry can activate the default widget. var activatesDefault: Bool? @@ -92,45 +92,48 @@ public struct EntryRow: Widget { /// The body for the widget "prefix". var prefix: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `EntryRow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_entry_row_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) var suffixStorage: [ViewStorage] = [] for view in suffix() { - suffixStorage.append(view.storage(modifiers: modifiers)) - adw_entry_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast()) + suffixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_entry_row_add_suffix(storage.opaquePointer?.cast(), suffixStorage.last?.opaquePointer?.cast()) } storage.content["suffix"] = suffixStorage var prefixStorage: [ViewStorage] = [] for view in prefix() { - prefixStorage.append(view.storage(modifiers: modifiers)) - adw_entry_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast()) + prefixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_entry_row_add_prefix(storage.opaquePointer?.cast(), prefixStorage.last?.opaquePointer?.cast()) } storage.content["prefix"] = prefixStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let apply { storage.connectSignal(name: "apply", argCount: 0) { apply() @@ -143,25 +146,25 @@ public struct EntryRow: Widget { } storage.modify { widget in - if let activatesDefault, updateProperties { + if let activatesDefault, updateProperties, (storage.previousState as? Self)?.activatesDefault != activatesDefault { adw_entry_row_set_activates_default(widget?.cast(), activatesDefault.cBool) } - if let enableEmojiCompletion, updateProperties { + if let enableEmojiCompletion, updateProperties, (storage.previousState as? Self)?.enableEmojiCompletion != enableEmojiCompletion { adw_entry_row_set_enable_emoji_completion(widget?.cast(), enableEmojiCompletion.cBool) } - if let showApplyButton, updateProperties { + if let showApplyButton, updateProperties, (storage.previousState as? Self)?.showApplyButton != showApplyButton { adw_entry_row_set_show_apply_button(widget?.cast(), showApplyButton.cBool) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -171,7 +174,8 @@ public struct EntryRow: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -182,7 +186,8 @@ public struct EntryRow: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -192,6 +197,9 @@ public struct EntryRow: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether activating the embedded entry can activate the default widget. diff --git a/Sources/Adwaita/View/Generated/ExpanderRow.swift b/Sources/Adwaita/View/Generated/ExpanderRow.swift index 3fadaac..75e20a1 100644 --- a/Sources/Adwaita/View/Generated/ExpanderRow.swift +++ b/Sources/Adwaita/View/Generated/ExpanderRow.swift @@ -2,7 +2,7 @@ // ExpanderRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -33,19 +33,17 @@ import LevenshteinTransformations /// It contains the subnodes `row.header` for its main embedded row, /// `list.nested` for the list it can expand, and `image.expander-row-arrow` for /// its arrow. -public struct ExpanderRow: Widget { +public struct ExpanderRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether expansion is enabled. var enableExpansion: Binding? /// Whether the row is expanded. var expanded: Binding? - /// The icon name for this row. - var iconName: String? /// Whether the switch enabling the expansion is visible. var showEnableSwitch: Bool? /// The subtitle for this row. @@ -86,96 +84,96 @@ public struct ExpanderRow: Widget { /// The body for the widget "prefix". var prefix: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ExpanderRow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_expander_row_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) var rowsStorage: [ViewStorage] = [] for view in rows() { - rowsStorage.append(view.storage(modifiers: modifiers)) - adw_expander_row_add_row(storage.pointer?.cast(), rowsStorage.last?.pointer?.cast()) + rowsStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_expander_row_add_row(storage.opaquePointer?.cast(), rowsStorage.last?.opaquePointer?.cast()) } storage.content["rows"] = rowsStorage var suffixStorage: [ViewStorage] = [] for view in suffix() { - suffixStorage.append(view.storage(modifiers: modifiers)) - adw_expander_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast()) + suffixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_expander_row_add_suffix(storage.opaquePointer?.cast(), suffixStorage.last?.opaquePointer?.cast()) } storage.content["suffix"] = suffixStorage var prefixStorage: [ViewStorage] = [] for view in prefix() { - prefixStorage.append(view.storage(modifiers: modifiers)) - adw_expander_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast()) + prefixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_expander_row_add_prefix(storage.opaquePointer?.cast(), prefixStorage.last?.opaquePointer?.cast()) } storage.content["prefix"] = prefixStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in storage.notify(name: "enable-expansion") { - let newValue = adw_expander_row_get_enable_expansion(storage.pointer?.cast()) != 0 + let newValue = adw_expander_row_get_enable_expansion(storage.opaquePointer?.cast()) != 0 if let enableExpansion, newValue != enableExpansion.wrappedValue { enableExpansion.wrappedValue = newValue } } storage.notify(name: "expanded") { - let newValue = adw_expander_row_get_expanded(storage.pointer?.cast()) != 0 + let newValue = adw_expander_row_get_expanded(storage.opaquePointer?.cast()) != 0 if let expanded, newValue != expanded.wrappedValue { expanded.wrappedValue = newValue } } - if let enableExpansion, updateProperties, (adw_expander_row_get_enable_expansion(storage.pointer?.cast()) != 0) != enableExpansion.wrappedValue { - adw_expander_row_set_enable_expansion(storage.pointer?.cast(), enableExpansion.wrappedValue.cBool) - } - if let expanded, updateProperties, (adw_expander_row_get_expanded(storage.pointer?.cast()) != 0) != expanded.wrappedValue { - adw_expander_row_set_expanded(storage.pointer?.cast(), expanded.wrappedValue.cBool) + if let enableExpansion, updateProperties, (adw_expander_row_get_enable_expansion(storage.opaquePointer?.cast()) != 0) != enableExpansion.wrappedValue { + adw_expander_row_set_enable_expansion(storage.opaquePointer?.cast(), enableExpansion.wrappedValue.cBool) } - if let iconName, updateProperties { - adw_expander_row_set_icon_name(widget?.cast(), iconName) + if let expanded, updateProperties, (adw_expander_row_get_expanded(storage.opaquePointer?.cast()) != 0) != expanded.wrappedValue { + adw_expander_row_set_expanded(storage.opaquePointer?.cast(), expanded.wrappedValue.cBool) } - if let showEnableSwitch, updateProperties { + if let showEnableSwitch, updateProperties, (storage.previousState as? Self)?.showEnableSwitch != showEnableSwitch { adw_expander_row_set_show_enable_switch(widget?.cast(), showEnableSwitch.cBool) } - if let subtitle, updateProperties { + if let subtitle, updateProperties, (storage.previousState as? Self)?.subtitle != subtitle { adw_expander_row_set_subtitle(widget?.cast(), subtitle) } - if let subtitleLines, updateProperties { + if let subtitleLines, updateProperties, (storage.previousState as? Self)?.subtitleLines != subtitleLines { adw_expander_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let titleLines, updateProperties { + if let titleLines, updateProperties, (storage.previousState as? Self)?.titleLines != titleLines { adw_expander_row_set_title_lines(widget?.cast(), titleLines.cInt) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -185,7 +183,8 @@ if let expanded, newValue != expanded.wrappedValue { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -196,7 +195,8 @@ if let expanded, newValue != expanded.wrappedValue { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -207,7 +207,8 @@ if let expanded, newValue != expanded.wrappedValue { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -217,6 +218,9 @@ if let expanded, newValue != expanded.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether expansion is enabled. @@ -235,14 +239,6 @@ if let expanded, newValue != expanded.wrappedValue { return newSelf } - /// The icon name for this row. - public func iconName(_ iconName: String?) -> Self { - var newSelf = self - newSelf.iconName = iconName - - return newSelf - } - /// Whether the switch enabling the expansion is visible. public func showEnableSwitch(_ showEnableSwitch: Bool? = true) -> Self { var newSelf = self diff --git a/Sources/Adwaita/View/Generated/Fixed.swift b/Sources/Adwaita/View/Generated/Fixed.swift index 68d47c3..5c7be02 100644 --- a/Sources/Adwaita/View/Generated/Fixed.swift +++ b/Sources/Adwaita/View/Generated/Fixed.swift @@ -2,7 +2,7 @@ // Fixed.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -45,45 +45,48 @@ import LevenshteinTransformations /// If you know none of these things are an issue for your application, /// and prefer the simplicity of `GtkFixed`, by all means use the /// widget. But you should be aware of the tradeoffs. -public struct Fixed: Widget { +public struct Fixed: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. var accessibleRole: String? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Fixed`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_fixed_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in @@ -92,6 +95,9 @@ public struct Fixed: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/FlowBox.swift b/Sources/Adwaita/View/Generated/FlowBox.swift index 3b6a22b..b19a782 100644 --- a/Sources/Adwaita/View/Generated/FlowBox.swift +++ b/Sources/Adwaita/View/Generated/FlowBox.swift @@ -2,7 +2,7 @@ // FlowBox.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -52,12 +52,12 @@ import LevenshteinTransformations /// /// `GtkFlowBox` uses the %GTK_ACCESSIBLE_ROLE_GRID role, and `GtkFlowBoxChild` /// uses the %GTK_ACCESSIBLE_ROLE_GRID_CELL role. -public struct FlowBox: Widget where Element: Identifiable { +public struct FlowBox: AdwaitaWidget where Element: Identifiable { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// accept-unpaired-release var acceptUnpairedRelease: Bool? @@ -139,9 +139,9 @@ public struct FlowBox: Widget where Element: Identifiable { /// The dynamic widget content. var content: (Element) -> Body /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `FlowBox`. public init(_ elements: [Element], @ViewBuilder content: @escaping (Element) -> Body) { @@ -149,25 +149,28 @@ public struct FlowBox: Widget where Element: Identifiable { self.content = content } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_flow_box_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activateCursorChild { storage.connectSignal(name: "activate-cursor-child", argCount: 0) { activateCursorChild() @@ -205,22 +208,22 @@ public struct FlowBox: Widget where Element: Identifiable { } storage.modify { widget in - if let activateOnSingleClick, updateProperties { + if let activateOnSingleClick, updateProperties, (storage.previousState as? Self)?.activateOnSingleClick != activateOnSingleClick { gtk_flow_box_set_activate_on_single_click(widget, activateOnSingleClick.cBool) } - if let columnSpacing, updateProperties { + if let columnSpacing, updateProperties, (storage.previousState as? Self)?.columnSpacing != columnSpacing { gtk_flow_box_set_column_spacing(widget, columnSpacing.cInt) } - if let homogeneous, updateProperties { + if let homogeneous, updateProperties, (storage.previousState as? Self)?.homogeneous != homogeneous { gtk_flow_box_set_homogeneous(widget, homogeneous.cBool) } - if let maxChildrenPerLine, updateProperties { + if let maxChildrenPerLine, updateProperties, (storage.previousState as? Self)?.maxChildrenPerLine != maxChildrenPerLine { gtk_flow_box_set_max_children_per_line(widget, maxChildrenPerLine.cInt) } - if let minChildrenPerLine, updateProperties { + if let minChildrenPerLine, updateProperties, (storage.previousState as? Self)?.minChildrenPerLine != minChildrenPerLine { gtk_flow_box_set_min_children_per_line(widget, minChildrenPerLine.cInt) } - if let rowSpacing, updateProperties { + if let rowSpacing, updateProperties, (storage.previousState as? Self)?.rowSpacing != rowSpacing { gtk_flow_box_set_row_spacing(widget, rowSpacing.cInt) } @@ -229,29 +232,32 @@ public struct FlowBox: Widget where Element: Identifiable { old.identifiableTransform( to: elements, functions: .init { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) + let child = content(element).storage(modifiers: modifiers, type: type) gtk_flow_box_remove(widget, gtk_flow_box_get_child_at_index(widget, index.cInt)?.cast()) - gtk_flow_box_insert(widget, child.pointer?.cast(), index.cInt) + gtk_flow_box_insert(widget, child.opaquePointer?.cast(), index.cInt) contentStorage.remove(at: index) contentStorage.insert(child, at: index) } delete: { index in gtk_flow_box_remove(widget, gtk_flow_box_get_child_at_index(widget, index.cInt)?.cast()) contentStorage.remove(at: index) } insert: { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) - gtk_flow_box_insert(widget, child.pointer?.cast(), index.cInt) + let child = content(element).storage(modifiers: modifiers, type: type) + gtk_flow_box_insert(widget, child.opaquePointer?.cast(), index.cInt) contentStorage.insert(child, at: index) } ) storage.fields["element"] = elements storage.content[.mainContent] = contentStorage for (index, element) in elements.enumerated() { - content(element).widget(modifiers: modifiers).update(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties) + content(element).updateStorage(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties, type: type) } } for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// accept-unpaired-release diff --git a/Sources/Adwaita/View/Generated/HeaderBar.swift b/Sources/Adwaita/View/Generated/HeaderBar.swift index fc5618c..9113531 100644 --- a/Sources/Adwaita/View/Generated/HeaderBar.swift +++ b/Sources/Adwaita/View/Generated/HeaderBar.swift @@ -2,7 +2,7 @@ // HeaderBar.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -92,12 +92,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwHeaderBar` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct HeaderBar: Widget { +public struct HeaderBar: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The decoration layout for buttons. /// @@ -147,74 +147,77 @@ public struct HeaderBar: Widget { /// ```xml /// Title /// ``` - var titleWidget: (() -> Body)? + var titleWidget: (() -> Body)? /// The body for the widget "start". var start: () -> Body = { [] } /// The body for the widget "end". var end: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `HeaderBar`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_header_bar_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let titleWidgetStorage = titleWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let titleWidgetStorage = titleWidget?().storage(modifiers: modifiers, type: type) { storage.content["titleWidget"] = [titleWidgetStorage] - adw_header_bar_set_title_widget(storage.pointer, titleWidgetStorage.pointer?.cast()) + adw_header_bar_set_title_widget(storage.opaquePointer, titleWidgetStorage.opaquePointer?.cast()) } var startStorage: [ViewStorage] = [] for view in start() { - startStorage.append(view.storage(modifiers: modifiers)) - adw_header_bar_pack_start(storage.pointer, startStorage.last?.pointer?.cast()) + startStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_header_bar_pack_start(storage.opaquePointer, startStorage.last?.opaquePointer?.cast()) } storage.content["start"] = startStorage var endStorage: [ViewStorage] = [] for view in end() { - endStorage.append(view.storage(modifiers: modifiers)) - adw_header_bar_pack_end(storage.pointer, endStorage.last?.pointer?.cast()) + endStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_header_bar_pack_end(storage.opaquePointer, endStorage.last?.opaquePointer?.cast()) } storage.content["end"] = endStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let decorationLayout, updateProperties { + if let decorationLayout, updateProperties, (storage.previousState as? Self)?.decorationLayout != decorationLayout { adw_header_bar_set_decoration_layout(widget, decorationLayout) } - if let showBackButton, updateProperties { + if let showBackButton, updateProperties, (storage.previousState as? Self)?.showBackButton != showBackButton { adw_header_bar_set_show_back_button(widget, showBackButton.cBool) } - if let showEndTitleButtons, updateProperties { + if let showEndTitleButtons, updateProperties, (storage.previousState as? Self)?.showEndTitleButtons != showEndTitleButtons { adw_header_bar_set_show_end_title_buttons(widget, showEndTitleButtons.cBool) } - if let showStartTitleButtons, updateProperties { + if let showStartTitleButtons, updateProperties, (storage.previousState as? Self)?.showStartTitleButtons != showStartTitleButtons { adw_header_bar_set_show_start_title_buttons(widget, showStartTitleButtons.cBool) } - if let showTitle, updateProperties { + if let showTitle, updateProperties, (storage.previousState as? Self)?.showTitle != showTitle { adw_header_bar_set_show_title(widget, showTitle.cBool) } if let widget = storage.content["titleWidget"]?.first { - titleWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + titleWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } if let startStorage = storage.content["start"] { @@ -223,7 +226,8 @@ public struct HeaderBar: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -234,7 +238,8 @@ public struct HeaderBar: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -244,6 +249,9 @@ public struct HeaderBar: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The decoration layout for buttons. diff --git a/Sources/Adwaita/View/Generated/Label.swift b/Sources/Adwaita/View/Generated/Label.swift index 607252b..4c15133 100644 --- a/Sources/Adwaita/View/Generated/Label.swift +++ b/Sources/Adwaita/View/Generated/Label.swift @@ -2,7 +2,7 @@ // Label.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -184,12 +184,12 @@ import LevenshteinTransformations /// It is possible to implement custom handling for links and their tooltips /// with the [signal@Gtk.Label::activate-link] signal and the /// [method@Gtk.Label.get_current_uri] function. -public struct Label: Widget { +public struct Label: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -225,7 +225,7 @@ public struct Label: Widget { /// The mnemonic accelerator key for the label. var mnemonicKeyval: UInt? /// The widget to be activated when the labels mnemonic key is pressed. - var mnemonicWidget: (() -> Body)? + var mnemonicWidget: (() -> Body)? /// Whether the label text can be selected with the mouse. var selectable: Bool? /// Whether the label is in single line mode. @@ -269,38 +269,41 @@ public struct Label: Widget { /// The default binding for this signal is Ctrl+c. var copyClipboard: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Label`. public init(label: String) { self.label = label } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_label_new(label)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let mnemonicWidgetStorage = mnemonicWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let mnemonicWidgetStorage = mnemonicWidget?().storage(modifiers: modifiers, type: type) { storage.content["mnemonicWidget"] = [mnemonicWidgetStorage] - gtk_label_set_mnemonic_widget(storage.pointer, mnemonicWidgetStorage.pointer?.cast()) + gtk_label_set_mnemonic_widget(storage.opaquePointer, mnemonicWidgetStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let copyClipboard { storage.connectSignal(name: "copy-clipboard", argCount: 0) { copyClipboard() @@ -308,40 +311,40 @@ public struct Label: Widget { } storage.modify { widget in - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.label != label { gtk_label_set_label(widget, label) } - if let lines, updateProperties { + if let lines, updateProperties, (storage.previousState as? Self)?.lines != lines { gtk_label_set_lines(widget, lines.cInt) } - if let maxWidthChars, updateProperties { + if let maxWidthChars, updateProperties, (storage.previousState as? Self)?.maxWidthChars != maxWidthChars { gtk_label_set_max_width_chars(widget, maxWidthChars.cInt) } if let widget = storage.content["mnemonicWidget"]?.first { - mnemonicWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + mnemonicWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let selectable, updateProperties { + if let selectable, updateProperties, (storage.previousState as? Self)?.selectable != selectable { gtk_label_set_selectable(widget, selectable.cBool) } - if let singleLineMode, updateProperties { + if let singleLineMode, updateProperties, (storage.previousState as? Self)?.singleLineMode != singleLineMode { gtk_label_set_single_line_mode(widget, singleLineMode.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { gtk_label_set_use_markup(widget, useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { gtk_label_set_use_underline(widget, useUnderline.cBool) } - if let widthChars, updateProperties { + if let widthChars, updateProperties, (storage.previousState as? Self)?.widthChars != widthChars { gtk_label_set_width_chars(widget, widthChars.cInt) } - if let wrap, updateProperties { + if let wrap, updateProperties, (storage.previousState as? Self)?.wrap != wrap { gtk_label_set_wrap(widget, wrap.cBool) } - if let xalign, updateProperties { + if let xalign, updateProperties, (storage.previousState as? Self)?.xalign != xalign { gtk_label_set_xalign(widget, xalign) } - if let yalign, updateProperties { + if let yalign, updateProperties, (storage.previousState as? Self)?.yalign != yalign { gtk_label_set_yalign(widget, yalign) } @@ -350,6 +353,9 @@ public struct Label: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/LevelBar.swift b/Sources/Adwaita/View/Generated/LevelBar.swift index 9daeb64..cbabfd4 100644 --- a/Sources/Adwaita/View/Generated/LevelBar.swift +++ b/Sources/Adwaita/View/Generated/LevelBar.swift @@ -2,7 +2,7 @@ // LevelBar.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -105,12 +105,12 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkLevelBar` uses the %GTK_ACCESSIBLE_ROLE_METER role. -public struct LevelBar: Widget { +public struct LevelBar: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -137,33 +137,36 @@ public struct LevelBar: Widget { /// the value of offset "x" changes. var offsetChanged: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `LevelBar`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_level_bar_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let offsetChanged { storage.connectSignal(name: "offset-changed", argCount: 1) { offsetChanged() @@ -171,16 +174,16 @@ public struct LevelBar: Widget { } storage.modify { widget in - if let inverted, updateProperties { + if let inverted, updateProperties, (storage.previousState as? Self)?.inverted != inverted { gtk_level_bar_set_inverted(widget, inverted.cBool) } - if let maxValue, updateProperties { + if let maxValue, updateProperties, (storage.previousState as? Self)?.maxValue != maxValue { gtk_level_bar_set_max_value(widget, maxValue) } - if let minValue, updateProperties { + if let minValue, updateProperties, (storage.previousState as? Self)?.minValue != minValue { gtk_level_bar_set_min_value(widget, minValue) } - if let value, updateProperties { + if let value, updateProperties, (storage.previousState as? Self)?.value != value { gtk_level_bar_set_value(widget, value) } @@ -189,6 +192,9 @@ public struct LevelBar: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/LinkButton.swift b/Sources/Adwaita/View/Generated/LinkButton.swift index 03fffbf..ad0ad50 100644 --- a/Sources/Adwaita/View/Generated/LinkButton.swift +++ b/Sources/Adwaita/View/Generated/LinkButton.swift @@ -2,7 +2,7 @@ // LinkButton.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -34,12 +34,12 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkLinkButton` uses the %GTK_ACCESSIBLE_ROLE_LINK role. -public struct LinkButton: Widget { +public struct LinkButton: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -56,7 +56,7 @@ public struct LinkButton: Widget { /// property has no effect. var canShrink: Bool? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// Whether the button has a frame. var hasFrame: Bool? /// The name of the icon used to automatically populate the button. @@ -83,38 +83,41 @@ public struct LinkButton: Widget { /// Emitted when the button has been activated (pressed and released). var clicked: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `LinkButton`. public init(uri: String) { self.uri = uri } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_link_button_new(uri)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast()) + gtk_button_set_child(storage.opaquePointer?.cast(), childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activate { storage.connectSignal(name: "activate", argCount: 0) { activate() @@ -127,31 +130,31 @@ public struct LinkButton: Widget { } storage.modify { widget in - if let actionName, updateProperties { + if let actionName, updateProperties, (storage.previousState as? Self)?.actionName != actionName { gtk_actionable_set_action_name(widget, actionName) } - if let canShrink, updateProperties { + if let canShrink, updateProperties, (storage.previousState as? Self)?.canShrink != canShrink { gtk_button_set_can_shrink(widget?.cast(), canShrink.cBool) } if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let hasFrame, updateProperties { + if let hasFrame, updateProperties, (storage.previousState as? Self)?.hasFrame != hasFrame { gtk_button_set_has_frame(widget?.cast(), hasFrame.cBool) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { gtk_button_set_icon_name(widget?.cast(), iconName) } - if let label, storage.content["child"] == nil, updateProperties { + if let label, storage.content["child"] == nil, updateProperties, (storage.previousState as? Self)?.label != label { gtk_button_set_label(widget?.cast(), label) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.uri != uri { gtk_link_button_set_uri(widget, uri) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { gtk_button_set_use_underline(widget?.cast(), useUnderline.cBool) } - if let visited, updateProperties { + if let visited, updateProperties, (storage.previousState as? Self)?.visited != visited { gtk_link_button_set_visited(widget, visited.cBool) } @@ -160,6 +163,9 @@ public struct LinkButton: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/ListBox.swift b/Sources/Adwaita/View/Generated/ListBox.swift index 41b4b57..e073e23 100644 --- a/Sources/Adwaita/View/Generated/ListBox.swift +++ b/Sources/Adwaita/View/Generated/ListBox.swift @@ -2,7 +2,7 @@ // ListBox.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -61,12 +61,12 @@ import LevenshteinTransformations /// /// `GtkListBox` uses the %GTK_ACCESSIBLE_ROLE_LIST role and `GtkListBoxRow` uses /// the %GTK_ACCESSIBLE_ROLE_LIST_ITEM role. -public struct ListBox: Widget where Element: Identifiable { +public struct ListBox: AdwaitaWidget where Element: Identifiable { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether to accept unpaired release events. var acceptUnpairedRelease: Bool? @@ -116,9 +116,9 @@ public struct ListBox: Widget where Element: Identifiable { /// The dynamic widget content. var content: (Element) -> Body /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ListBox`. public init(_ elements: [Element], @ViewBuilder content: @escaping (Element) -> Body) { @@ -126,25 +126,28 @@ public struct ListBox: Widget where Element: Identifiable { self.content = content } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_list_box_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activateCursorRow { storage.connectSignal(name: "activate-cursor-row", argCount: 0) { activateCursorRow() @@ -187,10 +190,10 @@ public struct ListBox: Widget where Element: Identifiable { } storage.modify { widget in - if let activateOnSingleClick, updateProperties { + if let activateOnSingleClick, updateProperties, (storage.previousState as? Self)?.activateOnSingleClick != activateOnSingleClick { gtk_list_box_set_activate_on_single_click(widget, activateOnSingleClick.cBool) } - if let showSeparators, updateProperties { + if let showSeparators, updateProperties, (storage.previousState as? Self)?.showSeparators != showSeparators { gtk_list_box_set_show_separators(widget, showSeparators.cBool) } @@ -199,29 +202,32 @@ public struct ListBox: Widget where Element: Identifiable { old.identifiableTransform( to: elements, functions: .init { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) + let child = content(element).storage(modifiers: modifiers, type: type) gtk_list_box_remove(widget, gtk_list_box_get_row_at_index(widget, index.cInt)?.cast()) - gtk_list_box_insert(widget, child.pointer?.cast(), index.cInt) + gtk_list_box_insert(widget, child.opaquePointer?.cast(), index.cInt) contentStorage.remove(at: index) contentStorage.insert(child, at: index) } delete: { index in gtk_list_box_remove(widget, gtk_list_box_get_row_at_index(widget, index.cInt)?.cast()) contentStorage.remove(at: index) } insert: { index, element in - let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) - gtk_list_box_insert(widget, child.pointer?.cast(), index.cInt) + let child = content(element).storage(modifiers: modifiers, type: type) + gtk_list_box_insert(widget, child.opaquePointer?.cast(), index.cInt) contentStorage.insert(child, at: index) } ) storage.fields["element"] = elements storage.content[.mainContent] = contentStorage for (index, element) in elements.enumerated() { - content(element).widget(modifiers: modifiers).update(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties) + content(element).updateStorage(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties, type: type) } } for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether to accept unpaired release events. diff --git a/Sources/Adwaita/View/Generated/Menu.swift b/Sources/Adwaita/View/Generated/Menu.swift index 8278258..8ac6c70 100644 --- a/Sources/Adwaita/View/Generated/Menu.swift +++ b/Sources/Adwaita/View/Generated/Menu.swift @@ -2,7 +2,7 @@ // Menu.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -69,12 +69,12 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkMenuButton` uses the %GTK_ACCESSIBLE_ROLE_BUTTON role. -public struct Menu: Widget { +public struct Menu: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -88,7 +88,7 @@ public struct Menu: Widget { /// size of its contents. var canShrink: Bool? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// Whether the button has a frame. var hasFrame: Bool? /// The name of the icon used to automatically populate the button. @@ -99,7 +99,7 @@ public struct Menu: Widget { /// /// See [method@Gtk.MenuButton.set_menu_model] for the interaction /// with the [property@Gtk.MenuButton:popover] property. - var menuModel: (() -> MenuContent)? + var menuModel: (() -> Body)? /// Whether the menu button acts as a primary menu. /// /// Primary menus can be opened using the F10 key @@ -112,44 +112,45 @@ public struct Menu: Widget { /// emitting it causes the button to pop up its menu. var activate: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Menu`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_menu_button_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_menu_button_set_child(storage.pointer, childStorage.pointer?.cast()) + gtk_menu_button_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } - if let declarative = menuModel?(), let app { - let menu = g_menu_new() - gtk_menu_button_set_menu_model(storage.pointer, menu?.cast()) - for item in declarative { - item.addMenuItems(menu: menu, app: app, window: window) - } + if let menu = menuModel?(), let app { + let childStorage = MenuCollection { menu }.getMenu(app: app, window: window) + storage.content["menuModel"] = [childStorage] + gtk_menu_button_set_menu_model(storage.opaquePointer, childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activate { storage.connectSignal(name: "activate", argCount: 0) { activate() @@ -158,36 +159,40 @@ public struct Menu: Widget { storage.modify { widget in storage.notify(name: "active") { - let newValue = gtk_menu_button_get_active(storage.pointer) != 0 + let newValue = gtk_menu_button_get_active(storage.opaquePointer) != 0 if let active, newValue != active.wrappedValue { active.wrappedValue = newValue } } - if let active, updateProperties, (gtk_menu_button_get_active(storage.pointer) != 0) != active.wrappedValue { - gtk_menu_button_set_active(storage.pointer, active.wrappedValue.cBool) + if let active, updateProperties, (gtk_menu_button_get_active(storage.opaquePointer) != 0) != active.wrappedValue { + gtk_menu_button_set_active(storage.opaquePointer, active.wrappedValue.cBool) } - if let alwaysShowArrow, updateProperties { + if let alwaysShowArrow, updateProperties, (storage.previousState as? Self)?.alwaysShowArrow != alwaysShowArrow { gtk_menu_button_set_always_show_arrow(widget, alwaysShowArrow.cBool) } - if let canShrink, updateProperties { + if let canShrink, updateProperties, (storage.previousState as? Self)?.canShrink != canShrink { gtk_menu_button_set_can_shrink(widget, canShrink.cBool) } if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let hasFrame, updateProperties { + if let hasFrame, updateProperties, (storage.previousState as? Self)?.hasFrame != hasFrame { gtk_menu_button_set_has_frame(widget, hasFrame.cBool) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { gtk_menu_button_set_icon_name(widget, iconName) } - if let label, storage.content["child"] == nil, updateProperties { + if let label, storage.content["child"] == nil, updateProperties, (storage.previousState as? Self)?.label != label { gtk_menu_button_set_label(widget, label) } - if let primary, updateProperties { + if let menu = storage.content["menuModel"]?.first { + MenuCollection { menuModel?() ?? [] } + .updateStorage(menu, modifiers: [], updateProperties: updateProperties, type: MenuContext.self) + } + if let primary, updateProperties, (storage.previousState as? Self)?.primary != primary { gtk_menu_button_set_primary(widget, primary.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { gtk_menu_button_set_use_underline(widget, useUnderline.cBool) } @@ -196,6 +201,9 @@ if let active, newValue != active.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. @@ -269,7 +277,7 @@ if let active, newValue != active.wrappedValue { /// /// See [method@Gtk.MenuButton.set_menu_model] for the interaction /// with the [property@Gtk.MenuButton:popover] property. - public func menuModel(app: GTUIApp, window: GTUIApplicationWindow? = nil, @MenuBuilder _ menuModel: @escaping (() -> MenuContent)) -> Self { + public func menuModel(app: AdwaitaApp, window: AdwaitaWindow? = nil, @ViewBuilder _ menuModel: @escaping (() -> Body)) -> Self { var newSelf = self newSelf.menuModel = menuModel newSelf.app = app; newSelf.window = window diff --git a/Sources/Adwaita/View/Generated/NavigationView.swift b/Sources/Adwaita/View/Generated/NavigationView.swift index 9733dc0..42bcecf 100644 --- a/Sources/Adwaita/View/Generated/NavigationView.swift +++ b/Sources/Adwaita/View/Generated/NavigationView.swift @@ -2,7 +2,7 @@ // NavigationView.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -118,12 +118,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwNavigationView` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct NavigationView: Widget { +public struct NavigationView: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether to animate page transitions. /// @@ -163,33 +163,36 @@ public struct NavigationView: Widget { /// See [method@NavigationView.replace]. var replaced: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `NavigationView`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_navigation_view_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let getNextPage { storage.connectSignal(name: "get-next-page", argCount: 0) { getNextPage() @@ -212,10 +215,10 @@ public struct NavigationView: Widget { } storage.modify { widget in - if let animateTransitions, updateProperties { + if let animateTransitions, updateProperties, (storage.previousState as? Self)?.animateTransitions != animateTransitions { adw_navigation_view_set_animate_transitions(widget, animateTransitions.cBool) } - if let popOnEscape, updateProperties { + if let popOnEscape, updateProperties, (storage.previousState as? Self)?.popOnEscape != popOnEscape { adw_navigation_view_set_pop_on_escape(widget, popOnEscape.cBool) } @@ -224,6 +227,9 @@ public struct NavigationView: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether to animate page transitions. diff --git a/Sources/Adwaita/View/Generated/Overlay.swift b/Sources/Adwaita/View/Generated/Overlay.swift index e7b990c..591bdf7 100644 --- a/Sources/Adwaita/View/Generated/Overlay.swift +++ b/Sources/Adwaita/View/Generated/Overlay.swift @@ -2,7 +2,7 @@ // Overlay.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -40,19 +40,19 @@ import LevenshteinTransformations /// `GtkOverlay` has a single CSS node with the name “overlay”. Overlay children /// whose alignments cause them to be positioned at an edge get the style classes /// “.left”, “.right”, “.top”, and/or “.bottom” according to their position. -public struct Overlay: Widget { +public struct Overlay: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. var accessibleRole: String? /// The main child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// Emitted to determine the position and size of any overlay /// child widgets. /// @@ -71,43 +71,46 @@ public struct Overlay: Widget { /// The body for the widget "overlay". var overlay: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Overlay`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_overlay_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_overlay_set_child(storage.pointer, childStorage.pointer?.cast()) + gtk_overlay_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } var overlayStorage: [ViewStorage] = [] for view in overlay() { - overlayStorage.append(view.storage(modifiers: modifiers)) - gtk_overlay_add_overlay(storage.pointer, overlayStorage.last?.pointer?.cast()) + overlayStorage.append(view.storage(modifiers: modifiers, type: type)) + gtk_overlay_add_overlay(storage.opaquePointer, overlayStorage.last?.opaquePointer?.cast()) } storage.content["overlay"] = overlayStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let getChildPosition { storage.connectSignal(name: "get-child-position", argCount: 2) { getChildPosition() @@ -116,7 +119,7 @@ public struct Overlay: Widget { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } if let overlayStorage = storage.content["overlay"] { @@ -125,7 +128,8 @@ public struct Overlay: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -135,6 +139,9 @@ public struct Overlay: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/OverlaySplitView.swift b/Sources/Adwaita/View/Generated/OverlaySplitView.swift index 3c4f7cf..126a5f5 100644 --- a/Sources/Adwaita/View/Generated/OverlaySplitView.swift +++ b/Sources/Adwaita/View/Generated/OverlaySplitView.swift @@ -2,7 +2,7 @@ // OverlaySplitView.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -113,12 +113,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwOverlaySplitView` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct OverlaySplitView: Widget { +public struct OverlaySplitView: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether the split view is collapsed. /// @@ -126,7 +126,7 @@ public struct OverlaySplitView: Widget { /// content widget, otherwise they are displayed side by side. var collapsed: Bool? /// The content widget. - var content: (() -> Body)? + var content: (() -> Body)? /// Whether the sidebar can be closed with a swipe gesture. /// /// Only touchscreen swipes are supported. @@ -160,7 +160,7 @@ public struct OverlaySplitView: Widget { /// Whether the sidebar widget is shown. var showSidebar: Binding? /// The sidebar widget. - var sidebar: (() -> Body)? + var sidebar: (() -> Body)? /// The preferred sidebar width as a fraction of the total width. /// /// The preferred width is additionally limited by @@ -171,77 +171,80 @@ public struct OverlaySplitView: Widget { /// width exceeds the preferred width. var sidebarWidthFraction: Double? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `OverlaySplitView`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_overlay_split_view_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let contentStorage = content?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let contentStorage = content?().storage(modifiers: modifiers, type: type) { storage.content["content"] = [contentStorage] - adw_overlay_split_view_set_content(storage.pointer, contentStorage.pointer?.cast()) + adw_overlay_split_view_set_content(storage.opaquePointer, contentStorage.opaquePointer?.cast()) } - if let sidebarStorage = sidebar?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + if let sidebarStorage = sidebar?().storage(modifiers: modifiers, type: type) { storage.content["sidebar"] = [sidebarStorage] - adw_overlay_split_view_set_sidebar(storage.pointer, sidebarStorage.pointer?.cast()) + adw_overlay_split_view_set_sidebar(storage.opaquePointer, sidebarStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in storage.notify(name: "show-sidebar") { - let newValue = adw_overlay_split_view_get_show_sidebar(storage.pointer) != 0 + let newValue = adw_overlay_split_view_get_show_sidebar(storage.opaquePointer) != 0 if let showSidebar, newValue != showSidebar.wrappedValue { showSidebar.wrappedValue = newValue } } - if let collapsed, updateProperties { + if let collapsed, updateProperties, (storage.previousState as? Self)?.collapsed != collapsed { adw_overlay_split_view_set_collapsed(widget, collapsed.cBool) } if let widget = storage.content["content"]?.first { - content?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + content?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let enableHideGesture, updateProperties { + if let enableHideGesture, updateProperties, (storage.previousState as? Self)?.enableHideGesture != enableHideGesture { adw_overlay_split_view_set_enable_hide_gesture(widget, enableHideGesture.cBool) } - if let enableShowGesture, updateProperties { + if let enableShowGesture, updateProperties, (storage.previousState as? Self)?.enableShowGesture != enableShowGesture { adw_overlay_split_view_set_enable_show_gesture(widget, enableShowGesture.cBool) } - if let maxSidebarWidth, updateProperties { + if let maxSidebarWidth, updateProperties, (storage.previousState as? Self)?.maxSidebarWidth != maxSidebarWidth { adw_overlay_split_view_set_max_sidebar_width(widget, maxSidebarWidth) } - if let minSidebarWidth, updateProperties { + if let minSidebarWidth, updateProperties, (storage.previousState as? Self)?.minSidebarWidth != minSidebarWidth { adw_overlay_split_view_set_min_sidebar_width(widget, minSidebarWidth) } - if let pinSidebar, updateProperties { + if let pinSidebar, updateProperties, (storage.previousState as? Self)?.pinSidebar != pinSidebar { adw_overlay_split_view_set_pin_sidebar(widget, pinSidebar.cBool) } - if let showSidebar, updateProperties, (adw_overlay_split_view_get_show_sidebar(storage.pointer) != 0) != showSidebar.wrappedValue { - adw_overlay_split_view_set_show_sidebar(storage.pointer, showSidebar.wrappedValue.cBool) + if let showSidebar, updateProperties, (adw_overlay_split_view_get_show_sidebar(storage.opaquePointer) != 0) != showSidebar.wrappedValue { + adw_overlay_split_view_set_show_sidebar(storage.opaquePointer, showSidebar.wrappedValue.cBool) } if let widget = storage.content["sidebar"]?.first { - sidebar?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + sidebar?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let sidebarWidthFraction, updateProperties { + if let sidebarWidthFraction, updateProperties, (storage.previousState as? Self)?.sidebarWidthFraction != sidebarWidthFraction { adw_overlay_split_view_set_sidebar_width_fraction(widget, sidebarWidthFraction) } @@ -250,6 +253,9 @@ if let showSidebar, newValue != showSidebar.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether the split view is collapsed. diff --git a/Sources/Adwaita/View/Generated/PasswordEntryRow.swift b/Sources/Adwaita/View/Generated/PasswordEntryRow.swift index 52d33e7..fb2c4c2 100644 --- a/Sources/Adwaita/View/Generated/PasswordEntryRow.swift +++ b/Sources/Adwaita/View/Generated/PasswordEntryRow.swift @@ -2,7 +2,7 @@ // PasswordEntryRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -24,12 +24,12 @@ import LevenshteinTransformations /// /// `AdwPasswordEntryRow` has a single CSS node with name `row` that carries /// `.entry` and `.password` style classes. -public struct PasswordEntryRow: Widget { +public struct PasswordEntryRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether activating the embedded entry can activate the default widget. var activatesDefault: Bool? @@ -77,45 +77,48 @@ public struct PasswordEntryRow: Widget { /// The body for the widget "prefix". var prefix: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `PasswordEntryRow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_password_entry_row_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) var suffixStorage: [ViewStorage] = [] for view in suffix() { - suffixStorage.append(view.storage(modifiers: modifiers)) - adw_entry_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast()) + suffixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_entry_row_add_suffix(storage.opaquePointer?.cast(), suffixStorage.last?.opaquePointer?.cast()) } storage.content["suffix"] = suffixStorage var prefixStorage: [ViewStorage] = [] for view in prefix() { - prefixStorage.append(view.storage(modifiers: modifiers)) - adw_entry_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast()) + prefixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_entry_row_add_prefix(storage.opaquePointer?.cast(), prefixStorage.last?.opaquePointer?.cast()) } storage.content["prefix"] = prefixStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let apply { storage.connectSignal(name: "apply", argCount: 0) { apply() @@ -128,25 +131,25 @@ public struct PasswordEntryRow: Widget { } storage.modify { widget in - if let activatesDefault, updateProperties { + if let activatesDefault, updateProperties, (storage.previousState as? Self)?.activatesDefault != activatesDefault { adw_entry_row_set_activates_default(widget?.cast(), activatesDefault.cBool) } - if let enableEmojiCompletion, updateProperties { + if let enableEmojiCompletion, updateProperties, (storage.previousState as? Self)?.enableEmojiCompletion != enableEmojiCompletion { adw_entry_row_set_enable_emoji_completion(widget?.cast(), enableEmojiCompletion.cBool) } - if let showApplyButton, updateProperties { + if let showApplyButton, updateProperties, (storage.previousState as? Self)?.showApplyButton != showApplyButton { adw_entry_row_set_show_apply_button(widget?.cast(), showApplyButton.cBool) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -155,6 +158,9 @@ public struct PasswordEntryRow: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether activating the embedded entry can activate the default widget. diff --git a/Sources/Adwaita/View/Generated/Picture.swift b/Sources/Adwaita/View/Generated/Picture.swift index c350876..42a8f01 100644 --- a/Sources/Adwaita/View/Generated/Picture.swift +++ b/Sources/Adwaita/View/Generated/Picture.swift @@ -2,7 +2,7 @@ // Picture.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -54,12 +54,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `GtkPicture` uses the `GTK_ACCESSIBLE_ROLE_IMG` role. -public struct Picture: Widget { +public struct Picture: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -71,57 +71,57 @@ public struct Picture: Widget { var canShrink: Bool? /// How the content should be resized to fit inside the `GtkPicture`. var contentFit: ContentFit? - /// Whether the GtkPicture will render its contents trying to preserve the aspect - /// ratio. - var keepAspectRatio: Bool? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Picture`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_picture_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let alternativeText, updateProperties { + if let alternativeText, updateProperties, (storage.previousState as? Self)?.alternativeText != alternativeText { gtk_picture_set_alternative_text(widget, alternativeText) } - if let canShrink, updateProperties { + if let canShrink, updateProperties, (storage.previousState as? Self)?.canShrink != canShrink { gtk_picture_set_can_shrink(widget, canShrink.cBool) } - if let contentFit, updateProperties { + if let contentFit, updateProperties, (storage.previousState as? Self)?.contentFit != contentFit { gtk_picture_set_content_fit(widget, contentFit.gtkValue) } - if let keepAspectRatio, updateProperties { - gtk_picture_set_keep_aspect_ratio(widget, keepAspectRatio.cBool) - } } for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. @@ -158,13 +158,4 @@ public struct Picture: Widget { return newSelf } - /// Whether the GtkPicture will render its contents trying to preserve the aspect - /// ratio. - public func keepAspectRatio(_ keepAspectRatio: Bool? = true) -> Self { - var newSelf = self - newSelf.keepAspectRatio = keepAspectRatio - - return newSelf - } - } diff --git a/Sources/Adwaita/View/Generated/Popover.swift b/Sources/Adwaita/View/Generated/Popover.swift index 278d547..5aede96 100644 --- a/Sources/Adwaita/View/Generated/Popover.swift +++ b/Sources/Adwaita/View/Generated/Popover.swift @@ -2,7 +2,7 @@ // Popover.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -68,12 +68,12 @@ import LevenshteinTransformations /// be the same. The arrow also does not support any border shape other than /// solid, no border-radius, only one border width (border-bottom-width is /// used) and no box-shadow. -public struct Popover: Widget { +public struct Popover: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -86,9 +86,9 @@ public struct Popover: Widget { /// This is used to implement the expected behavior of submenus. var cascadePopdown: Bool? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// The default widget inside the popover. - var defaultWidget: (() -> Body)? + var defaultWidget: (() -> Body)? /// Whether to draw an arrow. var hasArrow: Bool? /// Whether mnemonics are currently visible in this popover. @@ -100,41 +100,44 @@ public struct Popover: Widget { /// Emitted when the popover is closed. var closed: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Popover`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_popover_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_popover_set_child(storage.pointer?.cast(), childStorage.pointer?.cast()) + gtk_popover_set_child(storage.opaquePointer?.cast(), childStorage.opaquePointer?.cast()) } - if let defaultWidgetStorage = defaultWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + if let defaultWidgetStorage = defaultWidget?().storage(modifiers: modifiers, type: type) { storage.content["defaultWidget"] = [defaultWidgetStorage] - gtk_popover_set_default_widget(storage.pointer?.cast(), defaultWidgetStorage.pointer?.cast()) + gtk_popover_set_default_widget(storage.opaquePointer?.cast(), defaultWidgetStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activateDefault { storage.connectSignal(name: "activate-default", argCount: 0) { activateDefault() @@ -147,22 +150,22 @@ public struct Popover: Widget { } storage.modify { widget in - if let autohide, updateProperties { + if let autohide, updateProperties, (storage.previousState as? Self)?.autohide != autohide { gtk_popover_set_autohide(widget?.cast(), autohide.cBool) } - if let cascadePopdown, updateProperties { + if let cascadePopdown, updateProperties, (storage.previousState as? Self)?.cascadePopdown != cascadePopdown { gtk_popover_set_cascade_popdown(widget?.cast(), cascadePopdown.cBool) } if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } if let widget = storage.content["defaultWidget"]?.first { - defaultWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + defaultWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let hasArrow, updateProperties { + if let hasArrow, updateProperties, (storage.previousState as? Self)?.hasArrow != hasArrow { gtk_popover_set_has_arrow(widget?.cast(), hasArrow.cBool) } - if let mnemonicsVisible, updateProperties { + if let mnemonicsVisible, updateProperties, (storage.previousState as? Self)?.mnemonicsVisible != mnemonicsVisible { gtk_popover_set_mnemonics_visible(widget?.cast(), mnemonicsVisible.cBool) } @@ -171,6 +174,9 @@ public struct Popover: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/PreferencesGroup.swift b/Sources/Adwaita/View/Generated/PreferencesGroup.swift index f80b8cb..d4a194c 100644 --- a/Sources/Adwaita/View/Generated/PreferencesGroup.swift +++ b/Sources/Adwaita/View/Generated/PreferencesGroup.swift @@ -2,7 +2,7 @@ // PreferencesGroup.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -36,12 +36,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwPreferencesGroup` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct PreferencesGroup: Widget { +public struct PreferencesGroup: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The description for this group of preferences. var description: String? @@ -51,58 +51,61 @@ public struct PreferencesGroup: Widget { /// /// Suffixes are commonly used to show a button or a spinner for the whole /// group. - var headerSuffix: (() -> Body)? + var headerSuffix: (() -> Body)? /// The title for this group of preferences. var title: String? /// The body for the widget "child". var child: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `PreferencesGroup`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_preferences_group_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let headerSuffixStorage = headerSuffix?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let headerSuffixStorage = headerSuffix?().storage(modifiers: modifiers, type: type) { storage.content["headerSuffix"] = [headerSuffixStorage] - adw_preferences_group_set_header_suffix(storage.pointer?.cast(), headerSuffixStorage.pointer?.cast()) + adw_preferences_group_set_header_suffix(storage.opaquePointer?.cast(), headerSuffixStorage.opaquePointer?.cast()) } var childStorage: [ViewStorage] = [] for view in child() { - childStorage.append(view.storage(modifiers: modifiers)) - adw_preferences_group_add(storage.pointer?.cast(), childStorage.last?.pointer?.cast()) + childStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_preferences_group_add(storage.opaquePointer?.cast(), childStorage.last?.opaquePointer?.cast()) } storage.content["child"] = childStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let description, updateProperties { + if let description, updateProperties, (storage.previousState as? Self)?.description != description { adw_preferences_group_set_description(widget?.cast(), description) } if let widget = storage.content["headerSuffix"]?.first { - headerSuffix?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + headerSuffix?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_group_set_title(widget?.cast(), title) } @@ -112,7 +115,8 @@ public struct PreferencesGroup: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -122,6 +126,9 @@ public struct PreferencesGroup: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The description for this group of preferences. diff --git a/Sources/Adwaita/View/Generated/PreferencesPage.swift b/Sources/Adwaita/View/Generated/PreferencesPage.swift index 1b3ae5b..2961903 100644 --- a/Sources/Adwaita/View/Generated/PreferencesPage.swift +++ b/Sources/Adwaita/View/Generated/PreferencesPage.swift @@ -2,7 +2,7 @@ // PreferencesPage.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -22,12 +22,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwPreferencesPage` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct PreferencesPage: Widget { +public struct PreferencesPage: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The description to be displayed at the top of the page. var description: String? @@ -42,54 +42,57 @@ public struct PreferencesPage: Widget { /// The body for the widget "child". var child: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `PreferencesPage`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_preferences_page_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) var childStorage: [ViewStorage] = [] for view in child() { - childStorage.append(view.storage(modifiers: modifiers)) - adw_preferences_group_add(storage.pointer?.cast(), childStorage.last?.pointer?.cast()) + childStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_preferences_group_add(storage.opaquePointer?.cast(), childStorage.last?.opaquePointer?.cast()) } storage.content["child"] = childStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let description, updateProperties { + if let description, updateProperties, (storage.previousState as? Self)?.description != description { adw_preferences_page_set_description(widget?.cast(), description) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { adw_preferences_page_set_icon_name(widget?.cast(), iconName) } - if let name, updateProperties { + if let name, updateProperties, (storage.previousState as? Self)?.name != name { adw_preferences_page_set_name(widget?.cast(), name) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_page_set_title(widget?.cast(), title) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_page_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -99,7 +102,8 @@ public struct PreferencesPage: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -109,6 +113,9 @@ public struct PreferencesPage: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The description to be displayed at the top of the page. diff --git a/Sources/Adwaita/View/Generated/PreferencesRow.swift b/Sources/Adwaita/View/Generated/PreferencesRow.swift index 912cfd7..1f0b067 100644 --- a/Sources/Adwaita/View/Generated/PreferencesRow.swift +++ b/Sources/Adwaita/View/Generated/PreferencesRow.swift @@ -2,7 +2,7 @@ // PreferencesRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -17,12 +17,12 @@ import LevenshteinTransformations /// [class@ActionRow] and its derivatives are convenient to use as preference /// rows as they take care of presenting the preference's title while letting you /// compose the inputs of the preference around it. -public struct PreferencesRow: Widget { +public struct PreferencesRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The title of the preference represented by this row. /// @@ -42,45 +42,48 @@ public struct PreferencesRow: Widget { /// Whether an embedded underline in the title indicates a mnemonic. var useUnderline: Bool? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `PreferencesRow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_preferences_row_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -89,6 +92,9 @@ public struct PreferencesRow: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The title of the preference represented by this row. diff --git a/Sources/Adwaita/View/Generated/ProgressBar.swift b/Sources/Adwaita/View/Generated/ProgressBar.swift index b6a519f..eaba9b2 100644 --- a/Sources/Adwaita/View/Generated/ProgressBar.swift +++ b/Sources/Adwaita/View/Generated/ProgressBar.swift @@ -2,7 +2,7 @@ // ProgressBar.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -54,12 +54,12 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkProgressBar` uses the %GTK_ACCESSIBLE_ROLE_PROGRESS_BAR role. -public struct ProgressBar: Widget { +public struct ProgressBar: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -85,48 +85,51 @@ public struct ProgressBar: Widget { /// Text to be displayed in the progress bar. var text: String? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ProgressBar`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_progress_bar_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let fraction, updateProperties { + if let fraction, updateProperties, (storage.previousState as? Self)?.fraction != fraction { gtk_progress_bar_set_fraction(widget, fraction) } - if let inverted, updateProperties { + if let inverted, updateProperties, (storage.previousState as? Self)?.inverted != inverted { gtk_progress_bar_set_inverted(widget, inverted.cBool) } - if let pulseStep, updateProperties { + if let pulseStep, updateProperties, (storage.previousState as? Self)?.pulseStep != pulseStep { gtk_progress_bar_set_pulse_step(widget, pulseStep) } - if let showText, updateProperties { + if let showText, updateProperties, (storage.previousState as? Self)?.showText != showText { gtk_progress_bar_set_show_text(widget, showText.cBool) } - if let text, updateProperties { + if let text, updateProperties, (storage.previousState as? Self)?.text != text { gtk_progress_bar_set_text(widget, text) } @@ -135,6 +138,9 @@ public struct ProgressBar: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/ScrolledWindow.swift b/Sources/Adwaita/View/Generated/ScrolledWindow.swift index 6f3ab0f..2bf2941 100644 --- a/Sources/Adwaita/View/Generated/ScrolledWindow.swift +++ b/Sources/Adwaita/View/Generated/ScrolledWindow.swift @@ -2,7 +2,7 @@ // ScrolledWindow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -76,12 +76,12 @@ import LevenshteinTransformations /// Until GTK 4.10, `GtkScrolledWindow` used the `GTK_ACCESSIBLE_ROLE_GROUP` role. /// /// Starting from GTK 4.12, `GtkScrolledWindow` uses the `GTK_ACCESSIBLE_ROLE_GENERIC` role. -public struct ScrolledWindow: Widget { +public struct ScrolledWindow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -92,7 +92,7 @@ public struct ScrolledWindow: Widget { /// When setting this property, if the child widget does not implement /// [iface@Gtk.Scrollable], the scrolled window will add the child to /// a [class@Gtk.Viewport] and then set the viewport as the child. - var child: (() -> Body)? + var child: (() -> Body)? /// Whether to draw a frame around the contents. var hasFrame: Bool? /// Whether kinetic scrolling is enabled or not. @@ -165,37 +165,40 @@ public struct ScrolledWindow: Widget { /// signal that the scrolled window’s child may listen to and scroll itself. var scrollChild: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ScrolledWindow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_scrolled_window_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_scrolled_window_set_child(storage.pointer, childStorage.pointer?.cast()) + gtk_scrolled_window_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let edgeOvershot { storage.connectSignal(name: "edge-overshot", argCount: 1) { edgeOvershot() @@ -219,33 +222,33 @@ public struct ScrolledWindow: Widget { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let hasFrame, updateProperties { + if let hasFrame, updateProperties, (storage.previousState as? Self)?.hasFrame != hasFrame { gtk_scrolled_window_set_has_frame(widget, hasFrame.cBool) } - if let kineticScrolling, updateProperties { + if let kineticScrolling, updateProperties, (storage.previousState as? Self)?.kineticScrolling != kineticScrolling { gtk_scrolled_window_set_kinetic_scrolling(widget, kineticScrolling.cBool) } - if let maxContentHeight, updateProperties { + if let maxContentHeight, updateProperties, (storage.previousState as? Self)?.maxContentHeight != maxContentHeight { gtk_scrolled_window_set_max_content_height(widget, maxContentHeight.cInt) } - if let maxContentWidth, updateProperties { + if let maxContentWidth, updateProperties, (storage.previousState as? Self)?.maxContentWidth != maxContentWidth { gtk_scrolled_window_set_max_content_width(widget, maxContentWidth.cInt) } - if let minContentHeight, updateProperties { + if let minContentHeight, updateProperties, (storage.previousState as? Self)?.minContentHeight != minContentHeight { gtk_scrolled_window_set_min_content_height(widget, minContentHeight.cInt) } - if let minContentWidth, updateProperties { + if let minContentWidth, updateProperties, (storage.previousState as? Self)?.minContentWidth != minContentWidth { gtk_scrolled_window_set_min_content_width(widget, minContentWidth.cInt) } - if let overlayScrolling, updateProperties { + if let overlayScrolling, updateProperties, (storage.previousState as? Self)?.overlayScrolling != overlayScrolling { gtk_scrolled_window_set_overlay_scrolling(widget, overlayScrolling.cBool) } - if let propagateNaturalHeight, updateProperties { + if let propagateNaturalHeight, updateProperties, (storage.previousState as? Self)?.propagateNaturalHeight != propagateNaturalHeight { gtk_scrolled_window_set_propagate_natural_height(widget, propagateNaturalHeight.cBool) } - if let propagateNaturalWidth, updateProperties { + if let propagateNaturalWidth, updateProperties, (storage.previousState as? Self)?.propagateNaturalWidth != propagateNaturalWidth { gtk_scrolled_window_set_propagate_natural_width(widget, propagateNaturalWidth.cBool) } @@ -254,6 +257,9 @@ public struct ScrolledWindow: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/SearchBar.swift b/Sources/Adwaita/View/Generated/SearchBar.swift index 93d6d27..41561eb 100644 --- a/Sources/Adwaita/View/Generated/SearchBar.swift +++ b/Sources/Adwaita/View/Generated/SearchBar.swift @@ -2,7 +2,7 @@ // SearchBar.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -52,70 +52,73 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkSearchBar` uses the %GTK_ACCESSIBLE_ROLE_SEARCH role. -public struct SearchBar: Widget { +public struct SearchBar: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. var accessibleRole: String? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// The key capture widget. - var keyCaptureWidget: (() -> Body)? + var keyCaptureWidget: (() -> Body)? /// Whether the search mode is on and the search bar shown. var searchModeEnabled: Bool? /// Whether to show the close button in the search bar. var showCloseButton: Bool? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `SearchBar`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_search_bar_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_search_bar_set_child(storage.pointer, childStorage.pointer?.cast()) + gtk_search_bar_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } - if let keyCaptureWidgetStorage = keyCaptureWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + if let keyCaptureWidgetStorage = keyCaptureWidget?().storage(modifiers: modifiers, type: type) { storage.content["keyCaptureWidget"] = [keyCaptureWidgetStorage] - gtk_search_bar_set_key_capture_widget(storage.pointer, keyCaptureWidgetStorage.pointer?.cast()) + gtk_search_bar_set_key_capture_widget(storage.opaquePointer, keyCaptureWidgetStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } if let widget = storage.content["keyCaptureWidget"]?.first { - keyCaptureWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + keyCaptureWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let showCloseButton, updateProperties { + if let showCloseButton, updateProperties, (storage.previousState as? Self)?.showCloseButton != showCloseButton { gtk_search_bar_set_show_close_button(widget, showCloseButton.cBool) } @@ -124,6 +127,9 @@ public struct SearchBar: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/SearchEntry.swift b/Sources/Adwaita/View/Generated/SearchEntry.swift index 883d3bb..1b11715 100644 --- a/Sources/Adwaita/View/Generated/SearchEntry.swift +++ b/Sources/Adwaita/View/Generated/SearchEntry.swift @@ -2,7 +2,7 @@ // SearchEntry.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -52,12 +52,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `GtkSearchEntry` uses the %GTK_ACCESSIBLE_ROLE_SEARCH_BOX role. -public struct SearchEntry: Widget { +public struct SearchEntry: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -155,33 +155,36 @@ public struct SearchEntry: Widget { /// The default bindings for this signal is Escape. var stopSearch: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `SearchEntry`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_search_entry_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activate { storage.connectSignal(name: "activate", argCount: 0) { activate() @@ -230,30 +233,30 @@ public struct SearchEntry: Widget { storage.modify { widget in storage.notify(name: "text") { - let newValue = String(cString: gtk_editable_get_text(storage.pointer)) + let newValue = String(cString: gtk_editable_get_text(storage.opaquePointer)) if let text, newValue != text.wrappedValue { text.wrappedValue = newValue } } - if let editable, updateProperties { + if let editable, updateProperties, (storage.previousState as? Self)?.editable != editable { gtk_editable_set_editable(widget, editable.cBool) } - if let enableUndo, updateProperties { + if let enableUndo, updateProperties, (storage.previousState as? Self)?.enableUndo != enableUndo { gtk_editable_set_enable_undo(widget, enableUndo.cBool) } - if let maxWidthChars, updateProperties { + if let maxWidthChars, updateProperties, (storage.previousState as? Self)?.maxWidthChars != maxWidthChars { gtk_editable_set_max_width_chars(widget, maxWidthChars.cInt) } - if let placeholderText, updateProperties { + if let placeholderText, updateProperties, (storage.previousState as? Self)?.placeholderText != placeholderText { gtk_search_entry_set_placeholder_text(widget, placeholderText) } - if let searchDelay, updateProperties { + if let searchDelay, updateProperties, (storage.previousState as? Self)?.searchDelay != searchDelay { gtk_search_entry_set_search_delay(widget, searchDelay.cInt) } - if let text, updateProperties, (String(cString: gtk_editable_get_text(storage.pointer))) != text.wrappedValue { - gtk_editable_set_text(storage.pointer, text.wrappedValue) + if let text, updateProperties, (String(cString: gtk_editable_get_text(storage.opaquePointer))) != text.wrappedValue { + gtk_editable_set_text(storage.opaquePointer, text.wrappedValue) } - if let widthChars, updateProperties { + if let widthChars, updateProperties, (storage.previousState as? Self)?.widthChars != widthChars { gtk_editable_set_width_chars(widget, widthChars.cInt) } @@ -262,6 +265,9 @@ if let text, newValue != text.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/Separator.swift b/Sources/Adwaita/View/Generated/Separator.swift index 06ae9a8..2dccb7c 100644 --- a/Sources/Adwaita/View/Generated/Separator.swift +++ b/Sources/Adwaita/View/Generated/Separator.swift @@ -2,7 +2,7 @@ // Separator.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -24,45 +24,48 @@ import LevenshteinTransformations /// # Accessibility /// /// `GtkSeparator` uses the %GTK_ACCESSIBLE_ROLE_SEPARATOR role. -public struct Separator: Widget { +public struct Separator: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. var accessibleRole: String? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Separator`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_separator_new(GTK_ORIENTATION_VERTICAL)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in @@ -71,6 +74,9 @@ public struct Separator: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/SpinRow.swift b/Sources/Adwaita/View/Generated/SpinRow.swift index 9cd86fb..5fcebe5 100644 --- a/Sources/Adwaita/View/Generated/SpinRow.swift +++ b/Sources/Adwaita/View/Generated/SpinRow.swift @@ -2,7 +2,7 @@ // SpinRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -24,12 +24,12 @@ import LevenshteinTransformations /// /// `AdwSpinRow` has the same structure as [class@ActionRow], as well as the /// `.spin` style class on the main node. -public struct SpinRow: Widget { +public struct SpinRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The widget to activate when the row is activated. /// @@ -40,13 +40,11 @@ public struct SpinRow: Widget { /// /// The target widget will be activated by emitting the /// [signal@Gtk.Widget::mnemonic-activate] signal on it. - var activatableWidget: (() -> Body)? + var activatableWidget: (() -> Body)? /// The acceleration rate when you hold down a button or key. var climbRate: Double /// The number of decimal places to display. var digits: UInt - /// The icon name for this row. - var iconName: String? /// Whether non-numeric characters should be ignored. var numeric: Bool? /// Whether invalid values are snapped to the nearest step increment. @@ -114,9 +112,9 @@ public struct SpinRow: Widget { /// The body for the widget "prefix". var prefix: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `SpinRow`. public init(climbRate: Double, digits: UInt) { @@ -124,41 +122,44 @@ public struct SpinRow: Widget { self.digits = digits } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_spin_row_new(nil, climbRate, digits.cInt)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let activatableWidgetStorage = activatableWidget?().storage(modifiers: modifiers, type: type) { storage.content["activatableWidget"] = [activatableWidgetStorage] - adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast()) + adw_action_row_set_activatable_widget(storage.opaquePointer?.cast(), activatableWidgetStorage.opaquePointer?.cast()) } var suffixStorage: [ViewStorage] = [] for view in suffix() { - suffixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast()) + suffixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_suffix(storage.opaquePointer?.cast(), suffixStorage.last?.opaquePointer?.cast()) } storage.content["suffix"] = suffixStorage var prefixStorage: [ViewStorage] = [] for view in prefix() { - prefixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast()) + prefixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_prefix(storage.opaquePointer?.cast(), prefixStorage.last?.opaquePointer?.cast()) } storage.content["prefix"] = prefixStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activated { storage.connectSignal(name: "activated", argCount: 0) { activated() @@ -182,57 +183,54 @@ public struct SpinRow: Widget { storage.modify { widget in storage.notify(name: "value") { - let newValue = adw_spin_row_get_value(storage.pointer) + let newValue = adw_spin_row_get_value(storage.opaquePointer) if let value, newValue != value.wrappedValue { value.wrappedValue = newValue } } if let widget = storage.content["activatableWidget"]?.first { - activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + activatableWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.climbRate != climbRate { adw_spin_row_set_climb_rate(widget, climbRate) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.digits != digits { adw_spin_row_set_digits(widget, digits.cInt) } - if let iconName, updateProperties { - adw_action_row_set_icon_name(widget?.cast(), iconName) - } - if let numeric, updateProperties { + if let numeric, updateProperties, (storage.previousState as? Self)?.numeric != numeric { adw_spin_row_set_numeric(widget, numeric.cBool) } - if let snapToTicks, updateProperties { + if let snapToTicks, updateProperties, (storage.previousState as? Self)?.snapToTicks != snapToTicks { adw_spin_row_set_snap_to_ticks(widget, snapToTicks.cBool) } - if let subtitle, updateProperties { + if let subtitle, updateProperties, (storage.previousState as? Self)?.subtitle != subtitle { adw_action_row_set_subtitle(widget?.cast(), subtitle) } - if let subtitleLines, updateProperties { + if let subtitleLines, updateProperties, (storage.previousState as? Self)?.subtitleLines != subtitleLines { adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt) } - if let subtitleSelectable, updateProperties { + if let subtitleSelectable, updateProperties, (storage.previousState as? Self)?.subtitleSelectable != subtitleSelectable { adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let titleLines, updateProperties { + if let titleLines, updateProperties, (storage.previousState as? Self)?.titleLines != titleLines { adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } - if let value, updateProperties, (adw_spin_row_get_value(storage.pointer)) != value.wrappedValue { - adw_spin_row_set_value(storage.pointer, value.wrappedValue) + if let value, updateProperties, (adw_spin_row_get_value(storage.opaquePointer)) != value.wrappedValue { + adw_spin_row_set_value(storage.opaquePointer, value.wrappedValue) } - if let wrap, updateProperties { + if let wrap, updateProperties, (storage.previousState as? Self)?.wrap != wrap { adw_spin_row_set_wrap(widget, wrap.cBool) } @@ -241,6 +239,9 @@ if let value, newValue != value.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The widget to activate when the row is activated. @@ -275,14 +276,6 @@ if let value, newValue != value.wrappedValue { return newSelf } - /// The icon name for this row. - public func iconName(_ iconName: String?) -> Self { - var newSelf = self - newSelf.iconName = iconName - - return newSelf - } - /// Whether non-numeric characters should be ignored. public func numeric(_ numeric: Bool? = true) -> Self { var newSelf = self diff --git a/Sources/Adwaita/View/Generated/Spinner.swift b/Sources/Adwaita/View/Generated/Spinner.swift index 3943f54..3f564bf 100644 --- a/Sources/Adwaita/View/Generated/Spinner.swift +++ b/Sources/Adwaita/View/Generated/Spinner.swift @@ -2,7 +2,7 @@ // Spinner.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -23,12 +23,12 @@ import LevenshteinTransformations /// `GtkSpinner` has a single CSS node with the name spinner. /// When the animation is active, the :checked pseudoclass is /// added to this node. -public struct Spinner: Widget { +public struct Spinner: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -37,36 +37,39 @@ public struct Spinner: Widget { /// Whether the spinner is spinning var spinning: Bool? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `Spinner`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_spinner_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if let spinning, updateProperties { + if let spinning, updateProperties, (storage.previousState as? Self)?.spinning != spinning { gtk_spinner_set_spinning(widget, spinning.cBool) } @@ -75,6 +78,9 @@ public struct Spinner: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/SplitButton.swift b/Sources/Adwaita/View/Generated/SplitButton.swift index 954cab9..999d1fb 100644 --- a/Sources/Adwaita/View/Generated/SplitButton.swift +++ b/Sources/Adwaita/View/Generated/SplitButton.swift @@ -2,7 +2,7 @@ // SplitButton.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -40,12 +40,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwSplitButton` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct SplitButton: Widget { +public struct SplitButton: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// Whether the button can be smaller than the natural size of its contents. /// @@ -58,7 +58,7 @@ public struct SplitButton: Widget { /// /// Setting the child widget will set [property@SplitButton:label] and /// [property@SplitButton:icon-name] to `NULL`. - var child: (() -> Body)? + var child: (() -> Body)? /// The tooltip of the dropdown button. /// /// The tooltip can be marked up with the Pango text markup language. @@ -83,7 +83,7 @@ public struct SplitButton: Widget { /// /// If [property@SplitButton:popover] is already set, it will be dissociated /// from the button, and the property is set to `NULL`. - var menuModel: (() -> MenuContent)? + var menuModel: (() -> Body)? /// Whether an underline in the text indicates a mnemonic. /// /// See [property@SplitButton:label]. @@ -96,44 +96,45 @@ public struct SplitButton: Widget { /// Emitted when the button has been activated (pressed and released). var clicked: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `SplitButton`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_split_button_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - adw_split_button_set_child(storage.pointer, childStorage.pointer?.cast()) + adw_split_button_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } - if let declarative = menuModel?(), let app { - let menu = g_menu_new() - adw_split_button_set_menu_model(storage.pointer, menu?.cast()) - for item in declarative { - item.addMenuItems(menu: menu, app: app, window: window) - } + if let menu = menuModel?(), let app { + let childStorage = MenuCollection { menu }.getMenu(app: app, window: window) + storage.content["menuModel"] = [childStorage] + adw_split_button_set_menu_model(storage.opaquePointer, childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activate { storage.connectSignal(name: "activate", argCount: 0) { activate() @@ -146,22 +147,26 @@ public struct SplitButton: Widget { } storage.modify { widget in - if let canShrink, updateProperties { + if let canShrink, updateProperties, (storage.previousState as? Self)?.canShrink != canShrink { adw_split_button_set_can_shrink(widget, canShrink.cBool) } if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let dropdownTooltip, updateProperties { + if let dropdownTooltip, updateProperties, (storage.previousState as? Self)?.dropdownTooltip != dropdownTooltip { adw_split_button_set_dropdown_tooltip(widget, dropdownTooltip) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { adw_split_button_set_icon_name(widget, iconName) } - if let label, storage.content["child"] == nil, updateProperties { + if let label, storage.content["child"] == nil, updateProperties, (storage.previousState as? Self)?.label != label { adw_split_button_set_label(widget, label) } - if let useUnderline, updateProperties { + if let menu = storage.content["menuModel"]?.first { + MenuCollection { menuModel?() ?? [] } + .updateStorage(menu, modifiers: [], updateProperties: updateProperties, type: MenuContext.self) + } + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_split_button_set_use_underline(widget, useUnderline.cBool) } @@ -170,6 +175,9 @@ public struct SplitButton: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// Whether the button can be smaller than the natural size of its contents. @@ -238,7 +246,7 @@ public struct SplitButton: Widget { /// /// If [property@SplitButton:popover] is already set, it will be dissociated /// from the button, and the property is set to `NULL`. - public func menuModel(app: GTUIApp, window: GTUIApplicationWindow? = nil, @MenuBuilder _ menuModel: @escaping (() -> MenuContent)) -> Self { + public func menuModel(app: AdwaitaApp, window: AdwaitaWindow? = nil, @ViewBuilder _ menuModel: @escaping (() -> Body)) -> Self { var newSelf = self newSelf.menuModel = menuModel newSelf.app = app; newSelf.window = window diff --git a/Sources/Adwaita/View/Generated/StatusPage.swift b/Sources/Adwaita/View/Generated/StatusPage.swift index e271123..24a657b 100644 --- a/Sources/Adwaita/View/Generated/StatusPage.swift +++ b/Sources/Adwaita/View/Generated/StatusPage.swift @@ -2,7 +2,7 @@ // StatusPage.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -22,15 +22,15 @@ import LevenshteinTransformations /// `AdwStatusPage` can use the /// [`.compact`](style-classes.html#compact-status-page) style class for when it /// needs to fit into a small space such a sidebar or a popover. -public struct StatusPage: Widget { +public struct StatusPage: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// The description markup to be displayed below the title. var description: String? /// The name of the icon to be used. @@ -42,49 +42,52 @@ public struct StatusPage: Widget { /// It is not parsed as Pango markup. var title: String? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `StatusPage`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_status_page_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - adw_status_page_set_child(storage.pointer, childStorage.pointer?.cast()) + adw_status_page_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let description, updateProperties { + if let description, updateProperties, (storage.previousState as? Self)?.description != description { adw_status_page_set_description(widget, description) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { adw_status_page_set_icon_name(widget, iconName) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_status_page_set_title(widget, title) } @@ -93,6 +96,9 @@ public struct StatusPage: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The child widget. diff --git a/Sources/Adwaita/View/Generated/SwitchRow.swift b/Sources/Adwaita/View/Generated/SwitchRow.swift index 514443d..f169c2b 100644 --- a/Sources/Adwaita/View/Generated/SwitchRow.swift +++ b/Sources/Adwaita/View/Generated/SwitchRow.swift @@ -2,7 +2,7 @@ // SwitchRow.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -28,12 +28,12 @@ import LevenshteinTransformations /// /// The [property@SwitchRow:active] property should be connected to in order to /// monitor changes to the active state. -public struct SwitchRow: Widget { +public struct SwitchRow: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The widget to activate when the row is activated. /// @@ -44,11 +44,9 @@ public struct SwitchRow: Widget { /// /// The target widget will be activated by emitting the /// [signal@Gtk.Widget::mnemonic-activate] signal on it. - var activatableWidget: (() -> Body)? + var activatableWidget: (() -> Body)? /// Whether the switch row is in the "on" or "off" position. var active: Binding? - /// The icon name for this row. - var iconName: String? /// The subtitle for this row. /// /// The subtitle is interpreted as Pango markup unless @@ -91,49 +89,52 @@ public struct SwitchRow: Widget { /// The body for the widget "prefix". var prefix: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `SwitchRow`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_switch_row_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let activatableWidgetStorage = activatableWidget?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let activatableWidgetStorage = activatableWidget?().storage(modifiers: modifiers, type: type) { storage.content["activatableWidget"] = [activatableWidgetStorage] - adw_action_row_set_activatable_widget(storage.pointer?.cast(), activatableWidgetStorage.pointer?.cast()) + adw_action_row_set_activatable_widget(storage.opaquePointer?.cast(), activatableWidgetStorage.opaquePointer?.cast()) } var suffixStorage: [ViewStorage] = [] for view in suffix() { - suffixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_suffix(storage.pointer?.cast(), suffixStorage.last?.pointer?.cast()) + suffixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_suffix(storage.opaquePointer?.cast(), suffixStorage.last?.opaquePointer?.cast()) } storage.content["suffix"] = suffixStorage var prefixStorage: [ViewStorage] = [] for view in prefix() { - prefixStorage.append(view.storage(modifiers: modifiers)) - adw_action_row_add_prefix(storage.pointer?.cast(), prefixStorage.last?.pointer?.cast()) + prefixStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_action_row_add_prefix(storage.opaquePointer?.cast(), prefixStorage.last?.opaquePointer?.cast()) } storage.content["prefix"] = prefixStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activated { storage.connectSignal(name: "activated", argCount: 0) { activated() @@ -142,42 +143,39 @@ public struct SwitchRow: Widget { storage.modify { widget in storage.notify(name: "active") { - let newValue = adw_switch_row_get_active(storage.pointer) != 0 + let newValue = adw_switch_row_get_active(storage.opaquePointer) != 0 if let active, newValue != active.wrappedValue { active.wrappedValue = newValue } } if let widget = storage.content["activatableWidget"]?.first { - activatableWidget?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) - } - if let active, updateProperties, (adw_switch_row_get_active(storage.pointer) != 0) != active.wrappedValue { - adw_switch_row_set_active(storage.pointer, active.wrappedValue.cBool) + activatableWidget?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let iconName, updateProperties { - adw_action_row_set_icon_name(widget?.cast(), iconName) + if let active, updateProperties, (adw_switch_row_get_active(storage.opaquePointer) != 0) != active.wrappedValue { + adw_switch_row_set_active(storage.opaquePointer, active.wrappedValue.cBool) } - if let subtitle, updateProperties { + if let subtitle, updateProperties, (storage.previousState as? Self)?.subtitle != subtitle { adw_action_row_set_subtitle(widget?.cast(), subtitle) } - if let subtitleLines, updateProperties { + if let subtitleLines, updateProperties, (storage.previousState as? Self)?.subtitleLines != subtitleLines { adw_action_row_set_subtitle_lines(widget?.cast(), subtitleLines.cInt) } - if let subtitleSelectable, updateProperties { + if let subtitleSelectable, updateProperties, (storage.previousState as? Self)?.subtitleSelectable != subtitleSelectable { adw_action_row_set_subtitle_selectable(widget?.cast(), subtitleSelectable.cBool) } - if let title, updateProperties { + if let title, updateProperties, (storage.previousState as? Self)?.title != title { adw_preferences_row_set_title(widget?.cast(), title) } - if let titleLines, updateProperties { + if let titleLines, updateProperties, (storage.previousState as? Self)?.titleLines != titleLines { adw_action_row_set_title_lines(widget?.cast(), titleLines.cInt) } - if let titleSelectable, updateProperties { + if let titleSelectable, updateProperties, (storage.previousState as? Self)?.titleSelectable != titleSelectable { adw_preferences_row_set_title_selectable(widget?.cast(), titleSelectable.cBool) } - if let useMarkup, updateProperties { + if let useMarkup, updateProperties, (storage.previousState as? Self)?.useMarkup != useMarkup { adw_preferences_row_set_use_markup(widget?.cast(), useMarkup.cBool) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { adw_preferences_row_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -186,6 +184,9 @@ if let active, newValue != active.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The widget to activate when the row is activated. @@ -212,14 +213,6 @@ if let active, newValue != active.wrappedValue { return newSelf } - /// The icon name for this row. - public func iconName(_ iconName: String?) -> Self { - var newSelf = self - newSelf.iconName = iconName - - return newSelf - } - /// The subtitle for this row. /// /// The subtitle is interpreted as Pango markup unless diff --git a/Sources/Adwaita/View/Generated/ToastOverlay.swift b/Sources/Adwaita/View/Generated/ToastOverlay.swift index a689ada..530159e 100644 --- a/Sources/Adwaita/View/Generated/ToastOverlay.swift +++ b/Sources/Adwaita/View/Generated/ToastOverlay.swift @@ -2,7 +2,7 @@ // ToastOverlay.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -44,51 +44,54 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwToastOverlay` uses the `GTK_ACCESSIBLE_ROLE_TAB_GROUP` role. -public struct ToastOverlay: Widget { +public struct ToastOverlay: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ToastOverlay`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_toast_overlay_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - adw_toast_overlay_set_child(storage.pointer, childStorage.pointer?.cast()) + adw_toast_overlay_set_child(storage.opaquePointer, childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } @@ -96,6 +99,9 @@ public struct ToastOverlay: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The child widget. diff --git a/Sources/Adwaita/View/Generated/ToggleButton.swift b/Sources/Adwaita/View/Generated/ToggleButton.swift index 0a16375..ed5cc3e 100644 --- a/Sources/Adwaita/View/Generated/ToggleButton.swift +++ b/Sources/Adwaita/View/Generated/ToggleButton.swift @@ -2,7 +2,7 @@ // ToggleButton.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -83,12 +83,12 @@ import LevenshteinTransformations /// gtk_window_present (GTK_WINDOW (window)); /// } /// ``` -public struct ToggleButton: Widget { +public struct ToggleButton: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The accessible role of the given `GtkAccessible` implementation. /// @@ -107,7 +107,7 @@ public struct ToggleButton: Widget { /// property has no effect. var canShrink: Bool? /// The child widget. - var child: (() -> Body)? + var child: (() -> Body)? /// Whether the button has a frame. var hasFrame: Bool? /// The name of the icon used to automatically populate the button. @@ -130,37 +130,40 @@ public struct ToggleButton: Widget { /// Emitted whenever the `GtkToggleButton`'s state is changed. var toggled: (() -> Void)? /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ToggleButton`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_toggle_button_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let childStorage = child?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let childStorage = child?().storage(modifiers: modifiers, type: type) { storage.content["child"] = [childStorage] - gtk_button_set_child(storage.pointer?.cast(), childStorage.pointer?.cast()) + gtk_button_set_child(storage.opaquePointer?.cast(), childStorage.opaquePointer?.cast()) } return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { if let activate { storage.connectSignal(name: "activate", argCount: 0) { activate() @@ -179,33 +182,33 @@ public struct ToggleButton: Widget { storage.modify { widget in storage.notify(name: "active") { - let newValue = gtk_toggle_button_get_active(storage.pointer?.cast()) != 0 + let newValue = gtk_toggle_button_get_active(storage.opaquePointer?.cast()) != 0 if let active, newValue != active.wrappedValue { active.wrappedValue = newValue } } - if let actionName, updateProperties { + if let actionName, updateProperties, (storage.previousState as? Self)?.actionName != actionName { gtk_actionable_set_action_name(widget, actionName) } - if let active, updateProperties, (gtk_toggle_button_get_active(storage.pointer?.cast()) != 0) != active.wrappedValue { - gtk_toggle_button_set_active(storage.pointer?.cast(), active.wrappedValue.cBool) + if let active, updateProperties, (gtk_toggle_button_get_active(storage.opaquePointer?.cast()) != 0) != active.wrappedValue { + gtk_toggle_button_set_active(storage.opaquePointer?.cast(), active.wrappedValue.cBool) } - if let canShrink, updateProperties { + if let canShrink, updateProperties, (storage.previousState as? Self)?.canShrink != canShrink { gtk_button_set_can_shrink(widget?.cast(), canShrink.cBool) } if let widget = storage.content["child"]?.first { - child?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + child?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let hasFrame, updateProperties { + if let hasFrame, updateProperties, (storage.previousState as? Self)?.hasFrame != hasFrame { gtk_button_set_has_frame(widget?.cast(), hasFrame.cBool) } - if let iconName, updateProperties { + if let iconName, updateProperties, (storage.previousState as? Self)?.iconName != iconName { gtk_button_set_icon_name(widget?.cast(), iconName) } - if let label, storage.content["child"] == nil, updateProperties { + if let label, storage.content["child"] == nil, updateProperties, (storage.previousState as? Self)?.label != label { gtk_button_set_label(widget?.cast(), label) } - if let useUnderline, updateProperties { + if let useUnderline, updateProperties, (storage.previousState as? Self)?.useUnderline != useUnderline { gtk_button_set_use_underline(widget?.cast(), useUnderline.cBool) } @@ -214,6 +217,9 @@ if let active, newValue != active.wrappedValue { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The accessible role of the given `GtkAccessible` implementation. diff --git a/Sources/Adwaita/View/Generated/ToolbarView.swift b/Sources/Adwaita/View/Generated/ToolbarView.swift index d5803cf..f3c4721 100644 --- a/Sources/Adwaita/View/Generated/ToolbarView.swift +++ b/Sources/Adwaita/View/Generated/ToolbarView.swift @@ -2,7 +2,7 @@ // ToolbarView.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -76,12 +76,12 @@ import LevenshteinTransformations /// ## Accessibility /// /// `AdwToolbarView` uses the `GTK_ACCESSIBLE_ROLE_GROUP` role. -public struct ToolbarView: Widget { +public struct ToolbarView: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The current bottom bar height. /// @@ -91,7 +91,7 @@ public struct ToolbarView: Widget { /// See [property@ToolbarView:top-bar-height]. var bottomBarHeight: Int? /// The content widget. - var content: (() -> Body)? + var content: (() -> Body)? /// Whether the content widget can extend behind bottom bars. /// /// This can be used in combination with @@ -139,64 +139,67 @@ public struct ToolbarView: Widget { /// The body for the widget "top". var top: () -> Body = { [] } /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `ToolbarView`. public init() { } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_toolbar_view_new()?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) - if let contentStorage = content?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + update(storage, modifiers: modifiers, updateProperties: true, type: type) + if let contentStorage = content?().storage(modifiers: modifiers, type: type) { storage.content["content"] = [contentStorage] - adw_toolbar_view_set_content(storage.pointer, contentStorage.pointer?.cast()) + adw_toolbar_view_set_content(storage.opaquePointer, contentStorage.opaquePointer?.cast()) } var bottomStorage: [ViewStorage] = [] for view in bottom() { - bottomStorage.append(view.storage(modifiers: modifiers)) - adw_toolbar_view_add_bottom_bar(storage.pointer, bottomStorage.last?.pointer?.cast()) + bottomStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_toolbar_view_add_bottom_bar(storage.opaquePointer, bottomStorage.last?.opaquePointer?.cast()) } storage.content["bottom"] = bottomStorage var topStorage: [ViewStorage] = [] for view in top() { - topStorage.append(view.storage(modifiers: modifiers)) - adw_toolbar_view_add_top_bar(storage.pointer, topStorage.last?.pointer?.cast()) + topStorage.append(view.storage(modifiers: modifiers, type: type)) + adw_toolbar_view_add_top_bar(storage.opaquePointer, topStorage.last?.opaquePointer?.cast()) } storage.content["top"] = topStorage return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in if let widget = storage.content["content"]?.first { - content?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + content?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } - if let extendContentToBottomEdge, updateProperties { + if let extendContentToBottomEdge, updateProperties, (storage.previousState as? Self)?.extendContentToBottomEdge != extendContentToBottomEdge { adw_toolbar_view_set_extend_content_to_bottom_edge(widget, extendContentToBottomEdge.cBool) } - if let extendContentToTopEdge, updateProperties { + if let extendContentToTopEdge, updateProperties, (storage.previousState as? Self)?.extendContentToTopEdge != extendContentToTopEdge { adw_toolbar_view_set_extend_content_to_top_edge(widget, extendContentToTopEdge.cBool) } - if let revealBottomBars, updateProperties { + if let revealBottomBars, updateProperties, (storage.previousState as? Self)?.revealBottomBars != revealBottomBars { adw_toolbar_view_set_reveal_bottom_bars(widget, revealBottomBars.cBool) } - if let revealTopBars, updateProperties { + if let revealTopBars, updateProperties, (storage.previousState as? Self)?.revealTopBars != revealTopBars { adw_toolbar_view_set_reveal_top_bars(widget, revealTopBars.cBool) } @@ -206,7 +209,8 @@ public struct ToolbarView: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -217,7 +221,8 @@ public struct ToolbarView: Widget { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -227,6 +232,9 @@ public struct ToolbarView: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The current bottom bar height. diff --git a/Sources/Adwaita/View/Generated/WindowTitle.swift b/Sources/Adwaita/View/Generated/WindowTitle.swift index bdfc12a..f2dd494 100644 --- a/Sources/Adwaita/View/Generated/WindowTitle.swift +++ b/Sources/Adwaita/View/Generated/WindowTitle.swift @@ -2,7 +2,7 @@ // WindowTitle.swift // Adwaita // -// Created by auto-generation on 21.07.24. +// Created by auto-generation on 03.08.24. // import CAdw @@ -18,12 +18,12 @@ import LevenshteinTransformations /// ## CSS nodes /// /// `AdwWindowTitle` has a single CSS node with name `windowtitle`. -public struct WindowTitle: Widget { +public struct WindowTitle: AdwaitaWidget { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] /// The subtitle to display. /// @@ -35,9 +35,9 @@ public struct WindowTitle: Widget { /// generally does not use the application name. var title: String /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? /// Initialize `WindowTitle`. public init(subtitle: String, title: String) { @@ -45,31 +45,34 @@ public struct WindowTitle: Widget { self.title = title } - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(adw_window_title_new(title, subtitle)?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { storage.modify { widget in - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.subtitle != subtitle { adw_window_title_set_subtitle(widget, subtitle) } - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.title != title { adw_window_title_set_title(widget, title) } @@ -78,6 +81,9 @@ public struct WindowTitle: Widget { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } /// The subtitle to display. diff --git a/Sources/Adwaita/View/HStack.swift b/Sources/Adwaita/View/HStack.swift index da125ab..9bb0527 100644 --- a/Sources/Adwaita/View/HStack.swift +++ b/Sources/Adwaita/View/HStack.swift @@ -6,7 +6,7 @@ // /// A horizontal GtkBox equivalent. -public struct HStack: View { +public struct HStack: SimpleView { /// The content. var content: () -> Body diff --git a/Sources/Adwaita/View/List.swift b/Sources/Adwaita/View/List.swift index 5cd3ef1..22744ef 100644 --- a/Sources/Adwaita/View/List.swift +++ b/Sources/Adwaita/View/List.swift @@ -6,6 +6,7 @@ // import CAdw +import Meta /// A list box widget. public typealias List = ListBox @@ -29,32 +30,31 @@ extension List { ) { self.init(elements, content: content) let id: (ViewStorage, [Element]) -> Element.ID? = { storage, elements in - if let row = gtk_list_box_get_selected_row(storage.pointer) { + if let row = gtk_list_box_get_selected_row(storage.opaquePointer) { return elements[safe: .init(gtk_list_box_row_get_index(row))]?.id } return nil } if let selection { - appearFunctions.append { storage, _ in - storage.fields[Self.selectionField] = selection + updateFunctions.append { storage, _, _ in storage.connectSignal(name: "selected_rows_changed", id: Self.selectionField) { - if let binding = storage.fields[Self.selectionField] as? Binding, - let elements = storage.fields[Self.elementsField] as? [Element], - let id = id(storage, elements), - binding.wrappedValue != id { - binding.wrappedValue = id + if let elements = storage.fields[Self.elementsField] as? [Element], + let id = id(storage, elements), + selection.wrappedValue != id { + selection.wrappedValue = id } } - } - updateFunctions.append { storage, _, _ in if selection.wrappedValue != id(storage, elements), let index = elements.firstIndex(where: { $0.id == selection.wrappedValue })?.cInt { - gtk_list_box_select_row(storage.pointer, gtk_list_box_get_row_at_index(storage.pointer, index)) + gtk_list_box_select_row( + storage.opaquePointer, + gtk_list_box_get_row_at_index(storage.opaquePointer, index) + ) } } } else { appearFunctions.append { storage, _ in - gtk_list_box_set_selection_mode(storage.pointer, GTK_SELECTION_NONE) + gtk_list_box_set_selection_mode(storage.opaquePointer, GTK_SELECTION_NONE) } } } @@ -62,14 +62,14 @@ extension List { /// Add the "navigation-sidebar" style class. /// - Parameter active: Whether the style is applied. /// - Returns: A view. - public func sidebarStyle(_ active: Bool = true) -> View { + public func sidebarStyle(_ active: Bool = true) -> AnyView { style("navigation-sidebar", active: active) } /// Apply the boxed list style class. /// - Parameter active: Whether the style is applied. /// - Returns: A view. - public func boxedList(_ active: Bool = true) -> View { + public func boxedList(_ active: Bool = true) -> AnyView { style("boxed-list", active: active) } diff --git a/Sources/Adwaita/View/Menu+.swift b/Sources/Adwaita/View/Menu+.swift index 1b06e64..4307962 100644 --- a/Sources/Adwaita/View/Menu+.swift +++ b/Sources/Adwaita/View/Menu+.swift @@ -20,9 +20,9 @@ extension Menu { public init( _ label: String? = nil, icon: Icon, - app: GTUIApp, - window: GTUIApplicationWindow?, - @MenuBuilder content: @escaping () -> MenuContent + app: AdwaitaApp, + window: AdwaitaWindow?, + @ViewBuilder content: @escaping () -> Body ) { self.init() self = self @@ -43,11 +43,14 @@ extension Menu { /// - content: The menu's content. public init( _ label: String, - app: GTUIApp, - window: GTUIApplicationWindow?, - @MenuBuilder content: () -> MenuContent + app: AdwaitaApp, + window: AdwaitaWindow?, + @ViewBuilder content: @escaping () -> Body ) { self.init() + self = self + .label(label) + .menuModel(app: app, window: window, content) } } diff --git a/Sources/Adwaita/View/Modifiers/AnyView++.swift b/Sources/Adwaita/View/Modifiers/AnyView++.swift new file mode 100644 index 0000000..1923cf2 --- /dev/null +++ b/Sources/Adwaita/View/Modifiers/AnyView++.swift @@ -0,0 +1,129 @@ +// +// AnyView+.swift +// Adwaita +// +// Created by david-swift on 01.08.24. +// + +import CAdw +import Foundation + +extension AnyView { + + /// Run a function when the view gets an update. + /// - Parameter onUpdate: The function. + /// - Returns: A view. + public func onUpdate(_ onUpdate: @escaping () -> Void) -> AnyView { + inspect { _, _ in onUpdate() } + } + + /// Make the view insensitive (useful e.g. in overlays). + /// - Parameter insensitive: Whether the view is insensitive. + /// - Returns: A view. + public func insensitive(_ insensitive: Bool = true) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_sensitive(storage.opaquePointer?.cast(), insensitive ? 0 : 1) + } + } + } + + /// Set the view's visibility. + /// - Parameter visible: Whether the view is visible. + /// - Returns: A view. + public func visible(_ visible: Bool = true) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_visible(storage.opaquePointer?.cast(), visible.cBool) + } + } + } + + /// Bind to the view's focus. + /// - Parameter focus: Whether the view is focused. + /// - Returns: A view. + public func focused(_ focused: Binding) -> AnyView { + let focus = "focus" + return inspectOnAppear { storage in + let controller = gtk_event_controller_focus_new() + storage.content[focus] = [.init(controller)] + gtk_widget_add_controller(storage.opaquePointer?.cast(), controller) + } + .inspect { storage, _ in + guard let controller = storage.content[focus]?.first else { + return + } + controller.notify(name: "contains-focus", id: "focused") { + let newValue = gtk_event_controller_focus_contains_focus(controller.opaquePointer) != 0 + if focused.wrappedValue != newValue { + focused.wrappedValue = newValue + } + } + if gtk_event_controller_focus_contains_focus(controller.opaquePointer) == 0, focused.wrappedValue { + gtk_widget_grab_focus(storage.opaquePointer?.cast()) + } + } + } + + /// Bind a signal that focuses the view. + /// - Parameter focus: Whether the view is focused. + /// - Returns: A view. + public func focus(_ signal: Signal) -> AnyView { + inspect { storage, _ in + if signal.update { + gtk_widget_grab_focus(storage.opaquePointer?.cast()) + } + } + } + + /// Add a tooltip to the widget. + /// - Parameter tooltip: The tooltip text. + /// - Returns: A view. + public func tooltip(_ tooltip: String) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_tooltip_markup(storage.opaquePointer?.cast(), tooltip) + } + } + } + + /// Run a function when the view appears for the first time. + /// - Parameter closure: The function. + /// - Returns: A view. + public func onAppear(_ closure: @escaping () -> Void) -> AnyView { + inspectOnAppear { _ in closure() } + } + + /// Run a function when the widget gets clicked. + /// - Parameter handler: The function. + /// - Returns: A view. + public func onClick(handler: @escaping () -> Void) -> AnyView { + inspectOnAppear { storage in + let controller = ViewStorage(gtk_gesture_click_new()) + gtk_widget_add_controller(storage.opaquePointer?.cast(), controller.opaquePointer) + storage.fields["controller"] = controller + let argCount = 3 + controller.connectSignal(name: "released", argCount: argCount, handler: handler) + } + } + + /// Add CSS classes to the app as soon as the view appears. + /// - Parameter getString: Get the CSS. + /// - Returns: A view. + public func css(_ getString: @escaping () -> String) -> AnyView { + inspectOnAppear { _ in + let provider = gtk_css_provider_new() + gtk_css_provider_load_from_string( + provider, + getString() + ) + let display = gdk_display_get_default() + gtk_style_context_add_provider_for_display( + display, + provider?.opaque(), + .init(GTK_STYLE_PROVIDER_PRIORITY_APPLICATION) + ) + } + } + +} diff --git a/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift b/Sources/Adwaita/View/Modifiers/AnyView+.swift similarity index 50% rename from Sources/Adwaita/View/Modifiers/InspectorWrapper.swift rename to Sources/Adwaita/View/Modifiers/AnyView+.swift index 6825a2a..3f48282 100644 --- a/Sources/Adwaita/View/Modifiers/InspectorWrapper.swift +++ b/Sources/Adwaita/View/Modifiers/AnyView+.swift @@ -1,51 +1,54 @@ // -// InspectorWrapper.swift +// AnyView+.swift // Adwaita // -// Created by david-swift on 10.09.23. +// Created by david-swift on 01.08.24. // import CAdw +import Foundation -/// A widget which executes a custom code on the GTUI widget when being created and updated. -struct InspectorWrapper: Widget { +extension AnyView { - /// The custom code to edit the widget. - var modify: (ViewStorage) -> Void - /// The wrapped view. - var content: View - - /// Get the content's container. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The content's container. - func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = content.storage(modifiers: modifiers) - modify(storage) - return storage + /// Set the view's aspect ratio. + /// - Parameter aspectRatio: The aspect ratio. + public func aspectRatio(_ aspectRatio: Float) -> AspectFrame { + .init(ratio: aspectRatio) + .child { self } } - /// Update the content. + /// Add a top toolbar to the view. /// - Parameters: - /// - storage: The content's storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - content.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties) - modify(storage) + /// - toolbar: The toolbar's content. + /// - visible: Whether the toolbar is visible. + /// - Returns: A view. + public func topToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> ToolbarView { + .init() + .content { self } + .top(toolbar) + .revealTopBars(visible) } -} - -extension View { - - /// The identifier for the focus event controller. - static var focus: String { "focus" } + /// Add a bottom toolbar to the view. + /// - Parameters: + /// - toolbar: The toolbar's content. + /// - visible: Whether the toolbar is visible. + /// - Returns: A view. + public func bottomToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> ToolbarView { + .init() + .content { self } + .bottom(toolbar) + .revealBottomBars(visible) + } - /// Modify a GTUI widget before being displayed and when being updated. - /// - Parameter modify: Modify the widget. + /// Add an overlay view. + /// - Parameters: + /// - overlay: The overlay view. /// - Returns: A view. - public func inspect(_ modify: @escaping (ViewStorage) -> Void) -> View { - InspectorWrapper(modify: modify, content: self) + public func overlay(@ViewBuilder _ overlay: @escaping () -> Body) -> Overlay { + .init() + .child { self } + .overlay(overlay) } /// Add padding around a view. @@ -53,41 +56,59 @@ extension View { /// - padding: The size of the padding. /// - edges: The edges which are affected by the padding. /// - Returns: A view. - public func padding(_ padding: Int = 10, _ edges: Set = .all) -> View { - inspect { widget in - if edges.contains(.leading) { gtk_widget_set_margin_start(widget.pointer?.cast(), padding.cInt) } - if edges.contains(.trailing) { gtk_widget_set_margin_end(widget.pointer?.cast(), padding.cInt) } - if edges.contains(.top) { gtk_widget_set_margin_top(widget.pointer?.cast(), padding.cInt) } - if edges.contains(.bottom) { gtk_widget_set_margin_bottom(widget.pointer?.cast(), padding.cInt) } + public func padding(_ padding: Int = 10, _ edges: Set = .all) -> AnyView { + inspect { widget, updateProperties in + if updateProperties { + if edges.contains(.leading) { gtk_widget_set_margin_start(widget.opaquePointer?.cast(), padding.cInt) } + if edges.contains(.trailing) { gtk_widget_set_margin_end(widget.opaquePointer?.cast(), padding.cInt) } + if edges.contains(.top) { gtk_widget_set_margin_top(widget.opaquePointer?.cast(), padding.cInt) } + if edges.contains(.bottom) { gtk_widget_set_margin_bottom(widget.opaquePointer?.cast(), padding.cInt) } + } } } /// Enable or disable the horizontal expansion. /// - Parameter enabled: Whether it is enabled or disabled. /// - Returns: A view. - public func hexpand(_ enabled: Bool = true) -> View { - inspect { gtk_widget_set_hexpand($0.pointer?.cast(), enabled.cBool) } + public func hexpand(_ enabled: Bool = true) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_hexpand(storage.opaquePointer?.cast(), enabled.cBool) + } + } } /// Enable or disable the vertical expansion. /// - Parameter enabled: Whether it is enabled or disabled. /// - Returns: A view. - public func vexpand(_ enabled: Bool = true) -> View { - inspect { gtk_widget_set_vexpand($0.pointer?.cast(), enabled.cBool) } + public func vexpand(_ enabled: Bool = true) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_vexpand(storage.opaquePointer?.cast(), enabled.cBool) + } + } } /// Set the horizontal alignment. /// - Parameter align: The alignment. /// - Returns: A view. - public func halign(_ align: Alignment) -> View { - inspect { gtk_widget_set_halign($0.pointer?.cast(), align.cAlign) } + public func halign(_ align: Alignment) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_halign(storage.opaquePointer?.cast(), align.cAlign) + } + } } /// Set the vertical alignment. /// - Parameter align: The alignment. /// - Returns: A view. - public func valign(_ align: Alignment) -> View { - inspect { gtk_widget_set_valign($0.pointer?.cast(), align.cAlign) } + public func valign(_ align: Alignment) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_valign(storage.opaquePointer?.cast(), align.cAlign) + } + } } /// Set the view's minimal width or height. @@ -95,22 +116,34 @@ extension View { /// - minWidth: The minimal width. /// - minHeight: The minimal height. /// - Returns: A view. - public func frame(minWidth: Int? = nil, minHeight: Int? = nil) -> View { - inspect { gtk_widget_set_size_request($0.pointer?.cast(), minWidth?.cInt ?? 1, minHeight?.cInt ?? -1) } + public func frame(minWidth: Int? = nil, minHeight: Int? = nil) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + gtk_widget_set_size_request(storage.opaquePointer?.cast(), minWidth?.cInt ?? 1, minHeight?.cInt ?? -1) + } + } } /// Set the view's transition. /// - Parameter transition: The transition. /// - Returns: A view. - public func transition(_ transition: Transition) -> View { - inspect { $0.fields[.transition] = transition } + public func transition(_ transition: Transition) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + storage.fields[.transition] = transition + } + } } /// Set the view's navigation title. /// - Parameter label: The navigation title. /// - Returns: A view. - public func navigationTitle(_ label: String) -> View { - inspect { $0.fields[.navigationLabel] = label } + public func navigationTitle(_ label: String) -> AnyView { + inspect { storage, updateProperties in + if updateProperties { + storage.fields[.navigationLabel] = label + } + } } /// Add a style class to the view. @@ -118,12 +151,15 @@ extension View { /// - style: The style class. /// - active: Whether the style is currently applied. /// - Returns: A view. - public func style(_ style: String, active: Bool = true) -> View { - inspect { storage in + public func style(_ style: String, active: Bool = true) -> AnyView { + inspect { storage, updateProperties in + guard updateProperties else { + return + } if active { - gtk_widget_add_css_class(storage.pointer?.cast(), style) + gtk_widget_add_css_class(storage.opaquePointer?.cast(), style) } else { - gtk_widget_remove_css_class(storage.pointer?.cast(), style) + gtk_widget_remove_css_class(storage.opaquePointer?.cast(), style) } } } @@ -131,154 +167,154 @@ extension View { /// Make a button or similar widget use accent colors. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func suggested(_ active: Bool = true) -> View { + public func suggested(_ active: Bool = true) -> AnyView { style("suggested-action", active: active) } /// Make a button or similar widget use destructive colors. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func destructive(_ active: Bool = true) -> View { + public func destructive(_ active: Bool = true) -> AnyView { style("destructive-action", active: active) } /// Make a button or similar widget use flat appearance. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func flat(_ active: Bool = true) -> View { + public func flat(_ active: Bool = true) -> AnyView { style("flat", active: active) } /// Make a button or similar widget use the regular appearance instead of the flat one. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func raised(_ active: Bool = true) -> View { + public func raised(_ active: Bool = true) -> AnyView { style("raised", active: active) } /// Make a button or similar widget round. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func circular(_ active: Bool = true) -> View { + public func circular(_ active: Bool = true) -> AnyView { style("circular", active: active) } /// Make a button or similar widget appear as a pill. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func pill(_ active: Bool = true) -> View { + public func pill(_ active: Bool = true) -> AnyView { style("pill", active: active) } /// Make the view partially transparent. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func dimLabel(_ active: Bool = true) -> View { + public func dimLabel(_ active: Bool = true) -> AnyView { style("dim-label", active: active) } /// Use a title typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func title1(_ active: Bool = true) -> View { + public func title1(_ active: Bool = true) -> AnyView { style("title-1", active: active) } /// Use a title typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func title2(_ active: Bool = true) -> View { + public func title2(_ active: Bool = true) -> AnyView { style("title-2", active: active) } /// Use a title typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func title3(_ active: Bool = true) -> View { + public func title3(_ active: Bool = true) -> AnyView { style("title-3", active: active) } /// Use a title typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func title4(_ active: Bool = true) -> View { + public func title4(_ active: Bool = true) -> AnyView { style("title-4", active: active) } /// Use the heading typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func heading(_ active: Bool = true) -> View { + public func heading(_ active: Bool = true) -> AnyView { style("heading", active: active) } /// Use the body typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func body(_ active: Bool = true) -> View { + public func body(_ active: Bool = true) -> AnyView { style("body", active: active) } /// Use the caption heading typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func captionHeading(_ active: Bool = true) -> View { + public func captionHeading(_ active: Bool = true) -> AnyView { style("caption-heading", active: active) } /// Use the caption typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func caption(_ active: Bool = true) -> View { + public func caption(_ active: Bool = true) -> AnyView { style("caption", active: active) } /// Use the monospace typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func monospace(_ active: Bool = true) -> View { + public func monospace(_ active: Bool = true) -> AnyView { style("monospace", active: active) } /// Use the numeric typography style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func numeric(_ active: Bool = true) -> View { + public func numeric(_ active: Bool = true) -> AnyView { style("numeric", active: active) } /// Apply the accent color. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func accent(_ active: Bool = true) -> View { + public func accent(_ active: Bool = true) -> AnyView { style("accent", active: active) } /// Apply the success color. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func success(_ active: Bool = true) -> View { + public func success(_ active: Bool = true) -> AnyView { style("success", active: active) } /// Apply the warning color. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func warning(_ active: Bool = true) -> View { + public func warning(_ active: Bool = true) -> AnyView { style("warning", active: active) } /// Apply the error color. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func error(_ active: Bool = true) -> View { + public func error(_ active: Bool = true) -> AnyView { style("error", active: active) } /// Apply the card style. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func card(_ active: Bool = true) -> View { + public func card(_ active: Bool = true) -> AnyView { style("card", active: active) } @@ -287,107 +323,43 @@ extension View { /// - Returns: A view. /// /// Use for icons larger than 32x32 pixels. - public func iconDropshadow(_ active: Bool = true) -> View { + public func iconDropshadow(_ active: Bool = true) -> AnyView { style("icon-dropshadow", active: active) } /// Use for icons smaller than or equal to 32x32 pixels. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func lowresIcon(_ active: Bool = true) -> View { + public func lowresIcon(_ active: Bool = true) -> AnyView { style("lowres-icon", active: active) } /// Use the OSD style class. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func osd(_ active: Bool = true) -> View { + public func osd(_ active: Bool = true) -> AnyView { style("osd", active: active) } /// Give a view the default window background and foreground colors. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func backgroundStyle(_ active: Bool = true) -> View { + public func backgroundStyle(_ active: Bool = true) -> AnyView { style("background", active: active) } /// Give a view the default view background and foreground colors. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func viewStyle(_ active: Bool = true) -> View { + public func viewStyle(_ active: Bool = true) -> AnyView { style("view", active: active) } /// Give a view the default border. /// - Parameter active: Whether the style is currently applied. /// - Returns: A view. - public func frameStyle(_ active: Bool = true) -> View { + public func frameStyle(_ active: Bool = true) -> AnyView { style("frame", active: active) } - /// Run a function when the view gets an update. - /// - Parameter onUpdate: The function. - /// - Returns: A view. - public func onUpdate(_ onUpdate: @escaping () -> Void) -> View { - inspect { _ in onUpdate() } - } - - /// Make the view insensitive (useful e.g. in overlays). - /// - Parameter insensitive: Whether the view is insensitive. - /// - Returns: A view. - public func insensitive(_ insensitive: Bool = true) -> View { - inspect { gtk_widget_set_sensitive($0.pointer?.cast(), insensitive ? 0 : 1) } - } - - /// Set the view's visibility. - /// - Parameter visible: Whether the view is visible. - /// - Returns: A view. - public func visible(_ visible: Bool = true) -> View { - inspect { gtk_widget_set_visible($0.pointer?.cast(), visible.cBool) } - } - - /// Bind to the view's focus. - /// - Parameter focus: Whether the view is focused. - /// - Returns: A view. - public func focused(_ focused: Binding) -> View { - inspectOnAppear { storage in - let controller = gtk_event_controller_focus_new() - storage.content[Self.focus] = [.init(controller)] - gtk_widget_add_controller(storage.pointer?.cast(), controller) - } - .inspect { storage in - guard let controller = storage.content[Self.focus]?.first else { - return - } - controller.notify(name: "contains-focus", id: "focused") { - let newValue = gtk_event_controller_focus_contains_focus(controller.pointer) != 0 - if focused.wrappedValue != newValue { - focused.wrappedValue = newValue - } - } - if gtk_event_controller_focus_contains_focus(controller.pointer) == 0, focused.wrappedValue { - gtk_widget_grab_focus(storage.pointer?.cast()) - } - } - } - - /// Bind a signal that focuses the view. - /// - Parameter focus: Whether the view is focused. - /// - Returns: A view. - public func focus(_ signal: Signal) -> View { - inspect { storage in - if signal.update { - gtk_widget_grab_focus(storage.pointer?.cast()) - } - } - } - - /// Add a tooltip to the widget. - /// - Parameter tooltip: The tooltip text. - /// - Returns: A view. - public func tooltip(_ tooltip: String) -> View { - inspect { gtk_widget_set_tooltip_markup($0.pointer?.cast(), tooltip) } - } - } diff --git a/Sources/Adwaita/View/Modifiers/AppearObserver.swift b/Sources/Adwaita/View/Modifiers/AppearObserver.swift deleted file mode 100644 index fc234d1..0000000 --- a/Sources/Adwaita/View/Modifiers/AppearObserver.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// AppearObserver.swift -// Adwaita -// -// Created by david-swift on 29.11.23. -// - -import CAdw - -/// A widget which executes a custom code when being rendered for the first time. -struct AppearObserver: Widget { - - /// The function. - var onAppear: (ViewStorage) -> Void - /// The content. - var content: View - - /// Get the content's container. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The content's container. - func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = content.storage(modifiers: modifiers) - onAppear(storage) - return storage - } - - /// Update the content. - /// - Parameters: - /// - storage: The content's storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - content.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties) - } - -} - -extension View { - - /// Run a function on the widget when it appears for the first time. - /// - Parameter closure: The function. - /// - Returns: A view. - public func inspectOnAppear(_ closure: @escaping (ViewStorage) -> Void) -> View { - AppearObserver(onAppear: closure, content: self) - } - - /// Run a function when the view appears for the first time. - /// - Parameter closure: The function. - /// - Returns: A view. - public func onAppear(_ closure: @escaping () -> Void) -> View { - inspectOnAppear { _ in closure() } - } - - /// Run a function when the widget gets clicked. - /// - Parameter handler: The function. - /// - Returns: A view. - public func onClick(handler: @escaping () -> Void) -> View { - inspectOnAppear { storage in - let controller = ViewStorage(gtk_gesture_click_new()) - gtk_widget_add_controller(storage.pointer?.cast(), controller.pointer) - storage.fields["controller"] = controller - let argCount = 3 - controller.connectSignal(name: "released", argCount: argCount, handler: handler) - } - } - - /// Add CSS classes to the app as soon as the view appears. - /// - Parameter getString: Get the CSS. - /// - Returns: A view. - public func css(_ getString: @escaping () -> String) -> View { - inspectOnAppear { _ in - let provider = gtk_css_provider_new() - gtk_css_provider_load_from_string( - provider, - getString() - ) - let display = gdk_display_get_default() - gtk_style_context_add_provider_for_display( - display, - provider?.opaque(), - .init(GTK_STYLE_PROVIDER_PRIORITY_APPLICATION) - ) - } - } - -} diff --git a/Sources/Adwaita/View/Modifiers/Clamp+.swift b/Sources/Adwaita/View/Modifiers/Clamp+.swift index a8b1d02..1840d3d 100644 --- a/Sources/Adwaita/View/Modifiers/Clamp+.swift +++ b/Sources/Adwaita/View/Modifiers/Clamp+.swift @@ -15,22 +15,14 @@ extension Clamp { self.init() if vertical { appearFunctions.append { storage, _ in - gtk_orientable_set_orientation(storage.pointer, GTK_ORIENTATION_VERTICAL) + gtk_orientable_set_orientation(storage.opaquePointer, GTK_ORIENTATION_VERTICAL) } } } } -extension View { - - /// Set the view's maximum width. - /// - Parameter maxSize: The maximum width. - /// - Returns: A view. - @available(*, deprecated, message: "Use frame(maxWidth:) on views instead") - public func frame(maxSize: Int? = nil) -> Clamp { - frame(maxWidth: maxSize) - } +extension AnyView { /// Set the view's maximum width. /// - Parameter maxWidth: The maximum width. diff --git a/Sources/Adwaita/View/Modifiers/ContentModifier.swift b/Sources/Adwaita/View/Modifiers/ContentModifier.swift deleted file mode 100644 index 2214ccc..0000000 --- a/Sources/Adwaita/View/Modifiers/ContentModifier.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// ContentModifier.swift -// Adwaita -// -// Created by david-swift on 11.11.23. -// - -/// A widget which replaces views of a specific type in its content. -struct ContentModifier: Widget where Content: View { - - /// The wrapped view. - var content: View - /// The closure for the modification. - var modify: (Content) -> View - - /// Get the content's container. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The content's container. - func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = content.storage(modifiers: modifiers + [modifyView]) - return storage - } - - /// Update the content. - /// - Parameters: - /// - storage: The content's storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - content.updateStorage(storage, modifiers: modifiers + [modifyView], updateProperties: updateProperties) - } - - /// Apply the modifier to a view. - /// - Parameter view: The view. - func modifyView(_ view: View) -> View { - if let view = view as? Content { - return modify(view).stopModifiers() - } else { - return view - } - } - -} - -extension View { - - /// Replace every occurrence of a certain view type in the content. - /// - Parameters: - /// - type: The view type. - /// - modify: Modify the view. - /// - Returns: A view. - public func modifyContent( - _ type: Content.Type, - modify: @escaping (Content) -> View - ) -> View where Content: View { - ContentModifier(content: self, modify: modify) - } - -} diff --git a/Sources/Adwaita/View/Modifiers/Freeze.swift b/Sources/Adwaita/View/Modifiers/Freeze.swift deleted file mode 100644 index 737b54a..0000000 --- a/Sources/Adwaita/View/Modifiers/Freeze.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// Freeze.swift -// Adwaita -// -// Created by david-swift on 13.02.24. -// - -/// State whether to update the child views or not. -struct Freeze: Widget { - - /// Whether not to update the child view. - var freeze: Bool - /// The wrapped view. - var content: View - - /// Get the content's container. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The content's container. - func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = content.storage(modifiers: []) - return storage - } - - /// Update the content. - /// - Parameters: - /// - storage: The content's storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - if !freeze { - content.updateStorage(storage, modifiers: [], updateProperties: updateProperties) - } - } - -} - -extension View { - - /// Prevent a view from being updated. - /// - Parameter freeze: Whether to freeze the view. - /// - Returns: A view. - public func freeze(_ freeze: Bool = true) -> View { - Freeze(freeze: freeze, content: self) - } - -} diff --git a/Sources/Adwaita/View/Modifiers/ModifierStopper.swift b/Sources/Adwaita/View/Modifiers/ModifierStopper.swift deleted file mode 100644 index 23a24de..0000000 --- a/Sources/Adwaita/View/Modifiers/ModifierStopper.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// ModifierStopper.swift -// Adwaita -// -// Created by david-swift on 11.11.23. -// - -/// Remove all of the content modifiers for the wrapped views. -struct ModifierStopper: Widget { - - /// The wrapped view. - var content: View - - /// Get the content's container. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The content's container. - func container(modifiers: [(View) -> View]) -> ViewStorage { - let storage = content.storage(modifiers: []) - return storage - } - - /// Update the content. - /// - Parameters: - /// - storage: The content's storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - content.updateStorage(storage, modifiers: [], updateProperties: updateProperties) - } - -} - -extension View { - - /// Remove all of the content modifiers for the wrapped views. - /// - Returns: A view. - public func stopModifiers() -> View { - ModifierStopper(content: self) - } - -} diff --git a/Sources/Adwaita/View/Modifiers/Popover+.swift b/Sources/Adwaita/View/Modifiers/Popover+.swift index de4d314..69ee19a 100644 --- a/Sources/Adwaita/View/Modifiers/Popover+.swift +++ b/Sources/Adwaita/View/Modifiers/Popover+.swift @@ -13,30 +13,23 @@ extension Popover { /// - Parameter vertical: Whether it is a vertical clamp. init(visible: Binding) { self.init() - appearFunctions.append { storage, _ in - storage.fields["visible"] = visible + updateFunctions.append { storage, _, _ in storage.connectSignal(name: "closed", id: "visible") { - if let binding = storage.fields["visible"] as? Binding { - if binding.wrappedValue { - binding.wrappedValue = false - } + if visible.wrappedValue { + visible.wrappedValue = false } } - } - updateFunctions.append { storage, _, _ in - if let binding = storage.fields["visible"] as? Binding { - if binding.wrappedValue { - gtk_popover_popup(storage.pointer?.cast()) - } else { - gtk_popover_popdown(storage.pointer?.cast()) - } + if visible.wrappedValue { + gtk_popover_popup(storage.opaquePointer?.cast()) + } else { + gtk_popover_popdown(storage.opaquePointer?.cast()) } } } } -extension View { +extension AnyView { /// Add a popover on top of the view. /// - Parameters: diff --git a/Sources/Adwaita/View/Modifiers/ToastOverlay+.swift b/Sources/Adwaita/View/Modifiers/ToastOverlay+.swift index 0b5e8c5..01dc2a5 100644 --- a/Sources/Adwaita/View/Modifiers/ToastOverlay+.swift +++ b/Sources/Adwaita/View/Modifiers/ToastOverlay+.swift @@ -16,19 +16,16 @@ extension ToastOverlay { /// - title: The toast's title. /// - signal: The signal for adding a toast. public init(_ title: String, signal: Signal) { - appearFunctions.append { storage, _ in - storage.fields["signal"] = signal - } updateFunctions.append { storage, _, _ in - if let signal = storage.fields["signal"] as? Signal, signal.update { + if signal.update { let toast = ViewStorage(adw_toast_new(title)) storage.fields[UUID().uuidString] = toast if let button = storage.fields["button"] as? String, let handler = storage.fields["handler"] as? () -> Void { - adw_toast_set_button_label(toast.pointer, button) + adw_toast_set_button_label(toast.opaquePointer, button) toast.connectSignal(name: "button-clicked", handler: handler) } - adw_toast_overlay_add_toast(storage.pointer, toast.pointer) + adw_toast_overlay_add_toast(storage.opaquePointer, toast.opaquePointer) } } } @@ -40,7 +37,7 @@ extension ToastOverlay { /// - Returns: The toast overlay. public func action(button: String, handler: @escaping () -> Void) -> Self { var newSelf = self - let action: (ViewStorage, [(View) -> View], Bool) -> Void = { storage, _, _ in + let action: (ViewStorage, [(AnyView) -> AnyView], Bool) -> Void = { storage, _, _ in storage.fields["button"] = button storage.fields["handler"] = handler } @@ -50,7 +47,7 @@ extension ToastOverlay { } -extension View { +extension AnyView { /// Present a toast when the signal gets activated. /// - Parameters: diff --git a/Sources/Adwaita/View/Modifiers/View+.swift b/Sources/Adwaita/View/Modifiers/View+.swift deleted file mode 100644 index fc1fc24..0000000 --- a/Sources/Adwaita/View/Modifiers/View+.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// View+.swift -// Adwaita -// -// Created by david-swift on 03.01.24. -// - -extension View { - - /// Set the view's aspect ratio. - /// - Parameter aspectRatio: The aspect ratio. - public func aspectRatio(_ aspectRatio: Float) -> AspectFrame { - .init(ratio: aspectRatio) - .child { self } - } - - /// Wrap the view in a vertical stack and center vertically. - /// - Returns: The view. - public func verticalCenter() -> View { - VStack { self } - .valign(.center) - } - - /// Wrap the view in a horizontal stack and center horizontally. - /// - Returns: The view. - public func horizontalCenter() -> View { - HStack { self } - .halign(.center) - } - - /// Add a top toolbar to the view. - /// - Parameters: - /// - toolbar: The toolbar's content. - /// - visible: Whether the toolbar is visible. - /// - Returns: A view. - public func topToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> ToolbarView { - .init() - .content { self } - .top(toolbar) - .revealTopBars(visible) - } - - /// Add a bottom toolbar to the view. - /// - Parameters: - /// - toolbar: The toolbar's content. - /// - visible: Whether the toolbar is visible. - /// - Returns: A view. - public func bottomToolbar(visible: Bool = true, @ViewBuilder _ toolbar: @escaping () -> Body) -> ToolbarView { - .init() - .content { self } - .bottom(toolbar) - .revealBottomBars(visible) - } - - /// Add an overlay view. - /// - Parameters: - /// - overlay: The overlay view. - /// - Returns: A view. - public func overlay(@ViewBuilder _ overlay: @escaping () -> Body) -> Overlay { - .init() - .child { self } - .overlay(overlay) - } - -} diff --git a/Sources/Adwaita/View/NavigationSplitView.swift b/Sources/Adwaita/View/NavigationSplitView.swift index f24cde7..f462dc0 100644 --- a/Sources/Adwaita/View/NavigationSplitView.swift +++ b/Sources/Adwaita/View/NavigationSplitView.swift @@ -8,12 +8,12 @@ import CAdw /// A navigation split view widget. -public struct NavigationSplitView: Widget { +public struct NavigationSplitView: AdwaitaWidget { /// The sidebar's content. - var sidebar: () -> Body + var sidebar: Body /// The split view's main content. - var content: () -> Body + var content: Body /// Whether the split view is collapsed. var collapsed = false /// Whether the content is visible (if the split view is collapsed). @@ -29,65 +29,74 @@ public struct NavigationSplitView: Widget { /// - sidebar: The sidebar content. /// - content: The main content. public init(@ViewBuilder sidebar: @escaping () -> Body, @ViewBuilder content: @escaping () -> Body) { - self.sidebar = sidebar - self.content = content + self.sidebar = sidebar() + self.content = content() } - /// Get the container of the navigation split view widget. - /// - Parameter modifiers: Modify views before being updated. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container( + modifiers: [(AnyView) -> AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { let splitView = adw_navigation_split_view_new() var content: [String: [ViewStorage]] = [:] - let sidebar = sidebar().widget(modifiers: modifiers).container(modifiers: modifiers) + let sidebar = sidebar.storage(modifiers: modifiers, type: type) let label = sidebar.fields[.navigationLabel] as? String ?? "" - let sidebarPage = adw_navigation_page_new(sidebar.pointer?.cast(), label) + let sidebarPage = adw_navigation_page_new(sidebar.opaquePointer?.cast(), label) adw_navigation_split_view_set_sidebar(.init(splitView), sidebarPage?.cast()) content[sidebarID] = [sidebar] - let mainContent = self.content().widget(modifiers: modifiers).container(modifiers: modifiers) + let mainContent = self.content.storage(modifiers: modifiers, type: type) let mainLabel = mainContent.fields[.navigationLabel] as? String ?? "" - let mainPage = adw_navigation_page_new(mainContent.pointer?.cast(), mainLabel) + let mainPage = adw_navigation_page_new(mainContent.opaquePointer?.cast(), mainLabel) adw_navigation_split_view_set_content(.init(splitView), mainPage?.cast()) content[contentID] = [mainContent] - let storage = ViewStorage(.init(splitView), content: content) - update(storage, modifiers: modifiers, updateProperties: true) + let storage = ViewStorage(splitView?.opaque(), content: content) + update(storage, modifiers: modifiers, updateProperties: true, type: type) storage.notify(name: "show-content") { - showContent?.wrappedValue = adw_navigation_split_view_get_show_content(storage.pointer) != 0 + showContent?.wrappedValue = adw_navigation_split_view_get_show_content(storage.opaquePointer) != 0 } return storage } - /// Update the view storage of the navigation split view widget. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { if let storage = storage.content[contentID]?[safe: 0] { - content() - .widget(modifiers: modifiers) - .update(storage, modifiers: modifiers, updateProperties: updateProperties) + content + .updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) } if let storage = storage.content[sidebarID]?[safe: 0] { - sidebar() - .widget(modifiers: modifiers) - .update(storage, modifiers: modifiers, updateProperties: updateProperties) + sidebar + .updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) } guard updateProperties else { return } - let collapsed = adw_navigation_split_view_get_collapsed(storage.pointer) != 0 + let collapsed = adw_navigation_split_view_get_collapsed(storage.opaquePointer) != 0 if collapsed != self.collapsed { - adw_navigation_split_view_set_collapsed(storage.pointer, self.collapsed.cBool) + adw_navigation_split_view_set_collapsed(storage.opaquePointer, self.collapsed.cBool) } - let showContent = adw_navigation_split_view_get_show_content(storage.pointer) != 0 + let showContent = adw_navigation_split_view_get_show_content(storage.opaquePointer) != 0 if let binding = self.showContent, showContent != binding.wrappedValue { - adw_navigation_split_view_set_show_content(storage.pointer, binding.wrappedValue.cBool) + adw_navigation_split_view_set_show_content(storage.opaquePointer, binding.wrappedValue.cBool) } } diff --git a/Sources/Adwaita/View/NavigationView+.swift b/Sources/Adwaita/View/NavigationView+.swift index 0bb9aef..7d17602 100644 --- a/Sources/Adwaita/View/NavigationView+.swift +++ b/Sources/Adwaita/View/NavigationView+.swift @@ -26,53 +26,31 @@ extension NavigationView { ) { self.init() appearFunctions.append { storage, modifiers in - let initialStorage = initialView().storage(modifiers: modifiers) + let initialStorage = initialView().storage(modifiers: modifiers, type: AdwaitaMainView.self) storage.fields[.mainContent] = [initialStorage] adw_navigation_view_push( - storage.pointer, - adw_navigation_page_new(initialStorage.pointer?.cast(), initialTitle) + storage.opaquePointer, + adw_navigation_page_new(initialStorage.opaquePointer?.cast(), initialTitle) ) storage.connectSignal(name: "popped", id: "components", argCount: 1) { args in guard var storages = storage.fields[.mainContent] as? [ViewStorage], let arg = args.first as? UnsafeRawPointer else { return } - if storages.last?.pointer == adw_navigation_page_get_child(OpaquePointer(arg).cast())?.opaque() { + if storages.last?.opaquePointer == adw_navigation_page_get_child(OpaquePointer(arg).cast())?.opaque() { storages.removeLast() storage.fields[.mainContent] = storages } } } - updateFunctions.append { storage, modifiers, updateProperties in - guard var storages = storage.fields[.mainContent] as? [ViewStorage] else { - return - } - switch stack.wrappedValue.action { - case .pop: - if storages.count > 1 { - adw_navigation_view_pop(storage.pointer) - storages.removeLast() - } else { print("Warning: removing the initial view is not allowed.") } - case let .push(component): - let contentStorage = content(component).storage(modifiers: modifiers) - contentStorage.fields[Self.componentID] = component - storages.append(contentStorage) - adw_navigation_view_push( - storage.pointer, - adw_navigation_page_new(contentStorage.pointer?.cast(), component.description) - ) - default: - break - } - UpdateManager.blockUpdates = true; stack.wrappedValue.action = nil; UpdateManager.blockUpdates = false - storage.fields[.mainContent] = storages - for storage in storages { - if let component = storage.fields[Self.componentID] as? Component { - content(component).updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties) - } else { - initialView().updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties) - } - } + updateFunctions.append { [self] storage, modifiers, updateProperties in + updateFunction( + storage: storage, + modifiers: modifiers, + stack: stack, + views: (content, initialView()), + updateProperties: updateProperties + ) } } @@ -108,6 +86,63 @@ extension NavigationView { } + /// Update a navigation view. + /// - Parameters: + /// - storage: The view storage. + /// - modifiers: The view modifiers. + /// - stack: The navigation stack for pushing and popping. + /// - views: The views. + /// - updateProperties: Whether to update properties. + func updateFunction( + storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + stack: Binding>, + views: ((Component) -> Body, Body), + updateProperties: Bool + ) { + guard var storages = storage.fields[.mainContent] as? [ViewStorage] else { + return + } + switch stack.wrappedValue.action { + case .pop: + if storages.count > 1 { + adw_navigation_view_pop(storage.opaquePointer) + storages.removeLast() + } else { print("Warning: removing the initial view is not allowed.") } + case let .push(component): + let contentStorage = views.0(component).storage(modifiers: modifiers, type: AdwaitaMainView.self) + contentStorage.fields[Self.componentID] = component + storages.append(contentStorage) + adw_navigation_view_push( + storage.opaquePointer, + adw_navigation_page_new(contentStorage.opaquePointer?.cast(), component.description) + ) + default: + break + } + StateManager.blockUpdates = true; stack.wrappedValue.action = nil; StateManager.blockUpdates = false + storage.fields[.mainContent] = storages + for storage in storages { + if let component = storage.fields[Self.componentID] as? Component { + views.0(component) + .updateStorage( + storage, + modifiers: modifiers, + updateProperties: updateProperties, + type: AdwaitaMainView.self + ) + } else { + views.1 + .updateStorage( + storage, + modifiers: modifiers, + updateProperties: updateProperties, + type: AdwaitaMainView.self + ) + } + } + } + } /// A stack controls a navigation view. diff --git a/Sources/Adwaita/View/OverlaySplitView+.swift b/Sources/Adwaita/View/OverlaySplitView+.swift index 4fbbd47..ceea5b8 100644 --- a/Sources/Adwaita/View/OverlaySplitView+.swift +++ b/Sources/Adwaita/View/OverlaySplitView+.swift @@ -31,7 +31,7 @@ extension OverlaySplitView { public func trailingSidebar(_ trailing: Bool = true) -> Self { var newSelf = self newSelf.updateFunctions.append { storage, _, _ in - adw_overlay_split_view_set_sidebar_position(storage.pointer, trailing ? GTK_PACK_END : GTK_PACK_START) + adw_overlay_split_view_set_sidebar_position(storage.opaquePointer, trailing ? GTK_PACK_END : GTK_PACK_START) } return newSelf } diff --git a/Sources/Adwaita/View/Picture+.swift b/Sources/Adwaita/View/Picture+.swift index 17fa075..ba23c98 100644 --- a/Sources/Adwaita/View/Picture+.swift +++ b/Sources/Adwaita/View/Picture+.swift @@ -13,18 +13,22 @@ extension Picture { /// Load the picture from Foundation's `Data`. /// - Parameter data: The data. /// - Returns: The view. - public func data(_ data: Data?) -> View { - inspect { storage in - let pointer = storage.pointer - guard let data else { - gtk_picture_set_paintable(pointer, gdk_paintable_new_empty(0, 0)) - return + public func data(_ data: Data?) -> AnyView { + let oldData = "old-data" + return inspect { storage, updateProperties in + if updateProperties { + let pointer = storage.opaquePointer + guard let data, data != storage.fields[oldData] as? Data else { + gtk_picture_set_paintable(pointer, gdk_paintable_new_empty(0, 0)) + return + } + let bytes = data.withUnsafeBytes { ptr in + g_bytes_new(ptr.baseAddress, .init(data.count)) + } + let texture = gdk_texture_new_from_bytes(bytes, nil) + gtk_picture_set_paintable(pointer, texture) + storage.fields[oldData] = data } - let bytes = data.withUnsafeBytes { ptr in - g_bytes_new(ptr.baseAddress, .init(data.count)) - } - let texture = gdk_texture_new_from_bytes(bytes, nil) - gtk_picture_set_paintable(pointer, texture) } } diff --git a/Sources/Adwaita/View/StateWrapper.swift b/Sources/Adwaita/View/StateWrapper.swift deleted file mode 100644 index 81e0000..0000000 --- a/Sources/Adwaita/View/StateWrapper.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// StateWrapper.swift -// Adwaita -// -// Created by david-swift on 26.09.23. -// - -/// A storage for `@State` properties. -public struct StateWrapper: Widget { - - /// The content. - var content: () -> Body - /// The state information (from properties with the `State` wrapper). - var state: [String: StateProtocol] = [:] - - /// Initialize a `StateWrapper`. - /// - Parameter content: The view content. - public init(@ViewBuilder content: @escaping () -> Body) { - self.content = content - } - - /// Initialize a `StateWrapper`. - /// - Parameters: - /// - content: The view content. - /// - state: The state information. - init(content: @escaping () -> Body, state: [String: StateProtocol]) { - self.content = content - self.state = state - } - - /// Update a view storage. - /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - var updateProperties = updateProperties - for property in state { - if let storage = storage.state[property.key]?.content.storage { - property.value.content.storage = storage - } - if property.value.content.storage.update { - updateProperties = true - property.value.content.storage.update = false - } - } - if let storage = storage.content[.mainContent]?.first { - content() - .widget(modifiers: modifiers) - .update(storage, modifiers: modifiers, updateProperties: updateProperties) - } - } - - /// Get a view storage. - /// - Parameter modifiers: Modify views before being updated. - /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { - let content = content().widget(modifiers: modifiers).container(modifiers: modifiers) - return .init(content.pointer, content: [.mainContent: [content]], state: state) - } - -} diff --git a/Sources/Adwaita/View/StatusPage+.swift b/Sources/Adwaita/View/StatusPage+.swift index c5410ff..8e1c830 100644 --- a/Sources/Adwaita/View/StatusPage+.swift +++ b/Sources/Adwaita/View/StatusPage+.swift @@ -29,7 +29,7 @@ extension StatusPage { /// Make the status page more compact. /// - Parameter active: Whether the style is applied. /// - Returns: A view. - public func compact(_ active: Bool = true) -> View { + public func compact(_ active: Bool = true) -> AnyView { style("compact", active: active) } diff --git a/Sources/Adwaita/View/Text.swift b/Sources/Adwaita/View/Text.swift index e348dda..b963a17 100644 --- a/Sources/Adwaita/View/Text.swift +++ b/Sources/Adwaita/View/Text.swift @@ -21,8 +21,8 @@ extension Text { /// Set whether the text should ellipsize at the end. /// - Parameter ellipsize: Whether it should ellipsize. /// - Returns: The text widget. - public func ellipsize(_ ellipsize: Bool = true) -> View { - inspect { gtk_label_set_ellipsize($0.pointer, PANGO_ELLIPSIZE_END) } + public func ellipsize(_ ellipsize: Bool = true) -> AnyView { + inspect { storage, _ in gtk_label_set_ellipsize(storage.opaquePointer, PANGO_ELLIPSIZE_END) } } } diff --git a/Sources/Adwaita/View/VStack.swift b/Sources/Adwaita/View/VStack.swift index 467b721..0f32c89 100644 --- a/Sources/Adwaita/View/VStack.swift +++ b/Sources/Adwaita/View/VStack.swift @@ -23,14 +23,64 @@ extension VStack { self = self.append(content) if horizontal { appearFunctions.append { storage, _ in - gtk_orientable_set_orientation(storage.pointer, GTK_ORIENTATION_HORIZONTAL) + gtk_orientable_set_orientation(storage.opaquePointer, GTK_ORIENTATION_HORIZONTAL) } } } /// Link the children. - public func linked(_ active: Bool = true) -> View { + public func linked(_ active: Bool = true) -> AnyView { style("linked", active: active) } } + +/// A wrapper around ``VStack`` which applies the ``VStack`` only if there is more than one view. +public struct VStackWrapper: AdwaitaWidget, Wrapper { + + /// The content. + var content: Body + + /// Initialize the wrapper. + /// - Parameter content: The view content. + public init(@ViewBuilder content: () -> Body) { + self.content = content() + } + + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. + /// - Returns: The view storage. + public func container( + modifiers: [(AnyView) -> AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { + if content.count == 1, let element = content.first { + return element.storage(modifiers: modifiers, type: type) + } else { + return VStack { content }.storage(modifiers: modifiers, type: type) + } + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { + if content.count == 1, let element = content.first { + element.updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) + } else { + VStack { content } + .updateStorage(storage, modifiers: modifiers, updateProperties: updateProperties, type: type) + } + } + +} diff --git a/Sources/Adwaita/View/ViewStack.swift b/Sources/Adwaita/View/ViewStack.swift index 56d9cdc..aba84cf 100644 --- a/Sources/Adwaita/View/ViewStack.swift +++ b/Sources/Adwaita/View/ViewStack.swift @@ -8,12 +8,14 @@ import CAdw /// A widget holding multiple children but only displaying one. -public struct ViewStack: Widget { +public struct ViewStack: AdwaitaWidget { /// The stack's active content. var content: Body /// The stack's active identifier. var id: CustomStringConvertible + /// Whether the view stack allocates the same height and width for all children. + var homogeneous: Bool? /// Initialize the stack. /// - Parameters: @@ -39,36 +41,60 @@ public struct ViewStack: Widget { 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 { + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. + /// - Returns: The view storage. + public func container( + modifiers: [(AnyView) -> AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { let stack = gtk_stack_new() - let storage = ViewStorage(.init(stack)) - update(storage, modifiers: modifiers, updateProperties: true) + let storage = ViewStorage(stack?.opaque()) + update(storage, modifiers: modifiers, updateProperties: true, type: type) return storage } - /// Update a stack's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { - let widget = content.widget(modifiers: modifiers) + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { if let view = storage.content[id.description]?.first { - widget.updateStorage(view, modifiers: modifiers, updateProperties: updateProperties) + content.updateStorage(view, modifiers: modifiers, updateProperties: updateProperties, type: type) } else { - let view = widget.storage(modifiers: modifiers) - gtk_stack_add_named(storage.pointer, view.pointer?.cast(), id.description) + let view = content.storage(modifiers: modifiers, type: type) + gtk_stack_add_named(storage.opaquePointer, view.opaquePointer?.cast(), id.description) storage.content[id.description] = [view] } if let visibleView = storage.content[id.description]?.first { if let transition = visibleView.fields[.transition] as? Transition { - gtk_stack_set_transition_type(storage.pointer, transition.cTransition) + gtk_stack_set_transition_type(storage.opaquePointer, transition.cTransition) + } + if (storage.previousState as? Self)?.id.description != id.description { + gtk_stack_set_visible_child_name(storage.opaquePointer, id.description) } - gtk_stack_set_visible_child_name(storage.pointer, id.description) } + if let homogeneous, homogeneous != (storage.previousState as? Self)?.homogeneous { + gtk_stack_set_vhomogeneous(storage.opaquePointer, homogeneous.cBool) + gtk_stack_set_hhomogeneous(storage.opaquePointer, homogeneous.cBool) + } + storage.previousState = self + } + + /// Whether the view stack allocates the same height and width for all children. + /// - Parameter homogeneous: Whether this is enabled. + /// - Returns: The stack. + public func homogeneous(_ homogeneous: Bool? = nil) -> Self { + modify { $0.homogeneous = homogeneous } } } diff --git a/Sources/Adwaita/View/ViewSwitcher.swift b/Sources/Adwaita/View/ViewSwitcher.swift index fb4d8c5..dc3bb62 100644 --- a/Sources/Adwaita/View/ViewSwitcher.swift +++ b/Sources/Adwaita/View/ViewSwitcher.swift @@ -10,7 +10,7 @@ import CAdw /// A picker used for indicating multiple views. /// /// It normally controls a `ViewStack` (e.g. via `switch` statements). -public struct ViewSwitcher: Widget where Element: ViewSwitcherOption { +public struct ViewSwitcher: AdwaitaWidget where Element: ViewSwitcherOption { /// The selected element. @Binding var selection: Element @@ -23,51 +23,68 @@ public struct ViewSwitcher: Widget where Element: ViewSwitcherOption { self._selection = selection } - /// Get a view switcher's view storage. - /// - Parameter modifiers: Modify views before being updated. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { - let switcher = ViewStorage(.init(adw_view_switcher_new())) - let stack = ViewStorage(.init(adw_view_stack_new())) - adw_view_switcher_set_stack(switcher.pointer, stack.pointer) + public func container( + modifiers: [(AnyView) -> AnyView], + type: Data.Type + ) -> ViewStorage where Data: ViewRenderData { + let switcher = ViewStorage(adw_view_switcher_new()?.opaque()) + let stack = ViewStorage(adw_view_stack_new()?.opaque()) + adw_view_switcher_set_stack(switcher.opaquePointer, stack.opaquePointer) for option in Element.allCases { adw_view_stack_add_titled_with_icon( - stack.pointer, + stack.opaquePointer, gtk_label_new(""), option.title, option.title, option.icon.string ) } - stack.notify(name: "visible-child") { - if let title = adw_view_stack_get_visible_child_name(stack.pointer), - let option = Element(title: .init(cString: title)) { - selection = option - } - } updateSwitcher(switcher: switcher) switcher.fields["stack"] = stack return switcher } - /// Update a view switcher's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: Modify views before being updated. - /// - updateProperties: Whether to update properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated + /// - updateProperties: Whether to update the view's properties. + /// - type: The type of the app storage. + public func update( + _ storage: ViewStorage, + modifiers: [(AnyView) -> AnyView], + updateProperties: Bool, + type: Data.Type + ) where Data: ViewRenderData { updateSwitcher(switcher: storage) } /// Update a view switcher's style and selection. /// - Parameter switcher: The view switcher. func updateSwitcher(switcher: ViewStorage) { - adw_view_switcher_set_policy( - switcher.pointer, - wide ? ADW_VIEW_SWITCHER_POLICY_WIDE : ADW_VIEW_SWITCHER_POLICY_NARROW - ) - let stack = adw_view_switcher_get_stack(switcher.pointer) - adw_view_stack_set_visible_child_name(stack, selection.title) + let stack = switcher.fields["stack"] as? ViewStorage + stack?.notify(name: "visible-child") { + if let title = adw_view_stack_get_visible_child_name(stack?.opaquePointer), + let option = Element(title: .init(cString: title)) { + selection = option + } + } + if (switcher.previousState as? Self)?.wide != wide { + adw_view_switcher_set_policy( + switcher.opaquePointer, + wide ? ADW_VIEW_SWITCHER_POLICY_WIDE : ADW_VIEW_SWITCHER_POLICY_NARROW + ) + } + if (switcher.previousState as? Self)?.selection.title != selection.title { + let stack = adw_view_switcher_get_stack(switcher.opaquePointer) + adw_view_stack_set_visible_child_name(stack, selection.title) + } + switcher.previousState = self } /// Set whether to use the wide design. diff --git a/Sources/Adwaita/Window/AboutWindow.swift b/Sources/Adwaita/Window/AboutWindow.swift deleted file mode 100644 index 09ef989..0000000 --- a/Sources/Adwaita/Window/AboutWindow.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// AboutWindow.swift -// Adwaita -// -// Created by david-swift on 05.12.23. -// - -import CAdw -import Foundation - -/// A structure representing an about window. -public struct AboutWindow: WindowScene { - - /// The window's identifier. - public var id: String - /// Whether an instance of the window type should be opened when the app is starting up. - public let `open` = 0 - /// The identifier of the window's parent. - public var parentID: String? - /// The keyboard shortcuts on the app level. - public var appShortcuts: [String: (GTUIApp) -> Void] = [:] - /// The app's name. - var appName: String? - /// The developer's name. - var developer: String? - /// The app version. - var version: String? - /// The app icon. - var icon: Icon? - /// The app's website. - var website: URL? - /// The link for opening issues. - var issues: URL? - /// The path to the app data file. - var path: URL? - - /// Create a window type with a certain identifier and content. - /// - Parameters: - /// - id: The identifier. - /// - appName: The app's name. - /// - developer: The developer's name. - /// - version: The app version. - public init(id: String, appName: String, developer: String, version: String) { - self.id = id - self.appName = appName - self.developer = developer - self.version = version - } - - /// Create a window type with a certain identifier and content. - /// - Parameters: - /// - id: The identifier. - /// - path: The path to the app data file. - public init(id: String, path: URL) { - self.id = id - self.path = path - } - - /// Set the app icon. - /// - Parameter icon: The app icon. - /// - Returns: The window. - public func icon(_ icon: Icon) -> Self { - var newSelf = self - newSelf.icon = icon - return newSelf - } - - /// Set the app's website. - /// - Parameter url: The app's website. - /// - Returns: The window. - public func website(_ url: URL?) -> Self { - var newSelf = self - newSelf.website = url - return newSelf - } - - /// Set the app's website. - /// - Parameter url: The URL to the issue tracker. - /// - Returns: The window. - public func issues(_ url: URL?) -> Self { - var newSelf = self - newSelf.issues = url - return newSelf - } - - /// Get the storage for the window. - /// - Parameter app: The application. - /// - Returns: The storage. - public func createWindow(app: GTUIApp) -> WindowStorage { - let window = createGTUIWindow(app: app) - let windowStorage = WindowStorage(id: id, window: window, view: nil) - windowStorage.parentID = parentID - return windowStorage - } - - /// Get the window. - /// - Parameter app: The application. - /// - Returns: The window. - func createGTUIWindow(app: GTUIApp) -> GTUIAboutWindow { - let window: GTUIAboutWindow - if let path { - window = .init(filePath: path.path) - } else { - window = .init() - } - updateAppShortcuts(app: app) - updateData(window: window) - window.show() - return window - } - - /// Update a window. - /// - Parameters: - /// - storage: The storage to update. - /// - app: The application. - /// - force: Whether to force update all the views. - public func update(_ storage: WindowStorage, app: GTUIApp, force: Bool) { - updateAppShortcuts(app: app) - storage.destroy = true - } - - /// Update the data for a window. - /// - Parameter window: The window. - func updateData(window: GTUIAboutWindow) { - if let appName, let icon, let developer, let version { - window.generalData(title: appName, icon: icon, developer: developer, version: version) - } - if let website { window.website(url: website.absoluteString) } - if let issues { window.issues(url: issues.absoluteString) } - } - -} diff --git a/Sources/Adwaita/Window/FileDialog.swift b/Sources/Adwaita/Window/FileDialog.swift deleted file mode 100644 index 3bb4083..0000000 --- a/Sources/Adwaita/Window/FileDialog.swift +++ /dev/null @@ -1,122 +0,0 @@ -// -// FileDialog.swift -// Adwaita -// -// Created by david-swift on 08.12.23. -// - -// swiftlint:disable discouraged_optional_collection - -import Foundation - -/// A structure representing a file dialog window. -public struct FileDialog: WindowScene { - - /// The window's identifier. - public var id: String - /// Whether the window is an importer. - public var importer = true - /// Whether an instance of the window type should be opened when the app is starting up. - public var open: Int { 0 } - /// The identifier of the window's parent. - public var parentID: String? - /// The keyboard shortcuts on the app level. - public var appShortcuts: [String: (GTUIApp) -> Void] = [:] - /// The initial folder. - var initialFolder: URL? - /// The initial file name for the file exporter. - var initialName: String? - /// The accepted extensions for the file importer. - var extensions: [String]? - /// The closure to run when the import or export is successful. - var result: ((URL) -> Void)? - /// The closure to run when the import or export is not successful. - var cancel: (() -> Void)? - - /// Create an importer file dialog window. - /// - Parameters: - /// - importer: The window's identifier. - /// - initialFolder: The URL to the folder open when being opened. - /// - extensions: The accepted file extensions. - /// - folders: Whether folders are accepted. - /// - onOpen: Run this when a file for importing has been chosen. - /// - onClose: Run this when the user cancelled the action. - public init( - importer: String, - initialFolder: URL? = nil, - extensions: [String]? = nil, - onOpen: @escaping (URL) -> Void, - onClose: @escaping () -> Void - ) { - self.id = importer - self.initialFolder = initialFolder - self.extensions = extensions - self.result = onOpen - self.cancel = onClose - } - - /// Create an exporter file dialog window. - /// - Parameters: - /// - exporter: The window's identifier. - /// - initialFolder: The URL to the folder open when being opened. - /// - initialName: The default file name. - /// - onSave: Run this when a path for exporting has been chosen. - /// - onClose: Run this when the user cancelled the action. - public init( - exporter: String, - initialFolder: URL? = nil, - initialName: String? = nil, - onSave: @escaping (URL) -> Void, - onClose: @escaping () -> Void - ) { - self.id = exporter - self.importer = false - self.initialFolder = initialFolder - self.initialName = initialName - self.result = onSave - self.cancel = onClose - } - - /// Get the storage for the window. - /// - Parameter app: The application. - /// - Returns: The storage. - public func createWindow(app: GTUIApp) -> WindowStorage { - let window = GTUIFileDialog() - let windowStorage = WindowStorage(id: id, window: window, view: nil) - windowStorage.parentID = parentID - update(window: window) - return windowStorage - } - - /// Update a window. - /// - Parameters: - /// - storage: The storage to update. - /// - app: The application. - public func update(_ storage: WindowStorage, app: GTUIApp, force: Bool) { - updateAppShortcuts(app: app) - if let window = storage.window as? GTUIFileDialog { - update(window: window) - } - storage.destroy = true - } - - /// Update the window. - /// - Parameter window: The window. - func update(window: GTUIFileDialog) { - window.isImporter = importer - window.folder = initialFolder - if let initialName { - window.setInitialName(initialName) - } - window.setExtensions(extensions) - if let result { - window.onResult = result - } - if let cancel { - window.onCancel = cancel - } - } - -} - -// swiftlint:enable discouraged_optional_collection diff --git a/Sources/Adwaita/Window/Window.swift b/Sources/Adwaita/Window/Window.swift index 943f194..ef0cd87 100644 --- a/Sources/Adwaita/Window/Window.swift +++ b/Sources/Adwaita/Window/Window.swift @@ -12,113 +12,71 @@ import Foundation /// A structure representing an application window type. /// -/// Note that multiple instances of a window can be opened at the same time. -public struct Window: WindowScene { +/// Note that it may be possible to open multiple instances of a window at the same time. +public struct Window: AdwaitaSceneElement { /// The window's identifier. public var id: String /// The window's content. - var content: (GTUIApplicationWindow) -> Body + var content: (AdwaitaWindow) -> Body /// Whether an instance of the window type should be opened when the app is starting up. - public var `open`: Int - /// The identifier of the window's parent. - public var parentID: String? + var `open`: Int /// The keyboard shortcuts. - var shortcuts: [String: (GTUIApplicationWindow) -> Void] = [:] + var shortcuts: [String: (AdwaitaWindow) -> Void] = [:] /// The keyboard shortcuts on the app level. - public var appShortcuts: [String: (GTUIApp) -> Void] = [:] + var appShortcuts: [String: (AdwaitaApp) -> Void] = [:] /// The window's title. var title: String? /// Whether the window is resizable. - var resizable = true + var resizable: Bool? /// Whether the window is deletable. - var deletable = true - /// The signals for the importers and exporters. - var signals: [Signal] = [] + var deletable: Bool? /// 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 + /// The window's default width. + var defaultWidth: Int? + /// The window's default height. + var defaultHeight: Int? /// Whether the window is maximized. var maximized: Binding? /// Whether the window uses the development style. - var devel = false + var devel: Bool? /// Create a window type with a certain identifier and user interface. /// - Parameters: /// - id: The identifier. /// - open: The number of instances of the window type when the app is starting. /// - content: The window's content. - public init(id: String, `open`: Int = 1, @ViewBuilder content: @escaping (GTUIApplicationWindow) -> Body) { + public init(id: String, `open`: Int = 1, @ViewBuilder content: @escaping (AdwaitaWindow) -> Body) { self.content = content self.id = id self.open = open } - /// Get the storage for the window. - /// - Parameter app: The application. - /// - Returns: The storage. - public func createWindow(app: GTUIApp) -> WindowStorage { - let window = createGTUIWindow(app: app) - let storage = getViewStorage(window: window) - let windowStorage = WindowStorage(id: id, window: window, view: storage) - window.observeHide { - windowStorage.destroy = true - } - windowStorage.parentID = parentID - if devel { - gtk_widget_add_css_class(window.pointer?.cast(), "devel") - } - return windowStorage - } + /// An AdwApplicationWindow. + public class AdwaitaWindow { - /// Get the window. - /// - Parameter app: The application. - /// - Returns: The window. - func createGTUIWindow(app: GTUIApp) -> GTUIApplicationWindow { - let window = GTUIApplicationWindow(app: app) - updateAppShortcuts(app: app) - window.show() - return window - } + /// The pointer to the window. + let pointer: UnsafeMutablePointer? + /// Fields for storing signal data. + var signals: [String: SignalData] = [:] + /// The App. + let app: AdwaitaApp - /// Get the storage of the content view. - /// - Parameter window: The window. - /// - 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) - setProperties(window: window, template: template) - updateShortcuts(window: window, template: template) - window.setDefaultSize(width: template.width?.wrappedValue, height: template.height?.wrappedValue) - return storage - } - - /// Update a window storage's content. - /// - Parameters: - /// - storage: The storage to update. - /// - app: The GTUI app. - /// - force: Whether to force update all the views. - 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, template: template) - updateShortcuts(window: window, template: template) - updateAppShortcuts(app: app) + /// Initialize the application window. + /// - Parameter app: The application. + init(app: AdwaitaApp) { + self.app = app + pointer = adw_application_window_new(app.pointer)?.cast() } - for signal in signals where signal.update { - Idle { - app.showWindow(signal.id.uuidString) - } + + /// Close the window. + public func close() { + gtk_window_close(pointer?.cast()) } + } /// Get the actual window template. @@ -132,157 +90,190 @@ public struct Window: WindowScene { return windowTemplate } - /// Set some general propreties of the window. - /// - Parameters: - /// - window: The window. - /// - template: The window template. - func setProperties(window: GTUIApplicationWindow, template: Self) { - if let title = template.title { - window.setTitle(title) + /// Set up the initial scene storages. + /// - Parameter app: The app storage. + public func setupInitialContainers(app: Storage) where Storage: AppStorage { + for _ in 0..(app: Storage) -> SceneStorage where Storage: AppStorage { + guard let app = app as? AdwaitaApp else { + return .init(id: id, pointer: nil) { } + } + let window = AdwaitaWindow(app: app) + let content = content(window) + let storage = SceneStorage(id: id, pointer: window) { + gtk_window_present(window.pointer?.cast()) + } + let viewStorage = content.storage(modifiers: [], type: AdwaitaMainView.self) + adw_application_window_set_content(window.pointer?.cast(), viewStorage.opaquePointer?.cast()) + storage.content[.mainContent] = [viewStorage] + let data = SignalData { + storage.destroy = true } - window.setResizability(template.resizable) - window.setDeletability(template.deletable) - if !(template.maximized?.wrappedValue ?? false) { - gtk_window_unmaximize(window.pointer) + let observeID = "destroy" + data.connect(pointer: window.pointer, signal: observeID) + window.signals[observeID] = data + let template = getTemplate(content: content) + let width = template.width?.wrappedValue ?? template.defaultWidth ?? -1 + let height = template.height?.wrappedValue ?? template.defaultHeight ?? -1 + gtk_window_set_default_size(window.pointer?.cast(), .init(width), .init(height)) + update(storage, app: app, updateProperties: true) + return storage + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - app: The app storage. + /// - updateProperties: Whether to update the view's properties. + public func update( + _ storage: SceneStorage, + app: Storage, + updateProperties: Bool + ) where Storage: AppStorage { + if Thread.isMainThread { + privateUpdate(storage, app: app, updateProperties: updateProperties) } else { - gtk_window_maximize(window.pointer) + Idle { + privateUpdate(storage, app: app, updateProperties: updateProperties) + } } - var storage = window.fields["storage"] as? ViewStorage - if storage == nil { - storage = .init(window.pointer?.opaque()) - window.fields["storage"] = storage + } + + /// Update the stored content. + /// - Parameters: + /// - storage: The storage to update. + /// - app: The app storage. + /// - updateProperties: Whether to update the view's properties. + private func privateUpdate( + _ storage: SceneStorage, + app: Storage, + updateProperties: Bool + ) where Storage: AppStorage { + guard let window = storage.pointer as? AdwaitaWindow, + let viewStorage = storage.content[.mainContent]?.first else { + return } - if template.setDefaultSize { - window.setDefaultSize(width: template.width?.wrappedValue, height: template.height?.wrappedValue) + let content = content(window) + content + .updateStorage(viewStorage, modifiers: [], updateProperties: updateProperties, type: AdwaitaMainView.self) + let template = getTemplate(content: content) + if let app = app as? AdwaitaApp { + for shortcut in template.shortcuts { + app.addKeyboardShortcut(shortcut.key, id: shortcut.key, window: window) { shortcut.value(window) } + } + for shortcut in template.appShortcuts { + app.addKeyboardShortcut(shortcut.key, id: shortcut.key) { shortcut.value(app) } + } } if template.width != nil { - storage?.notify(name: "default-width") { - updateSize(window: window, template: template) + storage.notify(name: "default-width", pointer: window.pointer?.opaque()) { + template.width?.wrappedValue = getDefaultSize(window: window).width } } if template.height != nil { - storage?.notify(name: "default-height") { - updateSize(window: window, template: template) + storage.notify(name: "default-height", pointer: window.pointer?.opaque()) { + template.height?.wrappedValue = getDefaultSize(window: window).height } } if template.maximized != nil { - storage?.notify(name: "maximized") { - updateSize(window: window, template: template) + storage.notify(name: "maximized", pointer: window.pointer?.opaque()) { + template.maximized?.wrappedValue = gtk_window_is_maximized(window.pointer?.cast()) != 0 } } + if updateProperties { + template.updateProperties(storage: storage, window: window) + } } - /// Update the window's size. + /// Update the properties of the windows. /// - Parameters: + /// - storage: The scene storage. /// - 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) - let maximized = gtk_window_is_maximized(window.pointer) != 0 - if width != template.width?.wrappedValue ?? -1 { - template.width?.wrappedValue = .init(width) + func updateProperties(storage: SceneStorage, window: AdwaitaWindow) { + let previousState = storage.previousState as? Self + if let title, previousState?.title != title { + gtk_window_set_title(window.pointer?.cast(), title) + } + if let resizable, previousState?.resizable != resizable { + gtk_window_set_resizable(window.pointer?.cast(), resizable.cBool) } - if height != template.height?.wrappedValue ?? -1 { - template.height?.wrappedValue = .init(height) + if let deletable, previousState?.deletable != deletable { + gtk_window_set_deletable(window.pointer?.cast(), deletable.cBool) + } + if let devel, previousState?.devel != devel { + if devel { + gtk_widget_add_css_class(window.pointer?.cast(), "devel") + } else { + gtk_widget_remove_css_class(window.pointer?.cast(), "devel") + } } - if maximized != template.maximized?.wrappedValue { - template.maximized?.wrappedValue = maximized + if width != nil || height != nil { + gtk_window_set_default_size( + window.pointer?.cast(), + .init(width?.wrappedValue ?? -1), + .init(height?.wrappedValue ?? -1) + ) } + if let maximized = maximized?.wrappedValue { + if maximized { + gtk_window_maximize(window.pointer?.cast()) + } else { + gtk_window_unmaximize(window.pointer?.cast()) + } + } + storage.previousState = self } - /// Add windows that overlay the last instance of this window if presented. - /// - Parameter windows: The windows. - /// - Returns: The new windows and this window. - public func overlay(@SceneBuilder windows: () -> [WindowSceneGroup]) -> [WindowScene] { - windows().windows().map { window in - var newWindow = window - newWindow.parentID = id - return newWindow - } + [self] + /// Get the window's default size. + /// - Parameter window: The window. + /// - Returns: The dimensions. + func getDefaultSize(window: AdwaitaWindow) -> (width: Int, height: Int) { + var width: Int32 = 0 + var height: Int32 = 0 + gtk_window_get_default_size(window.pointer?.cast(), &width, &height) + return (width: .init(width), height: .init(height)) } - /// Add an importer file dialog to the window. + /// Add a keyboard shortcut. /// - Parameters: - /// - signal: The signal for opening the dialog. - /// - initialFolder: The URL to the folder open when being opened. - /// - extensions: The accepted file extensions. - /// - folders: Whether folders are accepted. - /// - onOpen: Run this when a file for importing has been chosen. - /// - onClose: Run this when the user cancelled the action. - public func fileImporter( - _ signal: Signal, - initialFolder: URL? = nil, - extensions: [String]? = nil, - onOpen: @escaping (URL) -> Void, - onClose: @escaping () -> Void - ) -> Scene { + /// - shortcut: The keyboard shortcut. + /// - action: The closure to execute when the keyboard shortcut is pressed. + /// - Returns: The window. + public func keyboardShortcut(_ shortcut: String, action: @escaping (AdwaitaWindow) -> Void) -> Self { var newSelf = self - newSelf.signals.append(signal) + newSelf.shortcuts[shortcut] = action return newSelf - .overlay { - FileDialog( - importer: signal.id.uuidString, - initialFolder: initialFolder, - extensions: extensions, - onOpen: onOpen, - onClose: onClose - ) - } } - /// Add an exporter file dialog to the window. - /// - Parameters: - /// - signal: The signal for opening the dialog. - /// - initialFolder: The URL to the folder open when being opened. - /// - initialName: The default file name. - /// - onSave: Run this when a path for exporting has been chosen. - /// - onClose: Run this when the user cancelled the action. - public func fileExporter( - _ signal: Signal, - initialFolder: URL? = nil, - initialName: String? = nil, - onSave: @escaping (URL) -> Void, - onClose: @escaping () -> Void - ) -> Scene { - var newSelf = self - newSelf.signals.append(signal) - return newSelf - .overlay { - FileDialog( - exporter: signal.id.uuidString, - initialFolder: initialFolder, - initialName: initialName, - onSave: onSave, - onClose: onClose - ) - } + /// Add the shortcut "w" which closes the window. + /// - Returns: The window. + public func closeShortcut() -> Self { + keyboardShortcut("w".ctrl()) { $0.close() } } - /// Add a keyboard shortcut. + /// Add a keyboard shortcut for the whole application. /// - Parameters: /// - shortcut: The keyboard shortcut. /// - action: The closure to execute when the keyboard shortcut is pressed. /// - Returns: The window. - public func keyboardShortcut(_ shortcut: String, action: @escaping (GTUIApplicationWindow) -> Void) -> Self { + public func appKeyboardShortcut(_ shortcut: String, action: @escaping (AdwaitaApp) -> Void) -> Self { var newSelf = self - newSelf.shortcuts[shortcut] = action + newSelf.appShortcuts[shortcut] = action return newSelf } - /// Update the keyboard 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) } - } - } - - /// Add the shortcut "w" which closes the window. + /// Add the shortcut "q" which quits the application. /// - Returns: The window. - public func closeShortcut() -> Self { - keyboardShortcut("w".ctrl()) { $0.close() } + public func quitShortcut() -> Self { + appKeyboardShortcut("q".ctrl()) { $0.quit() } } /// Set the window's default size. @@ -290,18 +281,17 @@ public struct Window: WindowScene { /// - width: The window's width. /// - height: The window's height. /// - Returns: The window. - public func defaultSize(width: Int, height: Int) -> Self { + public func defaultSize(width: Int? = nil, height: Int? = nil) -> Self { var newSelf = self - newSelf.width = .constant(width) - newSelf.height = .constant(height) - newSelf.setDefaultSize = true + newSelf.defaultWidth = width + newSelf.defaultHeight = height return newSelf } /// Set the window's title. /// - Parameter title: The title. /// - Returns: The window. - public func title(_ title: String) -> Self { + public func title(_ title: String?) -> Self { var newSelf = self newSelf.title = title return newSelf @@ -310,7 +300,7 @@ public struct Window: WindowScene { /// Set whether the window is resizable. /// - Parameter resizable: The resizability. /// - Returns: The window. - public func resizable(_ resizable: Bool) -> Self { + public func resizable(_ resizable: Bool?) -> Self { var newSelf = self newSelf.resizable = resizable return newSelf @@ -319,7 +309,7 @@ public struct Window: WindowScene { /// Set whether the window is deletable. /// - Parameter resizable: The deletability. /// - Returns: The window. - public func deletable(_ deletable: Bool) -> Self { + public func deletable(_ deletable: Bool?) -> Self { var newSelf = self newSelf.deletable = deletable return newSelf @@ -340,7 +330,7 @@ public struct Window: WindowScene { /// Get and set whether the window is maximized. /// - Parameter maximized: Whether the window is maximized. /// - Returns: The window. - public func maximized(_ maximized: Binding) -> Self { + public func maximized(_ maximized: Binding?) -> Self { var newSelf = self newSelf.maximized = maximized return newSelf @@ -349,7 +339,7 @@ public struct Window: WindowScene { /// Whether the window used the development style. /// - Parameter active: Whether the style is active. /// - Returns: The window. - public func devel(_ active: Bool = true) -> Self { + public func devel(_ active: Bool? = true) -> Self { var newSelf = self newSelf.devel = active return newSelf @@ -358,3 +348,6 @@ public struct Window: WindowScene { } // swiftlint:enable discouraged_optional_collection + +/// An AdwApplicationWindow. +public typealias AdwaitaWindow = Window.AdwaitaWindow diff --git a/Sources/CAdw/shim.h b/Sources/CAdw/shim.h index 33f2be2..4a08ff1 100644 --- a/Sources/CAdw/shim.h +++ b/Sources/CAdw/shim.h @@ -27,7 +27,8 @@ static void gtui_filedialog_open_finish (uint64_t dialog, uint64_t result, uint64_t data) { GFile *file = gtk_file_dialog_open_finish (dialog, result, NULL); - const char *path = g_file_get_path (file); + const char *path = g_file_peek_path (file); + g_object_unref (file); filedialog_on_open_cb (dialog, path, data); } diff --git a/Tests/AlertDialogDemo.swift b/Sources/Demo/AlertDialogDemo.swift similarity index 100% rename from Tests/AlertDialogDemo.swift rename to Sources/Demo/AlertDialogDemo.swift diff --git a/Tests/CarouselDemo.swift b/Sources/Demo/CarouselDemo.swift similarity index 100% rename from Tests/CarouselDemo.swift rename to Sources/Demo/CarouselDemo.swift diff --git a/Tests/CounterDemo.swift b/Sources/Demo/CounterDemo.swift similarity index 91% rename from Tests/CounterDemo.swift rename to Sources/Demo/CounterDemo.swift index f2a19b5..1e86722 100644 --- a/Tests/CounterDemo.swift +++ b/Sources/Demo/CounterDemo.swift @@ -11,8 +11,7 @@ import Adwaita struct CounterDemo: View { - @State("count", folder: "io.github.AparokshaUI.Demo/count") - private var count = 0 + @State private var count = 0 var view: Body { VStack { diff --git a/Sources/Demo/Demo.swift b/Sources/Demo/Demo.swift new file mode 100644 index 0000000..b5bb6b2 --- /dev/null +++ b/Sources/Demo/Demo.swift @@ -0,0 +1,170 @@ +// +// Demo.swift +// Adwaita +// +// Created by david-swift on 25.09.23. +// + +// swiftlint:disable missing_docs implicitly_unwrapped_optional no_magic_numbers + +import Adwaita +import Foundation + +@main +struct Demo: App { + + let id = "io.github.AparokshaUI.Demo" + var app: AdwaitaApp! + + @State private var pictureURL: URL? + + var scene: Scene { + Window(id: "main") { window in + DemoContent(window: window, app: app, pictureURL: pictureURL) + } + .devel() + helperWindows + } + + @SceneBuilder var helperWindows: Scene { + Window(id: "content", open: 0) { _ in + WindowsDemo.WindowContent() + } + .resizable(false) + .closeShortcut() + .defaultSize(width: 400, height: 250) + Window(id: "toolbar-demo", open: 0) { _ in + ToolbarDemo.WindowContent().stopModifiers() + } + .closeShortcut() + .defaultSize(width: 400, height: 250) + .title("Toolbar Demo") + Window(id: "switcher-demo", open: 0) { _ in + ViewSwitcherDemo.WindowContent() + } + .closeShortcut() + .defaultSize(width: 600, height: 400) + .resizable(false) + .title("View Switcher Demo") + Window(id: "form-demo", open: 0) { _ in + FormDemo.WindowContent() + } + .closeShortcut() + .defaultSize(width: 400, height: 250) + .title("Form Demo") + Window(id: "password-checker-demo", open: 0) { _ in + PasswordCheckerDemo.WindowContent() + } + .closeShortcut() + .defaultSize(width: 400, height: 250) + .title("Password Checker Demo") + + Window(id: "navigation", open: 0) { _ in + NavigationViewDemo.WindowContent() + } + .closeShortcut() + .title("Navigation View Demo") + } + + struct DemoContent: WindowView { + + @State private var selection: Page = .welcome + @State private var toast: Signal = .init() + @State private var sidebarVisible = true + @State private var width = 650 + @State private var height = 550 + @State private var maximized = false + @State private var about = false + var window: AdwaitaWindow + var app: AdwaitaApp! + var pictureURL: URL? + + var view: Body { + OverlaySplitView(visible: $sidebarVisible) { + ScrollView { + List(Page.allCases, selection: $selection) { element in + Text(element.label) + .halign(.start) + .padding() + } + .sidebarStyle() + } + .topToolbar { + HeaderBar.end { + menu + } + .headerBarTitle { + WindowTitle(subtitle: "", title: "Demo") + } + } + } content: { + StatusPage( + selection.label, + icon: selection.icon, + description: selection.description + ) { selection.view(app: app, window: window, toast: toast) } + .topToolbar { + HeaderBar { + Toggle(icon: .default(icon: .sidebarShow), isOn: $sidebarVisible) + .tooltip("Toggle Sidebar") + } end: { + if sidebarVisible { + Text("").transition(.crossfade) + } else { + menu.transition(.crossfade) + } + } + .headerBarTitle { + if sidebarVisible { + Text("") + .transition(.crossfade) + } else { + WindowTitle(subtitle: "Demo", title: selection.label) + .transition(.crossfade) + } + } + } + .toast("This is a toast!", signal: toast) + } + .aboutDialog( + visible: $about, + app: "Demo", + developer: "david-swift", + version: "Test", + icon: .default(icon: .applicationXExecutable), + website: .init(string: "https://aparokshaui.github.io/adwaita-swift/"), + issues: .init(string: "https://github.com/AparokshaUI/adwaita-swift/issues") + ) + } + + var menu: AnyView { + Menu(icon: .default(icon: .openMenu), app: app, window: window) { + MenuButton("New Window", window: false) { + app.addWindow("main") + } + .keyboardShortcut("n".ctrl()) + MenuButton("Close Window") { + window.close() + } + .keyboardShortcut("w".ctrl()) + MenuSection { + MenuButton("About") { about = true } + MenuButton("Quit", window: false) { app.quit() } + .keyboardShortcut("q".ctrl()) + } + } + .primary() + .tooltip("Main Menu") + } + + func window(_ window: Window) -> Window { + window + .size(width: $width, height: $height) + .maximized($maximized) + } + + } + +} + +// swiftlint:enable missing_docs implicitly_unwrapped_optional no_magic_numbers diff --git a/Tests/DialogDemo.swift b/Sources/Demo/DialogDemo.swift similarity index 100% rename from Tests/DialogDemo.swift rename to Sources/Demo/DialogDemo.swift diff --git a/Tests/DiceDemo.swift b/Sources/Demo/DiceDemo.swift similarity index 100% rename from Tests/DiceDemo.swift rename to Sources/Demo/DiceDemo.swift diff --git a/Tests/FixedDemo.swift b/Sources/Demo/FixedDemo.swift similarity index 100% rename from Tests/FixedDemo.swift rename to Sources/Demo/FixedDemo.swift diff --git a/Tests/FlowBoxDemo.swift b/Sources/Demo/FlowBoxDemo.swift similarity index 100% rename from Tests/FlowBoxDemo.swift rename to Sources/Demo/FlowBoxDemo.swift diff --git a/Tests/FormDemo.swift b/Sources/Demo/FormDemo.swift similarity index 91% rename from Tests/FormDemo.swift rename to Sources/Demo/FormDemo.swift index 6753278..6ac90b7 100644 --- a/Tests/FormDemo.swift +++ b/Sources/Demo/FormDemo.swift @@ -11,7 +11,7 @@ import Adwaita struct FormDemo: View { - var app: GTUIApp + var app: AdwaitaApp var view: Body { VStack { @@ -42,9 +42,9 @@ struct FormDemo: View { Form { EntryRow("Entry Row", text: $text) .suffix { - Button(icon: .default(icon: .editCopy)) { State.copy(text) } + Button(icon: .default(icon: .editCopy)) { AdwaitaApp.copy(text) } .flat() - .verticalCenter() + .valign(.center) } EntryRow(password, text: $password) .secure(text: $password) @@ -70,20 +70,20 @@ struct FormDemo: View { } } - var actionRows: View { + var actionRows: AnyView { Form { ActionRow("Rows have a title") .subtitle(text) ActionRow("Rows can have suffix widgets") .suffix { Button("Action") { } - .verticalCenter() + .valign(.center) } } .padding() } - func rowDemo(_ title: String, row: View) -> View { + func rowDemo(_ title: String, row: AnyView) -> AnyView { FormSection(title) { Form { row diff --git a/Tests/IdleDemo.swift b/Sources/Demo/IdleDemo.swift similarity index 82% rename from Tests/IdleDemo.swift rename to Sources/Demo/IdleDemo.swift index 457b93d..d97d644 100644 --- a/Tests/IdleDemo.swift +++ b/Sources/Demo/IdleDemo.swift @@ -11,8 +11,10 @@ import Adwaita struct IdleDemo: View { - @State private var progress = 0.0 - @State private var activeProcess = false + @State(id: "progress") + private var progress = 0.0 + @State(id: "active-process") + private var activeProcess = false let max = 500.0 let delayFactor = 5_000.0 let maxWidth = 300 @@ -23,9 +25,9 @@ struct IdleDemo: View { .valign(.center) .frame(maxWidth: maxWidth) Button("Play") { - activeProcess = true - progress = 0 Task { + activeProcess = true + progress = 0 Idle(delay: .init(delayFactor / max)) { progress += 1 let done = progress == max diff --git a/Tests/ListDemo.swift b/Sources/Demo/ListDemo.swift similarity index 100% rename from Tests/ListDemo.swift rename to Sources/Demo/ListDemo.swift diff --git a/Tests/NavigationViewDemo.swift b/Sources/Demo/NavigationViewDemo.swift similarity index 98% rename from Tests/NavigationViewDemo.swift rename to Sources/Demo/NavigationViewDemo.swift index db41ea4..bcc83ed 100644 --- a/Tests/NavigationViewDemo.swift +++ b/Sources/Demo/NavigationViewDemo.swift @@ -11,7 +11,7 @@ import Adwaita struct NavigationViewDemo: View { - var app: GTUIApp + var app: AdwaitaApp var view: Body { VStack { diff --git a/Tests/Page.swift b/Sources/Demo/Page.swift similarity index 96% rename from Tests/Page.swift rename to Sources/Demo/Page.swift index 4582549..61f3807 100644 --- a/Tests/Page.swift +++ b/Sources/Demo/Page.swift @@ -110,7 +110,7 @@ enum Page: String, Identifiable, CaseIterable, Codable, CustomStringConvertible // swiftlint:disable cyclomatic_complexity @ViewBuilder - func view(app: GTUIApp!, window: GTUIApplicationWindow, toast: Signal, pictureURL: URL?) -> Body { + func view(app: AdwaitaApp!, window: AdwaitaWindow, toast: Signal) -> Body { switch self { case .welcome: [] @@ -147,7 +147,7 @@ enum Page: String, Identifiable, CaseIterable, Codable, CustomStringConvertible case .navigationView: NavigationViewDemo(app: app) case .picture: - PictureDemo(url: pictureURL, app: app, window: window) + PictureDemo(app: app, window: window) case .idle: IdleDemo() case .fixed: diff --git a/Tests/PasswordCheckerDemo.swift b/Sources/Demo/PasswordCheckerDemo.swift similarity index 97% rename from Tests/PasswordCheckerDemo.swift rename to Sources/Demo/PasswordCheckerDemo.swift index 2b2a1a5..81b39ef 100644 --- a/Tests/PasswordCheckerDemo.swift +++ b/Sources/Demo/PasswordCheckerDemo.swift @@ -54,7 +54,7 @@ enum PasswordChecker: String, CaseIterable, CustomStringConvertible, Identifiabl struct PasswordCheckerDemo: View { - var app: GTUIApp + var app: AdwaitaApp var view: Body { VStack { @@ -87,17 +87,17 @@ struct PasswordCheckerDemo: View { EntryRow("Password", text: $password) .suffix { Button(icon: .default(icon: .editCopy)) { - State.copy(password) + AdwaitaApp.copy(password) copied.signal() } .flat() - .verticalCenter() + .valign(.center) .tooltip("Copy") Button(icon: .default(icon: .editClear)) { password = "" } .flat() - .verticalCenter() + .valign(.center) .tooltip("Clear") } } diff --git a/Tests/PictureDemo.swift b/Sources/Demo/PictureDemo.swift similarity index 59% rename from Tests/PictureDemo.swift rename to Sources/Demo/PictureDemo.swift index a296063..45ecfe1 100644 --- a/Tests/PictureDemo.swift +++ b/Sources/Demo/PictureDemo.swift @@ -12,12 +12,13 @@ import Foundation struct PictureDemo: View { - var url: URL? - var app: GTUIApp - var window: GTUIWindow + @State private var fileDialog = Signal() + @State private var url: URL? + var app: AdwaitaApp + var window: AdwaitaWindow var data: Data { - guard let url, let data = try? Data(contentsOf: url) else { + guard let url, let data = try? Data(contentsOf: url) else { return .init() } return data @@ -27,12 +28,13 @@ struct PictureDemo: View { Picture() .data(data) Button("Import") { - app.addWindow("picture", parent: window) + fileDialog.signal() } .halign(.center) .pill() .suggested() .padding() + .fileImporter(open: fileDialog, extensions: ["jpg", "jpeg", "png", "svg"]) { url = $0 } onClose: { } } } diff --git a/Tests/PopoverDemo.swift b/Sources/Demo/PopoverDemo.swift similarity index 100% rename from Tests/PopoverDemo.swift rename to Sources/Demo/PopoverDemo.swift diff --git a/Tests/ToastDemo.swift b/Sources/Demo/ToastDemo.swift similarity index 100% rename from Tests/ToastDemo.swift rename to Sources/Demo/ToastDemo.swift diff --git a/Tests/ToolbarDemo.swift b/Sources/Demo/ToolbarDemo.swift similarity index 98% rename from Tests/ToolbarDemo.swift rename to Sources/Demo/ToolbarDemo.swift index 8590648..d9e88e4 100644 --- a/Tests/ToolbarDemo.swift +++ b/Sources/Demo/ToolbarDemo.swift @@ -11,7 +11,7 @@ import Adwaita struct ToolbarDemo: View { - var app: GTUIApp + var app: AdwaitaApp var view: Body { VStack { diff --git a/Tests/TransitionDemo.swift b/Sources/Demo/TransitionDemo.swift similarity index 100% rename from Tests/TransitionDemo.swift rename to Sources/Demo/TransitionDemo.swift diff --git a/Tests/ViewSwitcherDemo.swift b/Sources/Demo/ViewSwitcherDemo.swift similarity index 97% rename from Tests/ViewSwitcherDemo.swift rename to Sources/Demo/ViewSwitcherDemo.swift index 3b23bcb..be05616 100644 --- a/Tests/ViewSwitcherDemo.swift +++ b/Sources/Demo/ViewSwitcherDemo.swift @@ -11,7 +11,7 @@ import Adwaita struct ViewSwitcherDemo: View { - var app: GTUIApp + var app: AdwaitaApp var view: Body { VStack { @@ -54,7 +54,7 @@ struct ViewSwitcherDemo: View { } } - var toolbar: View { + var toolbar: AnyView { HeaderBar(titleButtons: !bottom) { } end: { } .headerBarTitle { ViewSwitcher(selection: $selection) diff --git a/Tests/WindowsDemo.swift b/Sources/Demo/WindowsDemo.swift similarity index 97% rename from Tests/WindowsDemo.swift rename to Sources/Demo/WindowsDemo.swift index 7407298..1fee228 100644 --- a/Tests/WindowsDemo.swift +++ b/Sources/Demo/WindowsDemo.swift @@ -11,7 +11,7 @@ import Adwaita struct WindowsDemo: View { - var app: GTUIApp! + var app: AdwaitaApp! var view: Body { HStack { diff --git a/Sources/Generation/GIR/Class+.swift b/Sources/Generation/GIR/Class+.swift index 7b7c5d2..fd5c4fb 100644 --- a/Sources/Generation/GIR/Class+.swift +++ b/Sources/Generation/GIR/Class+.swift @@ -164,9 +164,9 @@ extension Class { content += """ /// The application. - var app: GTUIApp? + var app: AdwaitaApp? /// The window. - var window: GTUIApplicationWindow? + var window: AdwaitaWindow? """ return content } @@ -199,7 +199,8 @@ extension Class { view.updateStorage( storage, modifiers: modifiers, - updateProperties: updateProperties + updateProperties: updateProperties, + type: type ) } } @@ -215,8 +216,8 @@ extension Class { /// - genConfig: The generation configuration. /// - Returns: The code. func generateDynamicWidgetUpdate(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String { - let child = "let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers)" - let pointer = "child.pointer?.cast()" + let child = "let child = content(element).storage(modifiers: modifiers, type: type)" + let pointer = "child.opaquePointer?.cast()" let widget = "widget" + (config.cast ? "?.cast()" : "") if let dynamicWidget = config.dynamicWidget { // swiftlint:disable line_length @@ -243,7 +244,7 @@ extension Class { storage.fields["element"] = elements storage.content[.mainContent] = contentStorage for (index, element) in elements.enumerated() { - content(element).widget(modifiers: modifiers).update(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties) + content(element).updateStorage(contentStorage[index], modifiers: modifiers, updateProperties: updateProperties, type: type) } """ // swiftlint:enable line_length diff --git a/Sources/Generation/GIR/Class.swift b/Sources/Generation/GIR/Class.swift index 3bc8f85..1f38000 100644 --- a/Sources/Generation/GIR/Class.swift +++ b/Sources/Generation/GIR/Class.swift @@ -65,9 +65,9 @@ struct Class: ClassLike, Decodable { let widgetName = config.name ?? config.class let definition: String if config.dynamicWidget == nil { - definition = "\(widgetName): Widget" + definition = "\(widgetName): AdwaitaWidget" } else { - definition = "\(widgetName): Widget where Element: Identifiable" + definition = "\(widgetName): AdwaitaWidget where Element: Identifiable" } return """ // @@ -84,33 +84,36 @@ struct Class: ClassLike, Decodable { public struct \(definition) { /// Additional update functions for type extensions. - var updateFunctions: [(ViewStorage, [(View) -> View], Bool) -> Void] = [] + var updateFunctions: [(ViewStorage, [(AnyView) -> AnyView], Bool) -> Void] = [] /// Additional appear functions for type extensions. - var appearFunctions: [(ViewStorage, [(View) -> View]) -> Void] = [] + var appearFunctions: [(ViewStorage, [(AnyView) -> AnyView]) -> Void] = [] \(generateProperties(config: config, genConfig: genConfig, namespace: namespace, configs: configs)) /// Initialize `\(widgetName)`. \(generateAdwaitaInitializer(config: config, genConfig: genConfig, namespace: namespace, configs: configs)) - /// Get the widget's view storage. - /// - Parameter modifiers: The view modifiers. + /// The view storage. + /// - Parameters: + /// - modifiers: Modify views before being updated. + /// - type: The type of the app storage. /// - Returns: The view storage. - public func container(modifiers: [(View) -> View]) -> ViewStorage { + public func container(modifiers: [(AnyView) -> AnyView], type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(\(generateInitializer(name: widgetName, config: config, namespace: namespace, configs: configs))?.opaque()) for function in appearFunctions { function(storage, modifiers) } - update(storage, modifiers: modifiers, updateProperties: true) + update(storage, modifiers: modifiers, updateProperties: true, type: type) \(generateWidgetAssignments(config: config, genConfig: genConfig, namespace: namespace, configs: configs)) return storage } - /// Update the widget's view storage. + /// Update the stored content. /// - Parameters: - /// - storage: The view storage. - /// - modifiers: The view modifiers. + /// - storage: The storage to update. + /// - modifiers: Modify views before being updated /// - updateProperties: Whether to update the view's properties. - public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) {\(generateSignalModifications(config: config, genConfig: genConfig, namespace: namespace)) + /// - type: The type of the app storage. + public func update(_ storage: ViewStorage, modifiers: [(AnyView) -> AnyView], updateProperties: Bool, type: Data.Type) where Data: ViewRenderData {\(generateSignalModifications(config: config, genConfig: genConfig, namespace: namespace)) storage.modify { widget in \(generateBindingAssignments(config: config, genConfig: genConfig, namespace: namespace, configs: configs)) \(generateModifications(config: config, genConfig: genConfig, namespace: namespace, configs: configs)) @@ -119,6 +122,9 @@ struct Class: ClassLike, Decodable { for function in updateFunctions { function(storage, modifiers, updateProperties) } + if updateProperties { + storage.previousState = self + } } \(generateModifiers(config: config, genConfig: genConfig, namespace: namespace, configs: configs)) } diff --git a/Sources/Generation/GIR/ClassLike.swift b/Sources/Generation/GIR/ClassLike.swift index 2a4692e..ed2ea58 100644 --- a/Sources/Generation/GIR/ClassLike.swift +++ b/Sources/Generation/GIR/ClassLike.swift @@ -29,7 +29,7 @@ extension ClassLike { /// - configurations: The configurations for the classes in the namespace. /// - Returns: The properties. func properties(namespace: Namespace, configurations: [WidgetConfiguration]) -> [Property] { - var properties = properties + var properties = properties.filter { !($0.deprecated ?? false) } for type in parentTypes(namespace: namespace) { properties += type .properties(namespace: namespace, configurations: configurations) @@ -85,14 +85,14 @@ extension ClassLike { guard let config = configs.first(where: { $0.class == name }) else { return content } - let widgetPointer = config.cast ? "storage.pointer?.cast()" : "storage.pointer" + let widgetPointer = config.cast ? "storage.opaquePointer?.cast()" : "storage.opaquePointer" for widget in config.staticWidgets { content += """ var \(widget.name)Storage: [ViewStorage] = [] for view in \(widget.name)() { - \(widget.name)Storage.append(view.storage(modifiers: modifiers)) - \(widget.add)(\(widgetPointer), \(widget.name)Storage.last?.pointer?.cast()) + \(widget.name)Storage.append(view.storage(modifiers: modifiers, type: type)) + \(widget.add)(\(widgetPointer), \(widget.name)Storage.last?.opaquePointer?.cast()) } storage.content["\(widget.name)"] = \(widget.name)Storage """ diff --git a/Sources/Generation/GIR/Property.swift b/Sources/Generation/GIR/Property.swift index 2d0a50e..04fc7a0 100644 --- a/Sources/Generation/GIR/Property.swift +++ b/Sources/Generation/GIR/Property.swift @@ -24,6 +24,8 @@ struct Property: Decodable { /// This overwrites the cast information provided by the caller of a function. var cast: Bool? // swiftlint:enable discouraged_optional_boolean + /// Whether the property is deprecated. + var deprecated: Bool? /// Get the Swift property name. /// - Parameter configuration: The generation configuration. @@ -46,15 +48,13 @@ struct Property: Decodable { defaultValue: Bool = false ) -> String { var type: String - if config.bindings.contains { $0.property == name } { + if config.bindings.contains(where: { $0.property == name }) { type = (try? self.type?.binding(configuration: genConfig)) ?? "Binding" } else { type = (try? self.type?.generate(configuration: genConfig)) ?? "String" } - if self.type?.isWidget ?? false { - type = "\(modifier ? "@escaping" : "") (() -> Body)" - } else if self.type?.isMenu ?? false { - type = "\(modifier ? "@escaping" : "") (() -> MenuContent)" + if (self.type?.isWidget ?? false) || (self.type?.isMenu ?? false) { + type = "\(modifier ? "@escaping " : "")(() -> Body)" } if !config.requiredProperties.contains(name) && !(((self.type?.isWidget ?? false) || (self.type?.isMenu ?? false)) && modifier) { @@ -87,12 +87,12 @@ struct Property: Decodable { /// - Returns: The code. func generateModifier(config: WidgetConfiguration, genConfig: GenerationConfiguration) -> String { let property = convertPropertyName(configuration: genConfig) - let builder = (type?.isWidget ?? false) ? "@ViewBuilder " : ((type?.isMenu ?? false) ? "@MenuBuilder " : "") + let builder = ((type?.isWidget ?? false) || (type?.isMenu ?? false)) ? "@ViewBuilder " : "" let mainParameter = parameter(config: config, genConfig: genConfig, modifier: true, defaultValue: true) var sideParameters = "" var sideAssignments = "" if type?.isMenu ?? false { - sideParameters += "app: GTUIApp, window: GTUIApplicationWindow? = nil, " + sideParameters += "app: AdwaitaApp, window: AdwaitaWindow? = nil, " sideAssignments += "newSelf.app = app; newSelf.window = window" } return """ @@ -124,13 +124,19 @@ struct Property: Decodable { guard !(type?.isWidget ?? false) else { return """ if let widget = storage.content["\(name)"]?.first { - \(name)?().widget(modifiers: modifiers).update(widget, modifiers: modifiers, updateProperties: updateProperties) + \(name)?().updateStorage(widget, modifiers: modifiers, updateProperties: updateProperties, type: type) } """ } guard !(type?.isMenu ?? false) else { - return "" + return """ + if let menu = storage.content["\(name)"]?.first { + MenuCollection { \(name)?() ?? [] } + .updateStorage(menu, modifiers: [], updateProperties: updateProperties, type: MenuContext.self) + } + + """ } guard var setter else { return "" @@ -152,7 +158,7 @@ struct Property: Decodable { } if config.bindings.contains(where: { $0.property == self.name }) { var check = "" - let widget = (self.cast ?? config.cast) ? "storage.pointer?.cast()" : "storage.pointer" + let widget = (self.cast ?? config.cast) ? "storage.opaquePointer?.cast()" : "storage.opaquePointer" if var getter { getter = "\((self.prefix ?? prefix) + "_" + getter)(\(widget))" check = ", (" + (genConfig.getterTypeConversions[self.type?.name ?? ""]?(getter) ?? getter) + ") != \(name).wrappedValue" @@ -165,14 +171,14 @@ struct Property: Decodable { """ } else if config.requiredProperties.contains(self.name) { return """ - if updateProperties { + if updateProperties, (storage.previousState as? Self)?.\(name) != \(name) { \(onlySetConditions)\(onlySetConditionsIndentation)\(setter)(\(widget), \(name)\(propertyString))\(onlySetConditionsEnd) } """ } else { return """ - if let \(name)\(setConditions), updateProperties { + if let \(name)\(setConditions), updateProperties, (storage.previousState as? Self)?.\(name) != \(name) { \(setter)(\(widget), \(name)\(propertyString)) } @@ -197,11 +203,11 @@ struct Property: Decodable { } setter = (self.prefix ?? prefix) + "_" + setter let name = convertPropertyName(configuration: genConfig) - let view = (self.cast ?? config.cast) ? "storage.pointer?.cast()" : "storage.pointer" + let view = (self.cast ?? config.cast) ? "storage.opaquePointer?.cast()" : "storage.opaquePointer" return """ - if let \(name)Storage = \(name)?().widget(modifiers: modifiers).storage(modifiers: modifiers) { + if let \(name)Storage = \(name)?().storage(modifiers: modifiers, type: type) { storage.content["\(name)"] = [\(name)Storage] - \(setter)(\(view), \(name)Storage.pointer?.cast()) + \(setter)(\(view), \(name)Storage.opaquePointer?.cast()) } """ @@ -223,14 +229,12 @@ struct Property: Decodable { } setter = (self.prefix ?? prefix) + "_" + setter let name = convertPropertyName(configuration: genConfig) - let view = (self.cast ?? config.cast) ? "storage.pointer?.cast()" : "storage.pointer" + let view = (self.cast ?? config.cast) ? "storage.opaquePointer?.cast()" : "storage.opaquePointer" return """ - if let declarative = \(name)?(), let app { - let menu = g_menu_new() - \(setter)(\(view), menu?.cast()) - for item in declarative { - item.addMenuItems(menu: menu, app: app, window: window) - } + if let menu = \(name)?(), let app { + let childStorage = MenuCollection { menu }.getMenu(app: app, window: window) + storage.content["\(name)"] = [childStorage] + \(setter)(\(view), childStorage.opaquePointer?.cast()) } """ @@ -252,7 +256,7 @@ struct Property: Decodable { guard var getter else { return "" } - let widget = (self.cast ?? config.cast) ? "storage.pointer?.cast()" : "storage.pointer" + let widget = (self.cast ?? config.cast) ? "storage.opaquePointer?.cast()" : "storage.opaquePointer" getter = "\((self.prefix ?? prefix) + "_" + getter)(\(widget))" let name = convertPropertyName(configuration: genConfig) let finalGetter = genConfig.getterTypeConversions[type?.name ?? ""]?(getter) ?? getter diff --git a/Tests/Demo.swift b/Tests/Demo.swift deleted file mode 100644 index 4cca82c..0000000 --- a/Tests/Demo.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// Demo.swift -// Adwaita -// -// Created by david-swift on 25.09.23. -// - -// swiftlint:disable missing_docs implicitly_unwrapped_optional no_magic_numbers - -import Adwaita -import Foundation - -@main -struct Demo: App { - - let id = "io.github.AparokshaUI.Demo" - var app: GTUIApp! - - @State private var pictureURL: URL? - - var scene: Scene { - Window(id: "main") { window in - DemoContent(window: window, app: app, pictureURL: pictureURL) - } - .devel() - HelperWindows() - FileDialog(importer: "picture", extensions: ["jpg", "jpeg", "png", "svg"]) { pictureURL = $0 } onClose: { } - } - - struct HelperWindows: WindowSceneGroup { - - var scene: Scene { - Window(id: "content", open: 0) { _ in - WindowsDemo.WindowContent() - } - .resizable(false) - .closeShortcut() - .defaultSize(width: 400, height: 250) - Window(id: "toolbar-demo", open: 0) { _ in - ToolbarDemo.WindowContent().stopModifiers() - } - .closeShortcut() - .defaultSize(width: 400, height: 250) - .title("Toolbar Demo") - Window(id: "switcher-demo", open: 0) { _ in - ViewSwitcherDemo.WindowContent() - } - .closeShortcut() - .defaultSize(width: 600, height: 400) - .resizable(false) - .title("View Switcher Demo") - Window(id: "form-demo", open: 0) { _ in - FormDemo.WindowContent() - } - .closeShortcut() - .defaultSize(width: 400, height: 250) - .title("Form Demo") - Window(id: "password-checker-demo", open: 0) { _ in - PasswordCheckerDemo.WindowContent() - } - .closeShortcut() - .defaultSize(width: 400, height: 250) - .title("Password Checker Demo") - - Window(id: "navigation", open: 0) { _ in - NavigationViewDemo.WindowContent() - } - .closeShortcut() - .title("Navigation View Demo") - } - - } - - 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 - @State("maximized") - private var maximized = false - @State private var about = false - var window: GTUIApplicationWindow - var app: GTUIApp! - var pictureURL: URL? - - var view: Body { - OverlaySplitView(visible: $sidebarVisible) { - ScrollView { - List(Page.allCases, selection: $selection) { element in - Text(element.label) - .halign(.start) - .padding() - } - .sidebarStyle() - } - .topToolbar { - HeaderBar.end { - menu - } - .headerBarTitle { - WindowTitle(subtitle: "", title: "Demo") - } - } - } content: { - ViewStack(element: selection) { selection in - StatusPage( - selection.label, - icon: selection.icon, - description: selection.description - ) { selection.view(app: app, window: window, toast: toast, pictureURL: pictureURL) } - .topToolbar { - HeaderBar { - Toggle(icon: .default(icon: .sidebarShow), isOn: $sidebarVisible) - .tooltip("Toggle Sidebar") - } end: { - if sidebarVisible { - Text("").transition(.crossfade) - } else { - menu.transition(.crossfade) - } - } - .headerBarTitle { - if sidebarVisible { - Text("") - .transition(.crossfade) - } else { - WindowTitle(subtitle: "Demo", title: selection.label) - .transition(.crossfade) - } - } - } - .toast("This is a toast!", signal: toast) - } - } - .aboutDialog( - visible: $about, - app: "Demo", - developer: "david-swift", - version: "Test", - icon: .default(icon: .applicationXExecutable), - website: .init(string: "https://aparokshaui.github.io/adwaita-swift/"), - issues: .init(string: "https://github.com/AparokshaUI/adwaita-swift/issues") - ) - } - - var menu: View { - Menu(icon: .default(icon: .openMenu), app: app, window: window) { - MenuButton("New Window", window: false) { - app.addWindow("main") - } - .keyboardShortcut("n".ctrl()) - MenuButton("Close Window") { - window.close() - } - .keyboardShortcut("w".ctrl()) - MenuSection { - MenuButton("About") { about = true } - MenuButton("Quit", window: false) { app.quit() } - .keyboardShortcut("q".ctrl()) - } - } - .primary() - .tooltip("Main Menu") - } - - func window(_ window: Window) -> Window { - window - .size(width: $width, height: $height) - .maximized($maximized) - } - - } - -} - -// swiftlint:enable missing_docs implicitly_unwrapped_optional no_magic_numbers