portico/Sources/Portico/Extensions/ComboRow+Extras.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
}
}