adwaita-swift/Sources/Adwaita/View/BreakpointBin.swift
david-swift 04c77831b5
Some checks are pending
Deploy Docs / publish (push) Waiting to run
SwiftLint / SwiftLint (push) Waiting to run
Remove Core library
2025-10-30 21:36:13 +01:00

155 lines
5.3 KiB
Swift

//
// 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<Bool> {
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<Bool> = .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<Bool>,
@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<GtkWidget>?, matches: Binding<Bool>) {
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
}
}
}