Skip to content

Commit f5df8b0

Browse files
committed
Implement WinUIBackend alert support
1 parent 36c3e56 commit f5df8b0

File tree

1 file changed

+71
-8
lines changed

1 file changed

+71
-8
lines changed

Sources/WinUIBackend/WinUIBackend.swift

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ class WinUIApplication: SwiftApplication {
2626
}
2727
}
2828

29-
public struct WinUIBackend: AppBackend {
29+
public final class WinUIBackend: AppBackend {
3030
public typealias Window = WinUI.Window
3131
public typealias Widget = WinUI.FrameworkElement
3232
public typealias Menu = Void
33-
public typealias Alert = Void
33+
public typealias Alert = WinUI.ContentDialog
3434

3535
public let defaultTableRowContentHeight = 20
3636
public let defaultTableCellVerticalPadding = 4
@@ -53,6 +53,11 @@ public struct WinUIBackend: AppBackend {
5353
}
5454

5555
private var internalState: InternalState
56+
/// WinUI only allows one dialog at a time (subsequent dialogs throw
57+
/// exceptions), so we limit ourselves.
58+
private var dialogSemaphore = DispatchSemaphore(value: 1)
59+
60+
private var windows: [Window] = []
5661

5762
public init() {
5863
internalState = InternalState()
@@ -82,6 +87,12 @@ public struct WinUIBackend: AppBackend {
8287

8388
public func createWindow(withDefaultSize size: SIMD2<Int>?) -> Window {
8489
let window = Window()
90+
windows.append(window)
91+
window.closed.addHandler { _, _ in
92+
self.windows.removeAll { other in
93+
window === other
94+
}
95+
}
8596

8697
if internalState.dispatcherQueue == nil {
8798
internalState.dispatcherQueue = window.dispatcherQueue
@@ -208,12 +219,6 @@ public struct WinUIBackend: AppBackend {
208219
.with(\.font, .system(size: 14))
209220
}
210221

211-
private func fatalError(_ message: String) -> Never {
212-
print(message)
213-
fflush(stdout)
214-
Foundation.exit(1)
215-
}
216-
217222
public func setRootEnvironmentChangeHandler(to action: @escaping () -> Void) {
218223
print("Implement set root environment change handler")
219224
// TODO
@@ -760,6 +765,64 @@ public struct WinUIBackend: AppBackend {
760765
}
761766
}
762767

768+
public func createAlert() -> Alert {
769+
ContentDialog()
770+
}
771+
772+
public func updateAlert(
773+
_ alert: Alert,
774+
title: String,
775+
actionLabels: [String],
776+
environment: EnvironmentValues
777+
) {
778+
alert.title = title
779+
if actionLabels.count >= 1 {
780+
alert.primaryButtonText = actionLabels[0]
781+
}
782+
if actionLabels.count >= 2 {
783+
alert.secondaryButtonText = actionLabels[1]
784+
}
785+
if actionLabels.count >= 3 {
786+
alert.closeButtonText = actionLabels[2]
787+
}
788+
}
789+
790+
public func showAlert(
791+
_ alert: Alert,
792+
window: Window?,
793+
responseHandler handleResponse: @escaping (Int) -> Void
794+
) {
795+
// WinUI only allows one dialog at a time so we limit ourselves using
796+
// a semaphore.
797+
guard let window = window ?? windows.first else {
798+
print("warning: WinUI can't show alert without window")
799+
return
800+
}
801+
802+
alert.xamlRoot = window.content.xamlRoot
803+
dialogSemaphore.wait()
804+
let promise = try! alert.showAsync()!
805+
promise.completed = { operation, status in
806+
self.dialogSemaphore.signal()
807+
guard
808+
status == .completed,
809+
let operation,
810+
let result = try? operation.getResults()
811+
else {
812+
return
813+
}
814+
let index =
815+
switch result {
816+
case .primary: 0
817+
case .secondary: 1
818+
case .none: 2
819+
default:
820+
fatalError("WinUIBackend: Invalid dialog response")
821+
}
822+
handleResponse(index)
823+
}
824+
}
825+
763826
// public func createTable(rows: Int, columns: Int) -> Widget {
764827
// let grid = Grid()
765828
// grid.columnSpacing = 10

0 commit comments

Comments
 (0)