70 lines
2.7 KiB
Swift
70 lines
2.7 KiB
Swift
import Gtk
|
|
|
|
/// A view that switches between two branches based on a `Binding<Bool>` condition.
|
|
///
|
|
/// Internally uses `Gtk.Stack` with named children `"true"` and `"false"`.
|
|
/// Each branch's widgets are mounted at most once — the first time that
|
|
/// branch becomes active. Subsequent condition changes only call
|
|
/// `setVisibleChildName`.
|
|
@MainActor public struct EitherView: View {
|
|
private let condition: Binding<Bool>
|
|
private let firstContent: [AnyView]
|
|
private let secondContent: [AnyView]
|
|
|
|
public var body: Never { fatalError() }
|
|
|
|
/// - Parameters:
|
|
/// - condition: The binding that selects the visible branch.
|
|
/// - first: The view for the `true` branch (mounted once, lazily).
|
|
/// - second: The view for the `false` branch (mounted once, lazily).
|
|
public init(
|
|
_ condition: Binding<Bool>,
|
|
@ViewBuilder first: () -> [AnyView],
|
|
@ViewBuilder second: () -> [AnyView]
|
|
) {
|
|
self.condition = condition
|
|
self.firstContent = first()
|
|
self.secondContent = second()
|
|
}
|
|
}
|
|
|
|
@_spi(Portico) extension EitherView: Mountable {
|
|
@_spi(Portico) public func mount(_ ctx: MountContext) -> Gtk.Widget {
|
|
let stack = Gtk.Stack()
|
|
|
|
// Branch widget caches — nil until the branch is first mounted.
|
|
var trueWidgets: [Gtk.Widget]? = nil
|
|
var falseWidgets: [Gtk.Widget]? = nil
|
|
|
|
// Mount the initially-active branch first so it's visible immediately.
|
|
if condition.wrappedValue {
|
|
trueWidgets = firstContent.map { $0.makeWidget(ctx) }
|
|
for w in trueWidgets! { _ = stack.addNamed(child: w, name: "true") }
|
|
} else {
|
|
falseWidgets = secondContent.map { $0.makeWidget(ctx) }
|
|
for w in falseWidgets! { _ = stack.addNamed(child: w, name: "false") }
|
|
}
|
|
|
|
// Track changes — re-evaluate condition, lazily mount the other
|
|
// branch on first flip.
|
|
let tracker = DependencyTracker { [stack] in
|
|
let isTrue = condition.wrappedValue
|
|
if isTrue {
|
|
if trueWidgets == nil {
|
|
trueWidgets = firstContent.map { $0.makeWidget(ctx) }
|
|
for w in trueWidgets! { _ = stack.addNamed(child: w, name: "true") }
|
|
}
|
|
stack.setVisibleChildName(name: "true")
|
|
} else {
|
|
if falseWidgets == nil {
|
|
falseWidgets = secondContent.map { $0.makeWidget(ctx) }
|
|
for w in falseWidgets! { _ = stack.addNamed(child: w, name: "false") }
|
|
}
|
|
stack.setVisibleChildName(name: "false")
|
|
}
|
|
}
|
|
tracker.run()
|
|
ctx.registry.add(tracker)
|
|
return stack
|
|
}
|
|
}
|