374 lines
15 KiB
Swift
374 lines
15 KiB
Swift
//
|
|
// FlowBox.swift
|
|
// Adwaita
|
|
//
|
|
// Created by auto-generation on 31.10.25.
|
|
//
|
|
|
|
import CAdw
|
|
import LevenshteinTransformations
|
|
|
|
/// Puts child widgets in a 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 `Gtk.FlowBox.insert`, and a
|
|
/// `GtkFlowBoxChild` widget will automatically be inserted between the box
|
|
/// and the widget.
|
|
///
|
|
/// Also see `Gtk.ListBox`.
|
|
///
|
|
///
|
|
public struct FlowBox<Element>: 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.
|
|
///
|
|
/// - <kbd>←</kbd>, <kbd>→</kbd>, <kbd>↑</kbd>, <kbd>↓</kbd>
|
|
/// move by individual children
|
|
/// - <kbd>Home</kbd>, <kbd>End</kbd> move to the ends of the box
|
|
/// - <kbd>PgUp</kbd>, <kbd>PgDn</kbd> 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 <kbd>Ctrl</kbd>-<kbd>a</kbd>.
|
|
var selectAll: (() -> Void)?
|
|
/// Emitted when the set of selected children changes.
|
|
///
|
|
/// Use `Gtk.FlowBox.selected_foreach` or
|
|
/// `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 <kbd>Ctrl</kbd>-<kbd>Space</kbd>.
|
|
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 <kbd>Ctrl</kbd>-<kbd>Shift</kbd>-<kbd>a</kbd>.
|
|
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>(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<Data>(_ 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 {
|
|
modify { $0.acceptUnpairedRelease = acceptUnpairedRelease }
|
|
}
|
|
|
|
/// The accessible role of the given `GtkAccessible` implementation.
|
|
///
|
|
/// The accessible role cannot be changed once set.
|
|
public func accessibleRole(_ accessibleRole: String?) -> Self {
|
|
modify { $0.accessibleRole = accessibleRole }
|
|
}
|
|
|
|
/// Determines whether children can be activated with a single
|
|
/// click, or require a double-click.
|
|
public func activateOnSingleClick(_ activateOnSingleClick: Bool? = true) -> Self {
|
|
modify { $0.activateOnSingleClick = activateOnSingleClick }
|
|
}
|
|
|
|
/// The amount of horizontal space between two children.
|
|
public func columnSpacing(_ columnSpacing: UInt?) -> Self {
|
|
modify { $0.columnSpacing = columnSpacing }
|
|
}
|
|
|
|
/// Determines whether all children should be allocated the
|
|
/// same size.
|
|
public func homogeneous(_ homogeneous: Bool? = true) -> Self {
|
|
modify { $0.homogeneous = homogeneous }
|
|
}
|
|
|
|
/// The maximum amount of children to request space for consecutively
|
|
/// in the given orientation.
|
|
public func maxChildrenPerLine(_ maxChildrenPerLine: UInt?) -> Self {
|
|
modify { $0.maxChildrenPerLine = maxChildrenPerLine }
|
|
}
|
|
|
|
/// 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 {
|
|
modify { $0.minChildrenPerLine = minChildrenPerLine }
|
|
}
|
|
|
|
/// The amount of vertical space between two children.
|
|
public func rowSpacing(_ rowSpacing: UInt?) -> Self {
|
|
modify { $0.rowSpacing = rowSpacing }
|
|
}
|
|
|
|
/// 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.
|
|
///
|
|
/// - <kbd>←</kbd>, <kbd>→</kbd>, <kbd>↑</kbd>, <kbd>↓</kbd>
|
|
/// move by individual children
|
|
/// - <kbd>Home</kbd>, <kbd>End</kbd> move to the ends of the box
|
|
/// - <kbd>PgUp</kbd>, <kbd>PgDn</kbd> 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 <kbd>Ctrl</kbd>-<kbd>a</kbd>.
|
|
public func selectAll(_ selectAll: @escaping () -> Void) -> Self {
|
|
var newSelf = self
|
|
newSelf.selectAll = selectAll
|
|
return newSelf
|
|
}
|
|
|
|
/// Emitted when the set of selected children changes.
|
|
///
|
|
/// Use `Gtk.FlowBox.selected_foreach` or
|
|
/// `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 <kbd>Ctrl</kbd>-<kbd>Space</kbd>.
|
|
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 <kbd>Ctrl</kbd>-<kbd>Shift</kbd>-<kbd>a</kbd>.
|
|
public func unselectAll(_ unselectAll: @escaping () -> Void) -> Self {
|
|
var newSelf = self
|
|
newSelf.unselectAll = unselectAll
|
|
return newSelf
|
|
}
|
|
|
|
}
|