// // BreakpointBin.swift // Adwaita // // Created by david-swift on 21.10.24. // import CAdw import Meta /// Bind a dimension's to observe whether the child view matches a condition. public struct BreakpointBin: AdwaitaWidget { /// The child view. @ViewProperty( set: { bin, view in var width: Int32 = 0 var height: Int32 = 0 gtk_widget_measure(view.cast(), GTK_ORIENTATION_VERTICAL, -1, &height, nil, nil, nil) gtk_widget_measure(view.cast(), GTK_ORIENTATION_HORIZONTAL, -1, &width, nil, nil, nil) gtk_widget_set_size_request(bin.cast(), width, height) adw_breakpoint_bin_set_child(bin.cast(), view.cast()) }, pointer: OpaquePointer.self, subview: OpaquePointer.self, context: AdwaitaMainView.self ) var content /// The condition. @Property( set: { bin, condition, storage in var condition = condition switch condition { case let .naturalWidth(padding): let child = adw_breakpoint_bin_get_child(bin.cast()) var size: Int32 = 0 gtk_widget_measure(child, GTK_ORIENTATION_HORIZONTAL, -1, nil, &size, nil, nil) condition = .minWidth(.init(size) + padding) case let .naturalHeight(padding): let child = adw_breakpoint_bin_get_child(bin.cast()) var size: Int32 = 0 gtk_widget_measure(child, GTK_ORIENTATION_VERTICAL, -1, nil, &size, nil, nil) condition = .minHeight(.init(size) + padding) default: break } storage.fields["condition"] = condition let string = adw_breakpoint_condition_parse(condition.condition) if let breakpoint = storage.fields["breakpoint"] as? OpaquePointer { g_object_unref(adw_breakpoint_get_condition(breakpoint)?.cast()) adw_breakpoint_set_condition(breakpoint, string) } else { let breakpoint = adw_breakpoint_new(string) adw_breakpoint_bin_add_breakpoint(bin.cast(), breakpoint) storage.fields["breakpoint"] = breakpoint if let matches = storage.fields["binding"] as? Binding { Idle { condition.initialize(child: adw_breakpoint_bin_get_child(bin.cast()), matches: matches) } } } }, pointer: OpaquePointer.self ) var condition: BreakpointCondition = .maxWidth(0) /// Whether the child view does not match the condition. @BindingProperty( observe: { bin, matches, storage in storage.notify(name: "current-breakpoint") { matches.wrappedValue = adw_breakpoint_bin_get_current_breakpoint(bin.cast()) != nil } storage.fields["binding"] = matches }, set: { _, _, _ in }, pointer: OpaquePointer.self ) var matches: Binding = .constant(false) /// Initialize a breakpoint bin. /// - Parameters: /// - condition: The condition. /// - matches: Whether the content matches the condition. /// - content: The content. public init( condition: BreakpointCondition, matches: Binding, @ViewBuilder content: () -> Body ) { self.condition = condition self.matches = matches self.content = content() } /// Initialize the widget. public func initializeWidget() -> Any { adw_breakpoint_bin_new()?.opaque() as Any } } /// A breakpoint condition. public enum BreakpointCondition: Equatable { /// Define a maximum width. case maxWidth(_ width: Int) /// Define a maximum height. case maxHeight(_ height: Int) /// Define a minimum width. case minWidth(_ width: Int) /// Define a minimum height. case minHeight(_ height: Int) /// The minimum width is the content's natural width. case naturalWidth(padding: Int = 0) /// The minimum height is the content's natural height. case naturalHeight(padding: Int = 0) /// The condition to parse. var condition: String? { switch self { case let .maxWidth(width): "max-width: \(width)sp" case let .maxHeight(height): "max-height: \(height)sp" case let .minWidth(width): "min-width: \(width)sp" case let .minHeight(height): "min-height: \(height)sp" default: nil } } /// Initialize the breakpoint when initializing the view. /// - Parameters: /// - child: The widget. /// - matches: The matches binding. func initialize(child: UnsafeMutablePointer?, matches: Binding) { switch self { case let .maxWidth(width): matches.wrappedValue = gtk_widget_get_width(child) <= width case let .minWidth(width): matches.wrappedValue = gtk_widget_get_width(child) >= width case let .maxHeight(height): matches.wrappedValue = gtk_widget_get_height(child) <= height case let .minHeight(height): matches.wrappedValue = gtk_widget_get_height(child) >= height default: break } } }