// // FlowBox.swift // Adwaita // // Created by auto-generation on 09.04.25. // import CAdw import LevenshteinTransformations /// Puts child widgets in a reflowing grid. /// /// An example GtkFlowBox /// /// 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]. /// /// # Shortcuts and Gestures /// /// The following signals have default keybindings: /// /// - [signal@Gtk.FlowBox::move-cursor] /// - [signal@Gtk.FlowBox::select-all] /// - [signal@Gtk.FlowBox::toggle-cursor-child] /// - [signal@Gtk.FlowBox::unselect-all] /// /// # 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 [enum@Gtk.AccessibleRole.grid] role, and `GtkFlowBoxChild` /// uses the [enum@Gtk.AccessibleRole.grid_cell] role. public struct FlowBox: AdwaitaWidget where Element: Identifiable { /// Additional update functions for type extensions. var updateFunctions: [(ViewStorage, WidgetData, Bool) -> Void] = [] /// Additional appear functions for type extensions. var appearFunctions: [(ViewStorage, WidgetData) -> Void] = [] /// Whether to accept unpaired release events. var acceptUnpairedRelease: Bool? /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. var accessibleRole: String? /// 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 /// Initialize `FlowBox`. public init(_ elements: [Element], @ViewBuilder content: @escaping (Element) -> Body) { self.elements = elements self.content = content } /// The view storage. /// - Parameters: /// - modifiers: Modify views before being updated. /// - type: The view render data type. /// - Returns: The view storage. public func container(data: WidgetData, type: Data.Type) -> ViewStorage where Data: ViewRenderData { let storage = ViewStorage(gtk_flow_box_new()?.opaque()) for function in appearFunctions { function(storage, data) } update(storage, data: data, 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 view render data type. public func update(_ storage: ViewStorage, data: WidgetData, updateProperties: Bool, type: Data.Type) where Data: ViewRenderData { 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, (storage.previousState as? Self)?.activateOnSingleClick != activateOnSingleClick { gtk_flow_box_set_activate_on_single_click(widget, activateOnSingleClick.cBool) } if let columnSpacing, updateProperties, (storage.previousState as? Self)?.columnSpacing != columnSpacing { gtk_flow_box_set_column_spacing(widget, columnSpacing.cInt) } if let homogeneous, updateProperties, (storage.previousState as? Self)?.homogeneous != homogeneous { gtk_flow_box_set_homogeneous(widget, homogeneous.cBool) } 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, (storage.previousState as? Self)?.minChildrenPerLine != minChildrenPerLine { gtk_flow_box_set_min_children_per_line(widget, minChildrenPerLine.cInt) } if let rowSpacing, updateProperties, (storage.previousState as? Self)?.rowSpacing != rowSpacing { 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 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).storage(data: data, 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).updateStorage(contentStorage[index], data: data, updateProperties: updateProperties, type: type) } } for function in updateFunctions { function(storage, data, updateProperties) } if updateProperties { storage.previousState = self } } /// Whether to accept unpaired release events. public func acceptUnpairedRelease(_ acceptUnpairedRelease: Bool? = true) -> Self { var newSelf = self newSelf.acceptUnpairedRelease = acceptUnpairedRelease return newSelf } /// The accessible role of the given `GtkAccessible` implementation. /// /// The accessible role cannot be changed once set. public func accessibleRole(_ accessibleRole: String?) -> Self { var newSelf = self newSelf.accessibleRole = accessibleRole 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 } }