Skip to content

Commit a76ca7c

Browse files
committed
finished GtkBackendSheets
1 parent ed300d2 commit a76ca7c

File tree

3 files changed

+81
-28
lines changed

3 files changed

+81
-28
lines changed

Examples/Sources/WindowingExample/WindowingApp.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ struct SheetDemo: View {
118118
print("nested sheet dismissed")
119119
} content: {
120120
NestedSheetBody(dismissParent: { dismiss() })
121+
.presentationCornerRadius(35)
121122
}
122123
}
123124

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/GtkBackend/GtkBackend.swift

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public final class GtkBackend: AppBackend {
3737
public let menuImplementationStyle = MenuImplementationStyle.dynamicPopover
3838
public let canRevealFiles = true
3939
public let deviceClass = DeviceClass.desktop
40+
public let defaultSheetCornerRadius = 10
4041

4142
var gtkApp: Application
4243

@@ -73,21 +74,35 @@ public final class GtkBackend: AppBackend {
7374
let backend = Unmanaged<GtkBackend>.fromOpaque(userData).takeUnretainedValue()
7475
let key = OpaquePointer(instance)
7576
guard let ctx = backend.sheetContexts[key] else { return 1 }
76-
77+
7778
if ctx.interactiveDismissDisabled { return 1 }
78-
79+
7980
if ctx.isProgrammaticDismiss {
8081
// Suppress onDismiss for programmatic closes
8182
ctx.isProgrammaticDismiss = false
8283
return 1
8384
}
84-
85+
8586
backend.runInMainThread {
8687
ctx.onDismiss()
8788
}
8889
return 1
8990
}
90-
91+
92+
// C-convention thunk for key-pressed
93+
private let escapeKeyPressedThunk: @convention(c) (
94+
UnsafeMutableRawPointer?, guint, guint, GdkModifierType, gpointer?
95+
) -> gboolean = { controller, keyval, keycode, state, userData in
96+
// TRUE (1) = consume event
97+
if keyval == GDK_KEY_Escape {
98+
guard let userData else { return 1 }
99+
let box = Unmanaged<ValueBox<() -> Void>>.fromOpaque(userData).takeUnretainedValue()
100+
box.value()
101+
return 1 // consume
102+
}
103+
return 0 // let others handle
104+
}
105+
91106
// A separate initializer to satisfy ``AppBackend``'s requirements.
92107
public convenience init() {
93108
self.init(appIdentifier: nil)
@@ -1616,34 +1631,53 @@ public final class GtkBackend: AppBackend {
16161631
public func updateSheet(_ sheet: Gtk.Window, content: Widget, onDismiss: @escaping () -> Void) {
16171632
sheet.setChild(content)
16181633

1619-
// Track per-sheet context and hook close-request once
16201634
let key: OpaquePointer = OpaquePointer(sheet.widgetPointer)
16211635

1622-
if let ctx = sheetContexts[key] {
1623-
// Update onDismiss if sheet already tracked
1624-
ctx.onDismiss = onDismiss
1625-
} else {
1626-
// First-time setup: store context and connect signal
1627-
let ctx = SheetContext(onDismiss: onDismiss)
1628-
sheetContexts[key] = ctx
1629-
1630-
if connectedCloseHandlers.insert(key).inserted {
1631-
let handler: GCallback = unsafeBitCast(Self.closeRequestThunk, to: GCallback.self)
1632-
g_signal_connect_data(
1633-
UnsafeMutableRawPointer(sheet.gobjectPointer),
1634-
"close-request",
1635-
handler,
1636-
Unmanaged.passUnretained(self).toOpaque(),
1637-
nil,
1638-
GConnectFlags(0)
1639-
)
1640-
}
1636+
//add a slight border to not be just a flat corner
1637+
sheet.css.set(property: .border(color: SwiftCrossUI.Color.gray.gtkColor, width: 1))
1638+
1639+
let ctx = getOrCreateSheetContext(for: sheet)
1640+
ctx.onDismiss = onDismiss
1641+
1642+
sheet.css.set(property: .cornerRadius(defaultSheetCornerRadius))
1643+
1644+
if connectedCloseHandlers.insert(key).inserted {
1645+
let handler: GCallback = unsafeBitCast(Self.closeRequestThunk, to: GCallback.self)
1646+
g_signal_connect_data(
1647+
UnsafeMutableRawPointer(sheet.gobjectPointer),
1648+
"close-request",
1649+
handler,
1650+
Unmanaged.passUnretained(self).toOpaque(),
1651+
nil,
1652+
GConnectFlags(0)
1653+
)
1654+
1655+
let escapeHandler = gtk_event_controller_key_new()
1656+
gtk_event_controller_set_propagation_phase(escapeHandler, GTK_PHASE_BUBBLE)
1657+
g_signal_connect_data (
1658+
UnsafeMutableRawPointer(escapeHandler),
1659+
"key-pressed",
1660+
unsafeBitCast(escapeKeyPressedThunk, to: GCallback.self),
1661+
Unmanaged.passRetained(ValueBox(value: {
1662+
if ctx.interactiveDismissDisabled { return }
1663+
self.runInMainThread {
1664+
ctx.onDismiss()
1665+
}
1666+
})).toOpaque(),
1667+
{ data, _ in
1668+
if let data {
1669+
Unmanaged<ValueBox<() -> Void>>.fromOpaque(data).release()
1670+
}
1671+
},
1672+
G_CONNECT_DEFAULT
1673+
)
1674+
gtk_widget_add_controller(sheet.widgetPointer, escapeHandler)
16411675
}
16421676
}
16431677

16441678
public func showSheet(_ sheet: Gtk.Window, window: ApplicationWindow?) {
16451679
sheet.isModal = true
1646-
sheet.isDecorated = false // optional for a more sheet-like look
1680+
sheet.isDecorated = false
16471681
sheet.setTransient(for: window ?? windows[0])
16481682
sheet.present()
16491683
}
@@ -1664,13 +1698,24 @@ public final class GtkBackend: AppBackend {
16641698
}
16651699

16661700
public func setInteractiveDismissDisabled(for sheet: Gtk.Window, to disabled: Bool) {
1701+
let ctx = getOrCreateSheetContext(for: sheet)
1702+
1703+
ctx.interactiveDismissDisabled = disabled
1704+
}
1705+
1706+
public func setPresentationCornerRadius(of sheet: Gtk.Window, to radius: Double) {
1707+
let radius = Int(radius)
1708+
sheet.css.set(property: .cornerRadius(radius))
1709+
}
1710+
1711+
private func getOrCreateSheetContext(for sheet: Gtk.Window) -> SheetContext {
16671712
let key: OpaquePointer = OpaquePointer(sheet.widgetPointer)
16681713
if let ctx = sheetContexts[key] {
1669-
ctx.interactiveDismissDisabled = disabled
1714+
return ctx
16701715
} else {
16711716
let ctx = SheetContext(onDismiss: {})
1672-
ctx.interactiveDismissDisabled = disabled
16731717
sheetContexts[key] = ctx
1718+
return ctx
16741719
}
16751720
}
16761721
}
@@ -1691,3 +1736,10 @@ extension Gtk.Window: SheetImplementation {
16911736
return SIMD2(x: self.size.width, y: self.size.height)
16921737
}
16931738
}
1739+
1740+
final class ValueBox<T> {
1741+
let value: T
1742+
init(value: T) {
1743+
self.value = value
1744+
}
1745+
}

0 commit comments

Comments
 (0)