diff --git a/Documentation/Reference/README.md b/Documentation/Reference/README.md index b7037c9..582695e 100644 --- a/Documentation/Reference/README.md +++ b/Documentation/Reference/README.md @@ -34,6 +34,7 @@ - [EntryRow](structs/EntryRow.md) - [ExpanderRow](structs/ExpanderRow.md) - [FileDialog](structs/FileDialog.md) +- [FlowBox](structs/FlowBox.md) - [ForEach](structs/ForEach.md) - [Form](structs/Form.md) - [HStack](structs/HStack.md) @@ -111,6 +112,7 @@ - [Clamp](extensions/Clamp.md) - [ComboRow](extensions/ComboRow.md) - [EntryRow](extensions/EntryRow.md) +- [FlowBox](extensions/FlowBox.md) - [FormSection](extensions/FormSection.md) - [HeaderBar](extensions/HeaderBar.md) - [Int](extensions/Int.md) diff --git a/Documentation/Reference/extensions/FlowBox.md b/Documentation/Reference/extensions/FlowBox.md new file mode 100644 index 0000000..0c04f7a --- /dev/null +++ b/Documentation/Reference/extensions/FlowBox.md @@ -0,0 +1,21 @@ +**EXTENSION** + +# `FlowBox` + +## Properties +### `selectionField` + +The ID for the field storing the selection value. + +### `elementsField` + +The ID for the field storing the elements. + +## Methods +### `init(_:selection:content:)` + +Initialize `FlowBox`. +- Parameters: + - elements: The elements. + - selection: The identifier of the selected element. Selection disabled if `nil`. + - content: The view for an element. diff --git a/Documentation/Reference/structs/FlowBox.md b/Documentation/Reference/structs/FlowBox.md new file mode 100644 index 0000000..7b6b0b6 --- /dev/null +++ b/Documentation/Reference/structs/FlowBox.md @@ -0,0 +1,290 @@ +**STRUCT** + +# `FlowBox` + +A `GtkFlowBox` puts child widgets in reflowing grid. + +For instance, with the horizontal orientation, the widgets will be +arranged from left to right, starting a new row under the previous +row when necessary. Reducing the width in this case will require more +rows, so a larger height will be requested. + +Likewise, with the vertical orientation, the widgets will be arranged +from top to bottom, starting a new column to the right when necessary. +Reducing the height will require more columns, so a larger width will +be requested. + +The size request of a `GtkFlowBox` alone may not be what you expect; +if you need to be able to shrink it along both axes and dynamically +reflow its children, you may have to wrap it in a `GtkScrolledWindow` +to enable that. + +The children of a `GtkFlowBox` can be dynamically sorted and filtered. + +Although a `GtkFlowBox` must have only `GtkFlowBoxChild` children, you +can add any kind of widget to it via [method@Gtk.FlowBox.insert], and a +`GtkFlowBoxChild` widget will automatically be inserted between the box +and the widget. + +Also see [class@Gtk.ListBox]. + +# CSS nodes + +``` +flowbox +├── flowboxchild +│ ╰── ├── flowboxchild +│ ╰── ┊ +╰── [rubberband] +``` + +`GtkFlowBox` uses a single CSS node with name flowbox. `GtkFlowBoxChild` +uses a single CSS node with name flowboxchild. For rubberband selection, +a subnode with name rubberband is used. + +# Accessibility + +`GtkFlowBox` uses the %GTK_ACCESSIBLE_ROLE_GRID role, and `GtkFlowBoxChild` +uses the %GTK_ACCESSIBLE_ROLE_GRID_CELL role. + +## Properties +### `updateFunctions` + +Additional update functions for type extensions. + +### `appearFunctions` + +Additional appear functions for type extensions. + +### `acceptUnpairedRelease` + +accept-unpaired-release + +### `activateOnSingleClick` + +Determines whether children can be activated with a single +click, or require a double-click. + +### `columnSpacing` + +The amount of horizontal space between two children. + +### `homogeneous` + +Determines whether all children should be allocated the +same size. + +### `maxChildrenPerLine` + +The maximum amount of children to request space for consecutively +in the given orientation. + +### `minChildrenPerLine` + +The minimum number of children to allocate consecutively +in the given orientation. + +Setting the minimum children per line ensures +that a reasonably small height will be requested +for the overall minimum width of the box. + +### `rowSpacing` + +The amount of vertical space between two children. + +### `activateCursorChild` + +Emitted when the user activates the @box. + +This is a [keybinding signal](class.SignalAction.html). + +### `childActivated` + +Emitted when a child has been activated by the user. + +### `moveCursor` + +Emitted when the user initiates a cursor movement. + +This is a [keybinding signal](class.SignalAction.html). +Applications should not connect to it, but may emit it with +g_signal_emit_by_name() if they need to control the cursor +programmatically. + +The default bindings for this signal come in two variants, +the variant with the Shift modifier extends the selection, +the variant without the Shift modifier does not. +There are too many key combinations to list them all here. + +- , , , +move by individual children +- Home, End move to the ends of the box +- PgUp, PgDn move vertically by pages + +### `selectAll` + +Emitted to select all children of the box, +if the selection mode permits it. + +This is a [keybinding signal](class.SignalAction.html). + +The default bindings for this signal is Ctrl-a. + +### `selectedChildrenChanged` + +Emitted when the set of selected children changes. + +Use [method@Gtk.FlowBox.selected_foreach] or +[method@Gtk.FlowBox.get_selected_children] to obtain the +selected children. + +### `toggleCursorChild` + +Emitted to toggle the selection of the child that has the focus. + +This is a [keybinding signal](class.SignalAction.html). + +The default binding for this signal is Ctrl-Space. + +### `unselectAll` + +Emitted to unselect all children of the box, +if the selection mode permits it. + +This is a [keybinding signal](class.SignalAction.html). + +The default bindings for this signal is Ctrl-Shift-a. + +### `elements` + +The dynamic widget elements. + +### `content` + +The dynamic widget content. + +### `app` + +The application. + +### `window` + +The window. + +## Methods +### `init(_:content:)` + +Initialize `FlowBox`. + +### `container(modifiers:)` + +Get the widget's view storage. +- Parameter modifiers: The view modifiers. +- Returns: The view storage. + +### `update(_:modifiers:updateProperties:)` + +Update the widget's view storage. +- Parameters: + - storage: The view storage. + - modifiers: The view modifiers. + - updateProperties: Whether to update the view's properties. + +### `acceptUnpairedRelease(_:)` + +accept-unpaired-release + +### `activateOnSingleClick(_:)` + +Determines whether children can be activated with a single +click, or require a double-click. + +### `columnSpacing(_:)` + +The amount of horizontal space between two children. + +### `homogeneous(_:)` + +Determines whether all children should be allocated the +same size. + +### `maxChildrenPerLine(_:)` + +The maximum amount of children to request space for consecutively +in the given orientation. + +### `minChildrenPerLine(_:)` + +The minimum number of children to allocate consecutively +in the given orientation. + +Setting the minimum children per line ensures +that a reasonably small height will be requested +for the overall minimum width of the box. + +### `rowSpacing(_:)` + +The amount of vertical space between two children. + +### `activateCursorChild(_:)` + +Emitted when the user activates the @box. + +This is a [keybinding signal](class.SignalAction.html). + +### `childActivated(_:)` + +Emitted when a child has been activated by the user. + +### `moveCursor(_:)` + +Emitted when the user initiates a cursor movement. + +This is a [keybinding signal](class.SignalAction.html). +Applications should not connect to it, but may emit it with +g_signal_emit_by_name() if they need to control the cursor +programmatically. + +The default bindings for this signal come in two variants, +the variant with the Shift modifier extends the selection, +the variant without the Shift modifier does not. +There are too many key combinations to list them all here. + +- , , , +move by individual children +- Home, End move to the ends of the box +- PgUp, PgDn move vertically by pages + +### `selectAll(_:)` + +Emitted to select all children of the box, +if the selection mode permits it. + +This is a [keybinding signal](class.SignalAction.html). + +The default bindings for this signal is Ctrl-a. + +### `selectedChildrenChanged(_:)` + +Emitted when the set of selected children changes. + +Use [method@Gtk.FlowBox.selected_foreach] or +[method@Gtk.FlowBox.get_selected_children] to obtain the +selected children. + +### `toggleCursorChild(_:)` + +Emitted to toggle the selection of the child that has the focus. + +This is a [keybinding signal](class.SignalAction.html). + +The default binding for this signal is Ctrl-Space. + +### `unselectAll(_:)` + +Emitted to unselect all children of the box, +if the selection mode permits it. + +This is a [keybinding signal](class.SignalAction.html). + +The default bindings for this signal is Ctrl-Shift-a. diff --git a/Sources/Adwaita/View/FlowBox+.swift b/Sources/Adwaita/View/FlowBox+.swift new file mode 100644 index 0000000..ea129b4 --- /dev/null +++ b/Sources/Adwaita/View/FlowBox+.swift @@ -0,0 +1,59 @@ +// +// FlowBox+.swift +// Adwaita +// +// Created by david-swift on 12.02.24. +// + +import CAdw + +extension FlowBox { + + /// The ID for the field storing the selection value. + static var selectionField: String { "selection" } + /// The ID for the field storing the elements. + static var elementsField: String { "element" } + + /// Initialize `FlowBox`. + /// - Parameters: + /// - elements: The elements. + /// - selection: The identifier of the selected element. Selection disabled if `nil`. + /// - content: The view for an element. + public init( + _ elements: [Element], + selection: Binding?, + @ViewBuilder content: @escaping (Element) -> Body + ) { + 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) { + let element = gtk_flow_box_child_get_child(child.cast()) + return elements[safe: storage.content[.mainContent]?.firstIndex { $0.pointer?.cast() == element }]?.id + } + return nil + } + if let selection { + appearFunctions.append { storage in + storage.fields[Self.selectionField] = selection + 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], + let id = id(storage, elements) { + binding.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)) + } + } + } else { + appearFunctions.append { storage in + gtk_flow_box_set_selection_mode(storage.pointer, GTK_SELECTION_NONE) + } + } + } + +} diff --git a/Sources/Adwaita/View/Generated/ActionRow.swift b/Sources/Adwaita/View/Generated/ActionRow.swift index 277ce41..85f81e5 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Avatar.swift b/Sources/Adwaita/View/Generated/Avatar.swift index 76d1741..143c414 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Banner.swift b/Sources/Adwaita/View/Generated/Banner.swift index 984638c..a08edb2 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Bin.swift b/Sources/Adwaita/View/Generated/Bin.swift index 9c7b212..6d813b6 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Box.swift b/Sources/Adwaita/View/Generated/Box.swift index d16e1b4..42f3207 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Button.swift b/Sources/Adwaita/View/Generated/Button.swift index 099631f..59f2f01 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ButtonContent.swift b/Sources/Adwaita/View/Generated/ButtonContent.swift index a404498..52fad32 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Carousel.swift b/Sources/Adwaita/View/Generated/Carousel.swift index f470eec..94ea66c 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/CenterBox.swift b/Sources/Adwaita/View/Generated/CenterBox.swift index f4245f3..5f63061 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/CheckButton.swift b/Sources/Adwaita/View/Generated/CheckButton.swift index 4dc1fbf..6f9a367 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Clamp.swift b/Sources/Adwaita/View/Generated/Clamp.swift index 4b6f3b1..8fe882f 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ComboRow.swift b/Sources/Adwaita/View/Generated/ComboRow.swift index 6ab28a6..436c2f2 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/EntryRow.swift b/Sources/Adwaita/View/Generated/EntryRow.swift index a47589b..50460c3 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ExpanderRow.swift b/Sources/Adwaita/View/Generated/ExpanderRow.swift index 68c18fc..b241dda 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/FlowBox.swift b/Sources/Adwaita/View/Generated/FlowBox.swift new file mode 100644 index 0000000..16a7545 --- /dev/null +++ b/Sources/Adwaita/View/Generated/FlowBox.swift @@ -0,0 +1,401 @@ +// +// FlowBox.swift +// Adwaita +// +// Created by auto-generation on 12.02.24. +// + +import CAdw +import LevenshteinTransformations + +/// A `GtkFlowBox` puts child widgets in reflowing grid. +/// +/// For instance, with the horizontal orientation, the widgets will be +/// arranged from left to right, starting a new row under the previous +/// row when necessary. Reducing the width in this case will require more +/// rows, so a larger height will be requested. +/// +/// Likewise, with the vertical orientation, the widgets will be arranged +/// from top to bottom, starting a new column to the right when necessary. +/// Reducing the height will require more columns, so a larger width will +/// be requested. +/// +/// The size request of a `GtkFlowBox` alone may not be what you expect; +/// if you need to be able to shrink it along both axes and dynamically +/// reflow its children, you may have to wrap it in a `GtkScrolledWindow` +/// to enable that. +/// +/// The children of a `GtkFlowBox` can be dynamically sorted and filtered. +/// +/// Although a `GtkFlowBox` must have only `GtkFlowBoxChild` children, you +/// can add any kind of widget to it via [method@Gtk.FlowBox.insert], and a +/// `GtkFlowBoxChild` widget will automatically be inserted between the box +/// and the widget. +/// +/// Also see [class@Gtk.ListBox]. +/// +/// # CSS nodes +/// +/// ``` +/// flowbox +/// ├── flowboxchild +/// │ ╰── ├── flowboxchild +/// │ ╰── ┊ +/// ╰── [rubberband] +/// ``` +/// +/// `GtkFlowBox` uses a single CSS node with name flowbox. `GtkFlowBoxChild` +/// uses a single CSS node with name flowboxchild. For rubberband selection, +/// a subnode with name rubberband is used. +/// +/// # Accessibility +/// +/// `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 { + + /// Additional update functions for type extensions. + var updateFunctions: [(ViewStorage) -> Void] = [] + /// Additional appear functions for type extensions. + var appearFunctions: [(ViewStorage) -> Void] = [] + +/// accept-unpaired-release + var acceptUnpairedRelease: Bool? + /// Determines whether children can be activated with a single + /// click, or require a double-click. + var activateOnSingleClick: Bool? + /// The amount of horizontal space between two children. + var columnSpacing: UInt? + /// Determines whether all children should be allocated the + /// same size. + var homogeneous: Bool? + /// The maximum amount of children to request space for consecutively + /// in the given orientation. + var maxChildrenPerLine: UInt? + /// The minimum number of children to allocate consecutively + /// in the given orientation. + /// + /// Setting the minimum children per line ensures + /// that a reasonably small height will be requested + /// for the overall minimum width of the box. + var minChildrenPerLine: UInt? + /// The amount of vertical space between two children. + var rowSpacing: UInt? + /// Emitted when the user activates the @box. + /// + /// This is a [keybinding signal](class.SignalAction.html). + var activateCursorChild: (() -> Void)? + /// Emitted when a child has been activated by the user. + var childActivated: (() -> Void)? + /// Emitted when the user initiates a cursor movement. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// Applications should not connect to it, but may emit it with + /// g_signal_emit_by_name() if they need to control the cursor + /// programmatically. + /// + /// The default bindings for this signal come in two variants, + /// the variant with the Shift modifier extends the selection, + /// the variant without the Shift modifier does not. + /// There are too many key combinations to list them all here. + /// + /// - , , , + /// move by individual children + /// - Home, End move to the ends of the box + /// - PgUp, PgDn move vertically by pages + var moveCursor: (() -> Void)? + /// Emitted to select all children of the box, + /// if the selection mode permits it. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// + /// The default bindings for this signal is Ctrl-a. + var selectAll: (() -> Void)? + /// Emitted when the set of selected children changes. + /// + /// Use [method@Gtk.FlowBox.selected_foreach] or + /// [method@Gtk.FlowBox.get_selected_children] to obtain the + /// selected children. + var selectedChildrenChanged: (() -> Void)? + /// Emitted to toggle the selection of the child that has the focus. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// + /// The default binding for this signal is Ctrl-Space. + var toggleCursorChild: (() -> Void)? + /// Emitted to unselect all children of the box, + /// if the selection mode permits it. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// + /// The default bindings for this signal is Ctrl-Shift-a. + var unselectAll: (() -> Void)? + /// The dynamic widget elements. + var elements: [Element] + /// The dynamic widget content. + var content: (Element) -> Body + /// The application. + var app: GTUIApp? + /// The window. + var window: GTUIApplicationWindow? + + /// Initialize `FlowBox`. + public init(_ elements: [Element], @ViewBuilder content: @escaping (Element) -> Body) { + self.elements = elements + self.content = content + } + + /// Get the widget's view storage. + /// - Parameter modifiers: The view modifiers. + /// - Returns: The view storage. + public func container(modifiers: [(View) -> View]) -> ViewStorage { + let storage = ViewStorage(gtk_flow_box_new()?.opaque()) + update(storage, modifiers: modifiers, updateProperties: true) + + + for function in appearFunctions { + function(storage) + } + return storage + } + + /// Update the widget's view storage. + /// - Parameters: + /// - storage: The view storage. + /// - modifiers: The view modifiers. + /// - updateProperties: Whether to update the view's properties. + public func update(_ storage: ViewStorage, modifiers: [(View) -> View], updateProperties: Bool) { + if let activateCursorChild { + storage.connectSignal(name: "activate-cursor-child", argCount: 0) { + activateCursorChild() + } + } + if let childActivated { + storage.connectSignal(name: "child-activated", argCount: 1) { + childActivated() + } + } + if let moveCursor { + storage.connectSignal(name: "move-cursor", argCount: 4) { + moveCursor() + } + } + if let selectAll { + storage.connectSignal(name: "select-all", argCount: 0) { + selectAll() + } + } + if let selectedChildrenChanged { + storage.connectSignal(name: "selected-children-changed", argCount: 0) { + selectedChildrenChanged() + } + } + if let toggleCursorChild { + storage.connectSignal(name: "toggle-cursor-child", argCount: 0) { + toggleCursorChild() + } + } + if let unselectAll { + storage.connectSignal(name: "unselect-all", argCount: 0) { + unselectAll() + } + } + storage.modify { widget in + if let activateOnSingleClick, updateProperties { + gtk_flow_box_set_activate_on_single_click(widget, activateOnSingleClick.cBool) + } + if let columnSpacing, updateProperties { + gtk_flow_box_set_column_spacing(widget, columnSpacing.cInt) + } + if let homogeneous, updateProperties { + gtk_flow_box_set_homogeneous(widget, homogeneous.cBool) + } + if let maxChildrenPerLine, updateProperties { + gtk_flow_box_set_max_children_per_line(widget, maxChildrenPerLine.cInt) + } + if let minChildrenPerLine, updateProperties { + gtk_flow_box_set_min_children_per_line(widget, minChildrenPerLine.cInt) + } + if let rowSpacing, updateProperties { + gtk_flow_box_set_row_spacing(widget, rowSpacing.cInt) + } + + var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? [] + let old = storage.fields["element"] as? [Element] ?? [] + old.identifiableTransform( + to: elements, + functions: .init { index, element in + let child = content(element).widget(modifiers: modifiers).container(modifiers: modifiers) + 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) + 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) + 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) + } + } + for function in updateFunctions { + function(storage) + } + } + +/// accept-unpaired-release + public func acceptUnpairedRelease(_ acceptUnpairedRelease: Bool? = true) -> Self { + var newSelf = self + newSelf.acceptUnpairedRelease = acceptUnpairedRelease + + return newSelf + } + + /// Determines whether children can be activated with a single + /// click, or require a double-click. + public func activateOnSingleClick(_ activateOnSingleClick: Bool? = true) -> Self { + var newSelf = self + newSelf.activateOnSingleClick = activateOnSingleClick + + return newSelf + } + + /// The amount of horizontal space between two children. + public func columnSpacing(_ columnSpacing: UInt?) -> Self { + var newSelf = self + newSelf.columnSpacing = columnSpacing + + return newSelf + } + + /// Determines whether all children should be allocated the + /// same size. + public func homogeneous(_ homogeneous: Bool? = true) -> Self { + var newSelf = self + newSelf.homogeneous = homogeneous + + return newSelf + } + + /// The maximum amount of children to request space for consecutively + /// in the given orientation. + public func maxChildrenPerLine(_ maxChildrenPerLine: UInt?) -> Self { + var newSelf = self + newSelf.maxChildrenPerLine = maxChildrenPerLine + + return newSelf + } + + /// The minimum number of children to allocate consecutively + /// in the given orientation. + /// + /// Setting the minimum children per line ensures + /// that a reasonably small height will be requested + /// for the overall minimum width of the box. + public func minChildrenPerLine(_ minChildrenPerLine: UInt?) -> Self { + var newSelf = self + newSelf.minChildrenPerLine = minChildrenPerLine + + return newSelf + } + + /// The amount of vertical space between two children. + public func rowSpacing(_ rowSpacing: UInt?) -> Self { + var newSelf = self + newSelf.rowSpacing = rowSpacing + + return newSelf + } + + /// Emitted when the user activates the @box. + /// + /// This is a [keybinding signal](class.SignalAction.html). + public func activateCursorChild(_ activateCursorChild: @escaping () -> Void) -> Self { + var newSelf = self + newSelf.activateCursorChild = activateCursorChild + return newSelf + } + + /// Emitted when a child has been activated by the user. + public func childActivated(_ childActivated: @escaping () -> Void) -> Self { + var newSelf = self + newSelf.childActivated = childActivated + return newSelf + } + + /// Emitted when the user initiates a cursor movement. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// Applications should not connect to it, but may emit it with + /// g_signal_emit_by_name() if they need to control the cursor + /// programmatically. + /// + /// The default bindings for this signal come in two variants, + /// the variant with the Shift modifier extends the selection, + /// the variant without the Shift modifier does not. + /// There are too many key combinations to list them all here. + /// + /// - , , , + /// move by individual children + /// - Home, End move to the ends of the box + /// - PgUp, PgDn move vertically by pages + public func moveCursor(_ moveCursor: @escaping () -> Void) -> Self { + var newSelf = self + newSelf.moveCursor = moveCursor + return newSelf + } + + /// Emitted to select all children of the box, + /// if the selection mode permits it. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// + /// The default bindings for this signal is Ctrl-a. + public func selectAll(_ selectAll: @escaping () -> Void) -> Self { + var newSelf = self + newSelf.selectAll = selectAll + return newSelf + } + + /// Emitted when the set of selected children changes. + /// + /// Use [method@Gtk.FlowBox.selected_foreach] or + /// [method@Gtk.FlowBox.get_selected_children] to obtain the + /// selected children. + public func selectedChildrenChanged(_ selectedChildrenChanged: @escaping () -> Void) -> Self { + var newSelf = self + newSelf.selectedChildrenChanged = selectedChildrenChanged + return newSelf + } + + /// Emitted to toggle the selection of the child that has the focus. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// + /// The default binding for this signal is Ctrl-Space. + public func toggleCursorChild(_ toggleCursorChild: @escaping () -> Void) -> Self { + var newSelf = self + newSelf.toggleCursorChild = toggleCursorChild + return newSelf + } + + /// Emitted to unselect all children of the box, + /// if the selection mode permits it. + /// + /// This is a [keybinding signal](class.SignalAction.html). + /// + /// The default bindings for this signal is Ctrl-Shift-a. + public func unselectAll(_ unselectAll: @escaping () -> Void) -> Self { + var newSelf = self + newSelf.unselectAll = unselectAll + return newSelf + } + +} diff --git a/Sources/Adwaita/View/Generated/HeaderBar.swift b/Sources/Adwaita/View/Generated/HeaderBar.swift index 397dc49..475835c 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Label.swift b/Sources/Adwaita/View/Generated/Label.swift index fe8d5bc..21696ac 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/LevelBar.swift b/Sources/Adwaita/View/Generated/LevelBar.swift index e3f0241..1431371 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/LinkButton.swift b/Sources/Adwaita/View/Generated/LinkButton.swift index 5a85e1b..2f2fd6c 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ListBox.swift b/Sources/Adwaita/View/Generated/ListBox.swift index 9a47497..3ed2b98 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Menu.swift b/Sources/Adwaita/View/Generated/Menu.swift index fb38702..83af9ba 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Overlay.swift b/Sources/Adwaita/View/Generated/Overlay.swift index 2376d68..b3ca536 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/OverlaySplitView.swift b/Sources/Adwaita/View/Generated/OverlaySplitView.swift index 2dd0dc7..e7f035c 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/PasswordEntryRow.swift b/Sources/Adwaita/View/Generated/PasswordEntryRow.swift index 3709b18..373476c 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Popover.swift b/Sources/Adwaita/View/Generated/Popover.swift index d8a8320..0b36066 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/PreferencesGroup.swift b/Sources/Adwaita/View/Generated/PreferencesGroup.swift index 410ff11..3c2a81e 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/PreferencesPage.swift b/Sources/Adwaita/View/Generated/PreferencesPage.swift index 2fd48e6..2ecd176 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/PreferencesRow.swift b/Sources/Adwaita/View/Generated/PreferencesRow.swift index 39adc53..e86ddaa 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ProgressBar.swift b/Sources/Adwaita/View/Generated/ProgressBar.swift index e83852f..97d7588 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ScrolledWindow.swift b/Sources/Adwaita/View/Generated/ScrolledWindow.swift index 6c8b43a..cdd9178 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/SpinRow.swift b/Sources/Adwaita/View/Generated/SpinRow.swift index 585d121..cb2557d 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/Spinner.swift b/Sources/Adwaita/View/Generated/Spinner.swift index 6fadfdc..f1ae909 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/SplitButton.swift b/Sources/Adwaita/View/Generated/SplitButton.swift index b7de3ef..4cb2611 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/StatusPage.swift b/Sources/Adwaita/View/Generated/StatusPage.swift index d2b7f82..4559018 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/SwitchRow.swift b/Sources/Adwaita/View/Generated/SwitchRow.swift index d5d5183..8fb95f2 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ToastOverlay.swift b/Sources/Adwaita/View/Generated/ToastOverlay.swift index a289d6f..58285d3 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ToggleButton.swift b/Sources/Adwaita/View/Generated/ToggleButton.swift index 030854a..aeae83f 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/ToolbarView.swift b/Sources/Adwaita/View/Generated/ToolbarView.swift index 6f24eeb..9eab9bb 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Adwaita/View/Generated/WindowTitle.swift b/Sources/Adwaita/View/Generated/WindowTitle.swift index d2d3fe6..71cd91c 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 10.02.24. +// Created by auto-generation on 12.02.24. // import CAdw diff --git a/Sources/Generation/GenerationConfiguration.swift b/Sources/Generation/GenerationConfiguration.swift index 4c1375f..1ef1000 100644 --- a/Sources/Generation/GenerationConfiguration.swift +++ b/Sources/Generation/GenerationConfiguration.swift @@ -220,7 +220,16 @@ struct GenerationConfiguration { ] ), .init(class: "Overlay", staticWidgets: [.init(name: "overlay", add: "gtk_overlay_add_overlay")]), - .init(class: "Popover", excludeProperties: ["pointing-to", "position"], cast: true) + .init(class: "Popover", excludeProperties: ["pointing-to", "position"], cast: true), + .init( + class: "FlowBox", + dynamicWidget: .init( + insert: "gtk_flow_box_insert", + remove: "gtk_flow_box_remove", + getElement: "gtk_flow_box_get_child_at_index(widget, index.cInt)?.cast()" + ), + excludeProperties: ["selection-mode"] + ) ] /// The unshortening map. diff --git a/Tests/FlowBoxDemo.swift b/Tests/FlowBoxDemo.swift new file mode 100644 index 0000000..221a5ed --- /dev/null +++ b/Tests/FlowBoxDemo.swift @@ -0,0 +1,49 @@ +// +// ListDemo.swift +// Adwaita +// +// Created by david-swift on 01.01.24. +// + +// swiftlint:disable missing_docs no_magic_numbers + +import Adwaita +import Foundation + +struct FlowBoxDemo: View { + + @State private var items: [ListDemo.Element] = [] + @State private var selectedItem = "" + + var view: Body { + HStack { + Button("Add Element") { + let element = ListDemo.Element(id: UUID().uuidString) + items.append(element) + selectedItem = element.id + } + Button("Delete Selected Element") { + let index = items.firstIndex { $0.id == selectedItem } + items = items.filter { $0.id != selectedItem } + selectedItem = items[safe: index]?.id ?? items[safe: index ?? 0 - 1]?.id ?? items.first?.id ?? "" + } + } + .padding() + .style("linked") + .halign(.center) + if !items.isEmpty { + FlowBox(items, selection: $selectedItem) { item in + HStack { + Text(.init("\(item.id)".prefix(5))) + .hexpand() + } + .padding() + } + .valign(.center) + .padding() + } + } + +} + +// swiftlint:enable missing_docs no_magic_numbers diff --git a/Tests/Page.swift b/Tests/Page.swift index 5aec70f..edc9bc6 100644 --- a/Tests/Page.swift +++ b/Tests/Page.swift @@ -24,6 +24,7 @@ enum Page: String, Identifiable, CaseIterable, Codable { case viewSwitcher case form case popover + case flowBox var id: Self { self @@ -35,6 +36,8 @@ enum Page: String, Identifiable, CaseIterable, Codable { return "Overlay Window" case .viewSwitcher: return "View Switcher" + case .flowBox: + return "Flow Box" default: return rawValue.capitalized } @@ -77,6 +80,8 @@ enum Page: String, Identifiable, CaseIterable, Codable { return "Group controls used for data entry." case .popover: return "Present content in a bubble-like context popup." + case .flowBox: + return "Display views in a reflowing grid." } } @@ -110,6 +115,8 @@ enum Page: String, Identifiable, CaseIterable, Codable { FormDemo(app: app) case .popover: PopoverDemo() + case .flowBox: + FlowBoxDemo() } } // swiftlint:enable cyclomatic_complexity diff --git a/Tests/PopoverDemo.swift b/Tests/PopoverDemo.swift index 8de65ac..c42e0d3 100644 --- a/Tests/PopoverDemo.swift +++ b/Tests/PopoverDemo.swift @@ -20,9 +20,9 @@ struct PopoverDemo: View { } .style("suggested-action") .frame(maxSize: 100) - } - .popover(visible: $visible) { - CounterDemo() + .popover(visible: $visible) { + CounterDemo() + } } }