Skip to content

Commit dce5be6

Browse files
committed
Fixed parent sheet dismissal leaves child sheet behind (GtkBackend testing pending)
1 parent eca589b commit dce5be6

File tree

8 files changed

+53
-22
lines changed

8 files changed

+53
-22
lines changed

Sources/AppKitBackend/AppKitBackend.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,13 +1725,23 @@ public final class AppKitBackend: AppBackend {
17251725
return SIMD2(x: Int(size.width), y: Int(size.height))
17261726
}
17271727

1728-
public func showSheet(_ sheet: NSCustomSheet, window: NSCustomWindow) {
1728+
public func showSheet(_ sheet: NSCustomSheet, sheetParent: Any) {
17291729
// Critical sheets stack. beginSheet only shows a nested sheet
17301730
// after its parent gets dismissed.
1731+
let window = sheetParent as! NSCustomWindow
17311732
window.beginCriticalSheet(sheet)
1733+
window.managedAttachedSheet = sheet
17321734
}
17331735

1734-
public func dismissSheet(_ sheet: NSCustomSheet, window: NSCustomWindow) {
1736+
public func dismissSheet(_ sheet: NSCustomSheet, sheetParent: Any) {
1737+
let window = sheetParent as! NSCustomWindow
1738+
1739+
if let nestedSheet = sheet.managedAttachedSheet {
1740+
dismissSheet(nestedSheet, sheetParent: sheet)
1741+
}
1742+
1743+
defer { window.managedAttachedSheet = nil }
1744+
17351745
window.endSheet(sheet)
17361746
}
17371747

@@ -2218,6 +2228,8 @@ public class NSCustomWindow: NSWindow {
22182228
var customDelegate = Delegate()
22192229
var persistentUndoManager = UndoManager()
22202230

2231+
var managedAttachedSheet: NSCustomSheet?
2232+
22212233
/// Allows the backing scale factor to be overridden. Useful for keeping
22222234
/// UI tests consistent across devices.
22232235
///

Sources/Gtk/Widgets/Window.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import CGtk
77
open class Window: Widget {
88
public var child: Widget?
99

10+
public var managedAttachedSheet: Window?
11+
1012
public convenience init() {
1113
self.init(gtk_window_new())
1214
}

Sources/Gtk3Backend/Gtk3Backend.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,9 +1517,3 @@ struct Gtk3Error: LocalizedError {
15171517
"gerror: code=\(code), domain=\(domain), message=\(message)"
15181518
}
15191519
}
1520-
1521-
extension Gtk3.Window: SheetImplementation {
1522-
public var sheetSize: SIMD2<Int> {
1523-
SIMD2(x: size.width, y: size.height)
1524-
}
1525-
}

Sources/GtkBackend/GtkBackend.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,18 +1634,30 @@ public final class GtkBackend: AppBackend {
16341634
}
16351635
}
16361636

1637-
public func showSheet(_ sheet: Gtk.Window, window: ApplicationWindow) {
1637+
public func showSheet(_ sheet: Gtk.Window, sheetParent: Any) {
1638+
let window = sheetParent as! Gtk.Window
16381639
sheet.isModal = true
16391640
sheet.isDecorated = false
16401641
sheet.setTransient(for: window)
16411642
sheet.present()
1643+
window.managedAttachedSheet = sheet
16421644
}
16431645

1644-
public func dismissSheet(_ sheet: Gtk.Window, window: ApplicationWindow) {
1646+
public func dismissSheet(_ sheet: Gtk.Window, sheetParent: Any) {
1647+
let window = sheetParent as! Gtk.Window
1648+
1649+
if let nestedSheet = window.managedAttachedSheet {
1650+
dismissSheet(nestedSheet, sheetParent: sheet)
1651+
}
1652+
1653+
defer { window.managedAttachedSheet = nil }
1654+
16451655
let key: OpaquePointer = OpaquePointer(sheet.widgetPointer)
1656+
16461657
if let ctx = sheetContexts[key] {
16471658
ctx.isProgrammaticDismiss = true
16481659
}
1660+
16491661
sheet.destroy()
16501662
sheetContexts.removeValue(forKey: key)
16511663
connectedCloseHandlers.remove(key)

Sources/SwiftCrossUI/Backend/AppBackend.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -627,12 +627,12 @@ public protocol AppBackend: Sendable {
627627
/// app modal, a standalone window, or a modal for a window of its choosing.
628628
func showSheet(
629629
_ sheet: Sheet,
630-
window: Window
630+
sheetParent: Any
631631
)
632632

633633
/// Dismisses a sheet programmatically.
634634
/// Gets used by the ``View/sheet`` modifier to close a sheet.
635-
func dismissSheet(_ sheet: Sheet, window: Window)
635+
func dismissSheet(_ sheet: Sheet, sheetParent: Any)
636636

637637
/// Get the dimensions of a sheet
638638
func size(ofSheet sheet: Sheet) -> SIMD2<Int>
@@ -1281,12 +1281,12 @@ extension AppBackend {
12811281

12821282
public func showSheet(
12831283
_ sheet: Sheet,
1284-
window: Window
1284+
sheetParent: Any
12851285
) {
12861286
todo()
12871287
}
12881288

1289-
public func dismissSheet(_ sheet: Sheet, window: Window) {
1289+
public func dismissSheet(_ sheet: Sheet, sheetParent: Any) {
12901290
todo()
12911291
}
12921292

Sources/SwiftCrossUI/Environment/EnvironmentValues.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ public struct EnvironmentValues {
128128
/// The backend in use. Mustn't change throughout the app's lifecycle.
129129
let backend: any AppBackend
130130

131+
/// The backend's representation of the class that owns the current sheet,
132+
/// if any. This is a very internal detail that should never get
133+
/// exposed to users.
134+
package var sheetParent: Any?
135+
131136
/// Presents an 'Open file' dialog fit for selecting a single file. Some
132137
/// backends only allow selecting either files or directories but not both
133138
/// in a single dialog. Returns `nil` if the user cancels the operation.

Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,11 @@ struct SheetModifier<Content: View, SheetContent: View>: TypeSafeView {
8080
let dismissAction = DismissAction(action: { [isPresented] in
8181
isPresented.wrappedValue = false
8282
})
83-
let sheetEnvironment = environment.with(\.dismiss, dismissAction)
83+
84+
var sheetEnvironment =
85+
environment
86+
.with(\.dismiss, dismissAction)
87+
.with(\.sheetParent, sheet)
8488

8589
let result = children.sheetContentNode!.update(
8690
with: sheetContent(),
@@ -123,13 +127,13 @@ struct SheetModifier<Content: View, SheetContent: View>: TypeSafeView {
123127

124128
backend.showSheet(
125129
sheet,
126-
window: environment.window! as! Backend.Window
130+
sheetParent: (environment.sheetParent ?? environment.window)!
127131
)
128132
children.sheet = sheet
129133
} else if !isPresented.wrappedValue && children.sheet != nil {
130134
backend.dismissSheet(
131135
children.sheet as! Backend.Sheet,
132-
window: environment.window! as! Backend.Window
136+
sheetParent: (environment.sheetParent ?? environment.window)!
133137
)
134138
children.sheet = nil
135139
children.sheetContentNode = nil

Sources/UIKitBackend/UIKitBackend+Sheet.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,18 @@ extension UIKitBackend {
2222

2323
public func showSheet(
2424
_ sheet: CustomSheet,
25-
window: UIWindow
25+
sheetParent: Any
2626
) {
27-
var topController = window.rootViewController
28-
while let presented = topController?.presentedViewController {
29-
topController = presented
27+
var topController: UIViewController? = nil
28+
if let window = sheetParent as? UIWindow {
29+
topController = window.rootViewController
30+
} else {
31+
topController = sheetParent as? UIViewController
3032
}
3133
topController?.present(sheet, animated: true)
3234
}
3335

34-
public func dismissSheet(_ sheet: CustomSheet, window: UIWindow) {
36+
public func dismissSheet(_ sheet: CustomSheet, sheetParent: Any) {
3537
// If this sheet has a presented view controller (nested sheet), dismiss it first
3638
if let presentedVC = sheet.presentedViewController {
3739
presentedVC.dismiss(animated: false) { [weak sheet] in

0 commit comments

Comments
 (0)