89 lines
3.9 KiB
Swift
89 lines
3.9 KiB
Swift
import Adw
|
|
import Gtk
|
|
|
|
public extension ComboRow {
|
|
/// Creates a ComboRow with static text, items, and selection behavior.
|
|
///
|
|
/// Values are applied once when the widget mounts. The callback receives the selected item string.
|
|
/// - Parameter title: The row title.
|
|
/// - Parameter subtitle: The optional row subtitle.
|
|
/// - Parameter items: The optional strings shown by the row.
|
|
/// - Parameter onSelectedItem: The optional selection callback.
|
|
@_disfavoredOverload
|
|
init<S: StringProtocol>(_ title: S, subtitle: String? = nil, items: [String]? = nil, _ onSelectedItem: ((String) -> Void)? = nil) {
|
|
self.init()
|
|
self = self.title(String(title))
|
|
if let subtitle { self = self.subtitle(subtitle) }
|
|
if let items { self = self.comboItems(ComboItems(items), onSelectedItem: onSelectedItem) }
|
|
}
|
|
|
|
/// Creates a ComboRow whose text and items follow bindings.
|
|
///
|
|
/// Binding modifiers update the widget in place and write widget changes back. Replacing the model resets selection to its first item.
|
|
/// - Parameter title: The title binding.
|
|
/// - Parameter subtitle: The optional subtitle binding.
|
|
/// - Parameter items: The optional item-list binding.
|
|
/// - Parameter onSelectedItem: The optional selection callback.
|
|
init(_ title: Portico.Binding<String>, subtitle: Portico.Binding<String>? = nil, items: Portico.Binding<[String]>? = nil, _ onSelectedItem: ((String) -> Void)? = nil) {
|
|
self.init()
|
|
self = self.title(title)
|
|
if let subtitle { self = self.subtitle(subtitle) }
|
|
if let items {
|
|
let holder = ComboItems(items.untrackedValue)
|
|
self = self.comboItems(holder, onSelectedItem: onSelectedItem)
|
|
self = self.appending { w, ctx in
|
|
ctx.registry.add(items.subscribe { [w] values in
|
|
holder.values = values
|
|
w.setModel(model: Gtk.StringList(strings: values))
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Creates a ComboRow with live interpolated text values.
|
|
///
|
|
/// Interpolated segments are re-evaluated when their dependencies change. The callback receives the selected item string.
|
|
/// - Parameter title: The interpolated title.
|
|
/// - Parameter subtitle: The optional interpolated subtitle.
|
|
/// - Parameter items: The optional strings shown by the row.
|
|
/// - Parameter onSelectedItem: The optional selection callback.
|
|
init(_ title: Portico.InterpolatedText, subtitle: Portico.InterpolatedText? = nil, items: [String]? = nil, _ onSelectedItem: ((String) -> Void)? = nil) {
|
|
self.init()
|
|
self = self.title(title)
|
|
if let subtitle { self = self.subtitle(subtitle) }
|
|
if let items { self = self.comboItems(ComboItems(items), onSelectedItem: onSelectedItem) }
|
|
}
|
|
|
|
}
|
|
|
|
/// Mutable item storage shared by a combo model and its selection callback.
|
|
@MainActor private final class ComboItems {
|
|
var values: [String]
|
|
|
|
init(_ values: [String]) {
|
|
self.values = values
|
|
}
|
|
}
|
|
|
|
private extension ComboRow {
|
|
/// Installs a string model and reports valid selection changes.
|
|
///
|
|
/// Selection positions that are invalid or outside the current item list are ignored.
|
|
func comboItems(_ items: ComboItems, onSelectedItem: ((String) -> Void)?) -> Self {
|
|
var view = appending { w, _ in
|
|
w.setModel(model: Gtk.StringList(strings: items.values))
|
|
}
|
|
guard let onSelectedItem else { return view }
|
|
view = view.appending { w, ctx in
|
|
var last = w.getSelected()
|
|
ctx.registry.add(w.connectNotify(detail: nil) { [w] _, _ in
|
|
let position = w.getSelected()
|
|
guard position != last else { return }
|
|
last = position
|
|
guard position != UInt32.max, Int(position) < items.values.count else { return }
|
|
onSelectedItem(items.values[Int(position)])
|
|
})
|
|
}
|
|
return view
|
|
}
|
|
}
|