Add support for AdwBreakpointBin
All checks were successful
Deploy Docs / publish (push) Successful in 21m42s
SwiftLint / SwiftLint (push) Successful in 3s

This commit is contained in:
david-swift 2024-10-21 18:30:10 +02:00
parent 016425e329
commit 963a02b1e1
6 changed files with 169 additions and 8 deletions

View File

@ -267,4 +267,36 @@ extension AnyView {
.action(button: button, handler: handler)
}
/// Add a breakpoint.
/// - Parameters:
/// - maxWidth: The maximum width.
/// - matches: Whether the content view matches the breakpoint.
public func breakpoint(maxWidth: Int, matches: Binding<Bool>) -> AnyView {
BreakpointBin(condition: .maxWidth(maxWidth), matches: matches) { self }
}
/// Add a breakpoint.
/// - Parameters:
/// - minWidth: The minimum width.
/// - matches: Whether the content view matches the breakpoint.
public func breakpoint(minWidth: Int, matches: Binding<Bool>) -> AnyView {
BreakpointBin(condition: .minWidth(minWidth), matches: matches) { self }
}
/// Add a breakpoint.
/// - Parameters:
/// - maxHeight: The maximum height.
/// - matches: Whether the content view matches the breakpoint.
public func breakpoint(maxHeight: Int, matches: Binding<Bool>) -> AnyView {
BreakpointBin(condition: .maxHeight(maxHeight), matches: matches) { self }
}
/// Add a breakpoint.
/// - Parameters:
/// - minHeight: The minimum height.
/// - matches: Whether the content view matches the breakpoint.
public func breakpoint(minHeight: Int, matches: Binding<Bool>) -> AnyView {
BreakpointBin(condition: .minHeight(minHeight), matches: matches) { self }
}
}

View File

@ -52,3 +52,11 @@ gtui_alertdialog_choose (uint64_t dialog, uint64_t data, uint64_t parent)
adw_alert_dialog_choose (dialog, parent, NULL, gtui_alertdialog_cb, data);
}
static GValue
gtui_initialize_boolean (gboolean boolean)
{
GValue val = G_VALUE_INIT;
g_value_init(&val, G_TYPE_BOOLEAN);
g_value_set_boolean(&val, boolean);
return val;
}

View File

@ -5,6 +5,8 @@
// Created by david-swift on 15.01.24.
//
import CAdw
extension Bool {
/// Get the gboolean for C.
@ -12,4 +14,11 @@ extension Bool {
self ? 1 : 0
}
/// The gboolean as a GValue.
public var gValue: UnsafePointer<GValue> {
let pointer = UnsafeMutablePointer<GValue>.allocate(capacity: 1)
pointer.initialize(to: gtui_initialize_boolean(cBool))
return .init(pointer)
}
}

View File

@ -0,0 +1,115 @@
//
// 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
let condition = 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, condition)
} else {
let breakpoint = adw_breakpoint_new(condition)
adw_breakpoint_bin_add_breakpoint(bin.cast(), breakpoint)
storage.fields["breakpoint"] = breakpoint
if let bin = storage.content["_content"]?.first?.content["append"]?.first {
adw_breakpoint_add_setter(
breakpoint,
bin.opaquePointer?.cast(),
"visible",
false.gValue
)
}
}
},
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
}
},
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 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"
}
}
}

View File

@ -43,7 +43,6 @@ struct Demo: App {
}
.closeShortcut()
.defaultSize(width: 600, height: 400)
.resizable(false)
.title("View Switcher Demo")
Window(id: "form-demo", open: 0) { _ in
FormDemo.WindowContent()

View File

@ -8,6 +8,7 @@
// swiftlint:disable missing_docs
import Adwaita
import CAdw
struct ViewSwitcherDemo: View {
@ -33,10 +34,6 @@ struct ViewSwitcherDemo: View {
VStack {
Text(selection.title)
.padding()
Button(bottom ? "Show Top Bar" : "Show Bottom Bar") {
bottom.toggle()
}
.halign(.center)
}
.valign(.center)
.topToolbar {
@ -44,15 +41,16 @@ struct ViewSwitcherDemo: View {
HeaderBar
.empty()
} else {
toolbar
toolbar(bottom: false)
}
}
.bottomToolbar(visible: bottom) {
toolbar
toolbar(bottom: true)
}
.breakpoint(maxWidth: 600, matches: $bottom)
}
var toolbar: AnyView {
func toolbar(bottom: Bool) -> AnyView {
HeaderBar(titleButtons: !bottom) { } end: { }
.headerBarTitle {
ViewSwitcher(selectedElement: $selection)