Add support for content dialogs
This commit is contained in:
parent
9f545190a1
commit
821ba1f006
164
Sources/Core/View/ContentDialog.swift
Normal file
164
Sources/Core/View/ContentDialog.swift
Normal file
@ -0,0 +1,164 @@
|
||||
//
|
||||
// AlertDialog.swift
|
||||
// Adwaita
|
||||
//
|
||||
// Created by david-swift on 12.11.24.
|
||||
//
|
||||
|
||||
import WinUI
|
||||
|
||||
/// The content dialog widget.
|
||||
public struct ContentDialog: WinUIWidget {
|
||||
|
||||
/// Whether the dialog is visible.
|
||||
@Meta.Binding var visible: Bool
|
||||
/// An identifier used if multiple dialogs are on one view.
|
||||
var id: String
|
||||
/// The dialog's title.
|
||||
var title: String
|
||||
/// The body text.
|
||||
var body: Body
|
||||
/// The primary response.
|
||||
var primary: (String, () -> Void)?
|
||||
/// The secondary response.
|
||||
var secondary: (String, () -> Void)?
|
||||
/// The close response.
|
||||
var close: (String, () -> Void)?
|
||||
/// The child view.
|
||||
var child: AnyView
|
||||
|
||||
/// Initialize a content dialog wrapper.
|
||||
/// - Parameters:
|
||||
/// - visible: Whether the dialog is visible.
|
||||
/// - child: The child view.
|
||||
/// - id: A unique identifier for dialogs on the view.
|
||||
/// - title: The title.
|
||||
/// - body: The body.
|
||||
public init(
|
||||
visible: Meta.Binding<Bool>,
|
||||
child: AnyView,
|
||||
id: String,
|
||||
title: String,
|
||||
@ViewBuilder body: () -> Body = { [] }
|
||||
) {
|
||||
self._visible = visible
|
||||
self.child = child
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.body = body()
|
||||
}
|
||||
|
||||
/// The view storage.
|
||||
/// - Parameters:
|
||||
/// - modifiers: Modify views before being updated.
|
||||
/// - type: The view render data type.
|
||||
/// - Returns: The view storage.
|
||||
public func container<Data>(
|
||||
data: WidgetData,
|
||||
type: Data.Type
|
||||
) -> ViewStorage where Data: ViewRenderData {
|
||||
let storage = child.storage(data: data, type: type)
|
||||
let dialog = WinUI.ContentDialog()
|
||||
storage.fields["dialog-object"] = dialog
|
||||
let body = body.storage(data: data, type: type)
|
||||
dialog.content = body.pointer
|
||||
storage.content["body"] = [body]
|
||||
dialog.closeButtonClick.addHandler { _, _ in
|
||||
(storage.fields["close-action"] as? () -> Void)?()
|
||||
}
|
||||
dialog.primaryButtonClick.addHandler { _, _ in
|
||||
(storage.fields["primary-action"] as? () -> Void)?()
|
||||
}
|
||||
dialog.secondaryButtonClick.addHandler { _, _ in
|
||||
(storage.fields["secondary-action"] as? () -> Void)?()
|
||||
}
|
||||
update(storage, data: data, updateProperties: true, type: type)
|
||||
return storage
|
||||
}
|
||||
|
||||
/// Update the stored content.
|
||||
/// - Parameters:
|
||||
/// - storage: The storage to update.
|
||||
/// - modifiers: Modify views before being updated
|
||||
/// - updateProperties: Whether to update the view's properties.
|
||||
/// - type: The view render data type.
|
||||
public func update<Data>(
|
||||
_ storage: ViewStorage,
|
||||
data: WidgetData,
|
||||
updateProperties: Bool,
|
||||
type: Data.Type
|
||||
) where Data: ViewRenderData {
|
||||
child.updateStorage(storage, data: data, updateProperties: updateProperties, type: type)
|
||||
if let body = storage.content["body"]?.first {
|
||||
self.body.updateStorage(body, data: data, updateProperties: updateProperties, type: type)
|
||||
}
|
||||
guard updateProperties, let dialog = storage.fields["dialog-object"] as? WinUI.ContentDialog else {
|
||||
return
|
||||
}
|
||||
if storage.fields["dialog-presented"] as? Bool == true {
|
||||
if !visible {
|
||||
try? dialog.hide()
|
||||
}
|
||||
updateDialogProperties(dialog: dialog, storage: storage)
|
||||
storage.fields["dialog-presented"] = visible
|
||||
} else if visible {
|
||||
dialog.xamlRoot = (storage.pointer as? UIElement)?.xamlRoot
|
||||
updateDialogProperties(dialog: dialog, storage: storage)
|
||||
_ = try? dialog.showAsync()
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the dialog's properties.
|
||||
/// - Parameters:
|
||||
/// - dialog: The dialog object.
|
||||
/// - storage: The view storage.
|
||||
func updateDialogProperties(dialog: WinUI.ContentDialog, storage: ViewStorage) {
|
||||
dialog.title = title
|
||||
if let close {
|
||||
dialog.closeButtonText = close.0
|
||||
storage.fields["close-action"] = close.1
|
||||
}
|
||||
if let primary {
|
||||
dialog.primaryButtonText = primary.0
|
||||
storage.fields["primary-action"] = primary.1
|
||||
}
|
||||
if let secondary {
|
||||
dialog.secondaryButtonText = secondary.0
|
||||
storage.fields["secondary-action"] = secondary.1
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the close response.
|
||||
/// - Parameters:
|
||||
/// - title: The response.
|
||||
/// - action: The action.
|
||||
public func closeResponse(
|
||||
_ title: String,
|
||||
action: @escaping () -> Void
|
||||
) -> Self {
|
||||
modify { $0.close = (title, action) }
|
||||
}
|
||||
|
||||
/// Set the primary response.
|
||||
/// - Parameters:
|
||||
/// - title: The response.
|
||||
/// - action: The action.
|
||||
public func primaryResponse(
|
||||
_ title: String,
|
||||
action: @escaping () -> Void
|
||||
) -> Self {
|
||||
modify { $0.primary = (title, action) }
|
||||
}
|
||||
|
||||
/// Set the secondary response.
|
||||
/// - Parameters:
|
||||
/// - title: The response.
|
||||
/// - action: The action.
|
||||
public func secondaryResponse(
|
||||
_ title: String,
|
||||
action: @escaping () -> Void
|
||||
) -> Self {
|
||||
modify { $0.secondary = (title, action) }
|
||||
}
|
||||
|
||||
}
|
@ -33,6 +33,7 @@ struct ContentView: View {
|
||||
@State private var path: [NavigationSelection<NavigationItem>] = []
|
||||
@State private var comboBoxSelection: NavigationItem = .items
|
||||
@State private var switchState = false
|
||||
@State private var dialog = false
|
||||
var app: WinUIApp
|
||||
|
||||
var selectedItem: NavigationSelection<NavigationItem>? { path.last }
|
||||
@ -70,10 +71,13 @@ struct ContentView: View {
|
||||
}
|
||||
.header(settingsLabel: "Settings") {
|
||||
AppBarButton("Delete", icon: .systemIcon(unicode: "\u{E74D}")) {
|
||||
print("Delete")
|
||||
dialog = true
|
||||
}
|
||||
.visible(selectedItem != .settings)
|
||||
}
|
||||
.contentDialog(visible: $dialog, title: "Sample Dialog")
|
||||
.closeResponse("Close") { dialog = false }
|
||||
.primaryResponse("Delete") { dialog = false }
|
||||
}
|
||||
|
||||
/// The settings page.
|
||||
|
@ -73,4 +73,20 @@ extension AnyView {
|
||||
Card { self }
|
||||
}
|
||||
|
||||
/// Add a content dialog.
|
||||
/// - Parameters:
|
||||
/// - visible: The dialog's visibility.
|
||||
/// - title: The dialog's title.
|
||||
/// - id: An optional id. Use this for multiple dialogs on one view.
|
||||
/// - body: The dialog's content.
|
||||
/// - Returns: The view.
|
||||
public func contentDialog(
|
||||
visible: Binding<Bool>,
|
||||
title: String,
|
||||
id: String = "",
|
||||
@ViewBuilder body: () -> Body = { [] }
|
||||
) -> ContentDialog {
|
||||
.init(visible: visible, child: self, id: id, title: title, body: body)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user