// // ListBox.swift // Adwaita // // Created by auto-generation on 04.11.25. // import CAdw import LevenshteinTransformations /// Shows a vertical list. /// /// /// /// A `GtkListBox` only contains `GtkListBoxRow` children. These rows can /// by dynamically sorted and filtered, and headers can be added dynamically /// depending on the row content. It also allows keyboard and mouse navigation /// and selection like a typical list. /// /// Using `GtkListBox` is often an alternative to `GtkTreeView`, especially /// when the list contents has a more complicated layout than what is allowed /// by a `GtkCellRenderer`, or when the contents is interactive (i.e. has a /// button in it). /// /// Although a `GtkListBox` must have only `GtkListBoxRow` children, you can /// add any kind of widget to it via `Gtk.ListBox.prepend`, /// `Gtk.ListBox.append` and `Gtk.ListBox.insert` and a /// `GtkListBoxRow` widget will automatically be inserted between the list /// and the widget. /// /// `GtkListBoxRows` can be marked as activatable or selectable. If a row is /// activatable, `Gtk.ListBox::row-activated` will be emitted for it when /// the user tries to activate it. If it is selectable, the row will be marked /// as selected when the user tries to select it. /// /// public struct ListBox: 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? /// Whether to show separators between rows. var showSeparators: Bool? /// Emitted when the cursor row is activated. var activateCursorRow: (() -> Void)? /// Emitted when the user initiates a cursor movement. /// /// 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 when a row has been activated by the user. var rowActivated: (() -> Void)? /// Emitted when a new row is selected, or (with a %NULL @row) /// when the selection is cleared. /// /// When the @box is using %GTK_SELECTION_MULTIPLE, this signal will not /// give you the full picture of selection changes, and you should use /// the `Gtk.ListBox::selected-rows-changed` signal instead. var rowSelected: (() -> 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 binding for this signal is Ctrl-a. var selectAll: (() -> Void)? /// Emitted when the set of selected rows changes. var selectedRowsChanged: (() -> Void)? /// Emitted when the cursor row is toggled. /// /// The default bindings for this signal is Ctrl+. var toggleCursorRow: (() -> 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 binding 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 `ListBox`. 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_list_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 activateCursorRow { storage.connectSignal(name: "activate-cursor-row", argCount: 0) { activateCursorRow() } } if let moveCursor { storage.connectSignal(name: "move-cursor", argCount: 4) { moveCursor() } } if let rowActivated { storage.connectSignal(name: "row-activated", argCount: 1) { rowActivated() } } if let rowSelected { storage.connectSignal(name: "row-selected", argCount: 1) { rowSelected() } } if let selectAll { storage.connectSignal(name: "select-all", argCount: 0) { selectAll() } } if let selectedRowsChanged { storage.connectSignal(name: "selected-rows-changed", argCount: 0) { selectedRowsChanged() } } if let toggleCursorRow { storage.connectSignal(name: "toggle-cursor-row", argCount: 0) { toggleCursorRow() } } 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_list_box_set_activate_on_single_click(widget, activateOnSingleClick.cBool) } if let showSeparators, updateProperties, (storage.previousState as? Self)?.showSeparators != showSeparators { gtk_list_box_set_show_separators(widget, showSeparators.cBool) } var contentStorage: [ViewStorage] = storage.content[.mainContent] ?? [] let old = storage.fields["element"] as? [Element] ?? [] old.identifiableTransform( to: elements, functions: .init { 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).storage(data: data, 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).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 } } /// Whether to show separators between rows. public func showSeparators(_ showSeparators: Bool? = true) -> Self { modify { $0.showSeparators = showSeparators } } /// Emitted when the cursor row is activated. public func activateCursorRow(_ activateCursorRow: @escaping () -> Void) -> Self { var newSelf = self newSelf.activateCursorRow = activateCursorRow return newSelf } /// Emitted when the user initiates a cursor movement. /// /// 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 when a row has been activated by the user. public func rowActivated(_ rowActivated: @escaping () -> Void) -> Self { var newSelf = self newSelf.rowActivated = rowActivated return newSelf } /// Emitted when a new row is selected, or (with a %NULL @row) /// when the selection is cleared. /// /// When the @box is using %GTK_SELECTION_MULTIPLE, this signal will not /// give you the full picture of selection changes, and you should use /// the `Gtk.ListBox::selected-rows-changed` signal instead. public func rowSelected(_ rowSelected: @escaping () -> Void) -> Self { var newSelf = self newSelf.rowSelected = rowSelected 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 binding 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 rows changes. public func selectedRowsChanged(_ selectedRowsChanged: @escaping () -> Void) -> Self { var newSelf = self newSelf.selectedRowsChanged = selectedRowsChanged return newSelf } /// Emitted when the cursor row is toggled. /// /// The default bindings for this signal is Ctrl+. public func toggleCursorRow(_ toggleCursorRow: @escaping () -> Void) -> Self { var newSelf = self newSelf.toggleCursorRow = toggleCursorRow 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 binding for this signal is /// Ctrl-Shift-a. public func unselectAll(_ unselectAll: @escaping () -> Void) -> Self { var newSelf = self newSelf.unselectAll = unselectAll return newSelf } }