Add support for settings
This commit is contained in:
parent
4170728900
commit
448c26debb
@ -18,6 +18,8 @@ public struct Card: WinUIWidget, Wrapper {
|
|||||||
context: WinUIMainView.self
|
context: WinUIMainView.self
|
||||||
)
|
)
|
||||||
var content: Body
|
var content: Body
|
||||||
|
/// Whether it is a settings card.
|
||||||
|
var settingsCard = false
|
||||||
|
|
||||||
/// Initialize the wrapper.
|
/// Initialize the wrapper.
|
||||||
/// - Parameter content: The view content.
|
/// - Parameter content: The view content.
|
||||||
@ -29,7 +31,8 @@ public struct Card: WinUIWidget, Wrapper {
|
|||||||
/// - Returns: The widget.
|
/// - Returns: The widget.
|
||||||
public func initializeWidget() -> Any {
|
public func initializeWidget() -> Any {
|
||||||
let stack = WinUI.Grid()
|
let stack = WinUI.Grid()
|
||||||
stack.cornerRadius = .init(topLeft: 8, topRight: 8, bottomRight: 8, bottomLeft: 8)
|
let radius: Double = settingsCard ? 4 : 8
|
||||||
|
stack.cornerRadius = .init(topLeft: radius, topRight: radius, bottomRight: radius, bottomLeft: radius)
|
||||||
stack.padding = .init(left: 12, top: 12, right: 12, bottom: 12)
|
stack.padding = .init(left: 12, top: 12, right: 12, bottom: 12)
|
||||||
stack.borderThickness = .init(left: 1, top: 1, right: 1, bottom: 1)
|
stack.borderThickness = .init(left: 1, top: 1, right: 1, bottom: 1)
|
||||||
stack.borderBrush = Application.current.resources.lookup("CardStrokeColorDefaultBrush") as? Brush
|
stack.borderBrush = Application.current.resources.lookup("CardStrokeColorDefaultBrush") as? Brush
|
||||||
@ -37,4 +40,10 @@ public struct Card: WinUIWidget, Wrapper {
|
|||||||
return stack
|
return stack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use the settings style.
|
||||||
|
/// - Returns: The card.
|
||||||
|
public func settings() -> Self {
|
||||||
|
modify { $0.settingsCard = true }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
66
Sources/Core/View/Controls/ComboBox.swift
Normal file
66
Sources/Core/View/Controls/ComboBox.swift
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// ToggleSwitch.swift
|
||||||
|
// WinUI
|
||||||
|
//
|
||||||
|
// Created by david-swift on 06.11.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import LevenshteinTransformations
|
||||||
|
import WinUI
|
||||||
|
|
||||||
|
/// The combo box widget.
|
||||||
|
public struct ComboBox<Element>: WinUIWidget where Element: CustomStringConvertible {
|
||||||
|
|
||||||
|
/// The selected element.
|
||||||
|
@BindingProperty(
|
||||||
|
observe: { box, selection, storage in
|
||||||
|
box.selectionChanged.addHandler { _, _ in
|
||||||
|
selection.wrappedValue = (storage.previousState as? Self)?.elements[safe: .init(box.selectedIndex)]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set: { box, value, _ in
|
||||||
|
if let value {
|
||||||
|
box.selectedItem = value.description
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pointer: WinUI.ComboBox.self
|
||||||
|
)
|
||||||
|
var selection: Meta.Binding<Element?> = .constant(nil)
|
||||||
|
/// The elements.
|
||||||
|
@Property(
|
||||||
|
set: { box, newValue, storage in
|
||||||
|
((storage.previousState as? Self)?.elements ?? []).map { $0.description }.transform(
|
||||||
|
to: newValue.map { $0.description },
|
||||||
|
functions: .init { index in
|
||||||
|
box.items.removeAt(.init(index))
|
||||||
|
} insert: { index, element in
|
||||||
|
box.items.insertAt(.init(index), element)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
pointer: WinUI.ComboBox.self
|
||||||
|
)
|
||||||
|
var elements: [Element] = []
|
||||||
|
|
||||||
|
/// Initialize a combo box.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - elements: The elements.
|
||||||
|
/// - selection: The selected element.
|
||||||
|
public init(_ elements: [Element], selection: Meta.Binding<Element>) {
|
||||||
|
self.selection = .init {
|
||||||
|
selection.wrappedValue
|
||||||
|
} set: { newValue in
|
||||||
|
if let newValue {
|
||||||
|
selection.wrappedValue = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.elements = elements
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the widget.
|
||||||
|
/// - Returns: The widget.
|
||||||
|
public func initializeWidget() -> Any {
|
||||||
|
WinUI.ComboBox()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
Sources/Core/View/Controls/ToggleSwitch.swift
Normal file
37
Sources/Core/View/Controls/ToggleSwitch.swift
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// ToggleSwitch.swift
|
||||||
|
// WinUI
|
||||||
|
//
|
||||||
|
// Created by david-swift on 06.11.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import WinUI
|
||||||
|
|
||||||
|
/// The toggle switch widget.
|
||||||
|
public struct ToggleSwitch: WinUIWidget {
|
||||||
|
|
||||||
|
/// Whether the switch is active.
|
||||||
|
@BindingProperty(
|
||||||
|
observe: { view, binding in
|
||||||
|
view.toggled.addHandler { _, _ in
|
||||||
|
binding.wrappedValue = view.isOn
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set: { $0.isOn = $1 },
|
||||||
|
pointer: WinUI.ToggleSwitch.self
|
||||||
|
)
|
||||||
|
var isOn: Meta.Binding<Bool> = .constant(false)
|
||||||
|
|
||||||
|
/// Initialize the toggle switch.
|
||||||
|
/// - Parameter isOn: Whether the switch is active.
|
||||||
|
public init(isOn: Meta.Binding<Bool>) {
|
||||||
|
self.isOn = isOn
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the widget.
|
||||||
|
/// - Returns: The widget.
|
||||||
|
public func initializeWidget() -> Any {
|
||||||
|
WinUI.ToggleSwitch()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
Sources/Core/View/HyperlinkButton.swift
Normal file
42
Sources/Core/View/HyperlinkButton.swift
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// HyperlinkButton.swift
|
||||||
|
// WinUI
|
||||||
|
//
|
||||||
|
// Created by david-swift on 07.11.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import WinUI
|
||||||
|
|
||||||
|
/// A hyperlink as a button.
|
||||||
|
public struct HyperlinkButton: WinUIWidget {
|
||||||
|
|
||||||
|
/// The button's label.
|
||||||
|
@Property(
|
||||||
|
set: { $0.content = $1 },
|
||||||
|
pointer: WinUI.HyperlinkButton.self
|
||||||
|
)
|
||||||
|
var label: String?
|
||||||
|
/// The hyperlink.
|
||||||
|
@Property(
|
||||||
|
set: { $0.navigateUri = .init($1) },
|
||||||
|
pointer: WinUI.HyperlinkButton.self
|
||||||
|
)
|
||||||
|
var url: String?
|
||||||
|
|
||||||
|
/// Initialize the hyperlink button.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - label: The label.
|
||||||
|
/// - url: The hyperlink.
|
||||||
|
public init(_ label: String, url: String) {
|
||||||
|
self.label = label
|
||||||
|
self.url = url
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the widget.
|
||||||
|
/// - Returns:
|
||||||
|
public func initializeWidget() -> Any {
|
||||||
|
WinUI.HyperlinkButton()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,6 +28,8 @@ public struct ModifierWrapper: WinUIWidget, CommandBarWidget {
|
|||||||
var gridColumn: Int?
|
var gridColumn: Int?
|
||||||
/// Whether the element is displayed.
|
/// Whether the element is displayed.
|
||||||
var visible: Bool?
|
var visible: Bool?
|
||||||
|
/// The view's opacity.
|
||||||
|
var opacity: Double?
|
||||||
|
|
||||||
// swiftlint:disable large_tuple
|
// swiftlint:disable large_tuple
|
||||||
/// Initialize the modifier wrapper.
|
/// Initialize the modifier wrapper.
|
||||||
@ -40,6 +42,7 @@ public struct ModifierWrapper: WinUIWidget, CommandBarWidget {
|
|||||||
/// - verticalAlignment: The vertical alignment.
|
/// - verticalAlignment: The vertical alignment.
|
||||||
/// - gridColumn: The grid column.
|
/// - gridColumn: The grid column.
|
||||||
/// - visible: Whether the view is visible.
|
/// - visible: Whether the view is visible.
|
||||||
|
/// - opacity: The view's opacity.
|
||||||
public init(
|
public init(
|
||||||
view: AnyView,
|
view: AnyView,
|
||||||
width: Double? = nil,
|
width: Double? = nil,
|
||||||
@ -48,7 +51,8 @@ public struct ModifierWrapper: WinUIWidget, CommandBarWidget {
|
|||||||
horizontalAlignment: HorizontalAlignment? = nil,
|
horizontalAlignment: HorizontalAlignment? = nil,
|
||||||
verticalAlignment: VerticalAlignment? = nil,
|
verticalAlignment: VerticalAlignment? = nil,
|
||||||
gridColumn: Int? = nil,
|
gridColumn: Int? = nil,
|
||||||
visible: Bool? = nil
|
visible: Bool? = nil,
|
||||||
|
opacity: Double? = nil
|
||||||
) {
|
) {
|
||||||
self.view = view
|
self.view = view
|
||||||
self.width = width
|
self.width = width
|
||||||
@ -58,6 +62,7 @@ public struct ModifierWrapper: WinUIWidget, CommandBarWidget {
|
|||||||
self.verticalAlignment = verticalAlignment
|
self.verticalAlignment = verticalAlignment
|
||||||
self.gridColumn = gridColumn
|
self.gridColumn = gridColumn
|
||||||
self.visible = visible
|
self.visible = visible
|
||||||
|
self.opacity = opacity
|
||||||
}
|
}
|
||||||
// swiftlint:enable large_tuple
|
// swiftlint:enable large_tuple
|
||||||
|
|
||||||
@ -114,6 +119,9 @@ public struct ModifierWrapper: WinUIWidget, CommandBarWidget {
|
|||||||
if let visible, previousState?.visible != visible {
|
if let visible, previousState?.visible != visible {
|
||||||
view.visibility = visible ? .visible : .collapsed
|
view.visibility = visible ? .visible : .collapsed
|
||||||
}
|
}
|
||||||
|
if let opacity, previousState?.opacity != opacity {
|
||||||
|
view.opacity = opacity
|
||||||
|
}
|
||||||
storage.previousState = self
|
storage.previousState = self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
34
Sources/Core/View/ScrollView.swift
Normal file
34
Sources/Core/View/ScrollView.swift
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// ScrollView.swift
|
||||||
|
// WinUI
|
||||||
|
//
|
||||||
|
// Created by david-swift on 06.11.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import WinUI
|
||||||
|
|
||||||
|
/// A scroll view.
|
||||||
|
public struct ScrollView: WinUIWidget {
|
||||||
|
|
||||||
|
/// The view content.
|
||||||
|
@ViewProperty(
|
||||||
|
set: { $0.content = $1 },
|
||||||
|
pointer: WinUI.ScrollView.self,
|
||||||
|
subview: UIElement.self,
|
||||||
|
context: WinUIMainView.self
|
||||||
|
)
|
||||||
|
var content
|
||||||
|
|
||||||
|
/// Initialize a scroll view.
|
||||||
|
/// - Parameter content: The content.
|
||||||
|
public init(@ViewBuilder content: () -> Body) {
|
||||||
|
self.content = content()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the widget.
|
||||||
|
/// - Returns: The widget.
|
||||||
|
public func initializeWidget() -> Any {
|
||||||
|
WinUI.ScrollView()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
62
Sources/Core/View/Settings/SettingsControl.swift
Normal file
62
Sources/Core/View/Settings/SettingsControl.swift
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// SettingsControl.swift
|
||||||
|
// WinUI
|
||||||
|
//
|
||||||
|
// Created by david-swift on 06.11.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// A single settings item.
|
||||||
|
public struct SettingsControl: SimpleView {
|
||||||
|
|
||||||
|
/// The settings item's title.
|
||||||
|
var title: String
|
||||||
|
/// The settings item's description.
|
||||||
|
var description: String?
|
||||||
|
/// The control view.
|
||||||
|
var control: Body
|
||||||
|
|
||||||
|
/// Initialize a settings item.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - title: The settings item's title.
|
||||||
|
/// - description: The settings item's description.
|
||||||
|
/// - control: The control view.
|
||||||
|
public init(_ title: String, description: String? = nil, @ViewBuilder control: () -> Body) {
|
||||||
|
self.title = title
|
||||||
|
self.description = description
|
||||||
|
self.control = control()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The view.
|
||||||
|
public var view: Body {
|
||||||
|
ModifierWrapper(
|
||||||
|
view: Card {
|
||||||
|
ModifierWrapper(
|
||||||
|
view: Grid {
|
||||||
|
ModifierWrapper(
|
||||||
|
view: VStack {
|
||||||
|
Text(title)
|
||||||
|
ModifierWrapper(
|
||||||
|
view: Text(description ?? "")
|
||||||
|
.style(.caption),
|
||||||
|
visible: description != nil,
|
||||||
|
opacity: 0.8
|
||||||
|
)
|
||||||
|
},
|
||||||
|
horizontalAlignment: .stretch,
|
||||||
|
verticalAlignment: .center
|
||||||
|
)
|
||||||
|
ModifierWrapper(
|
||||||
|
view: control,
|
||||||
|
horizontalAlignment: .right
|
||||||
|
)
|
||||||
|
},
|
||||||
|
margin: (5, 5, 5, 5)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.settings(),
|
||||||
|
margin: (2, 2, 2, 2),
|
||||||
|
verticalAlignment: .top
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
Sources/Core/View/Settings/SettingsSection.swift
Normal file
38
Sources/Core/View/Settings/SettingsSection.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// SettingsSection.swift
|
||||||
|
// WinUI
|
||||||
|
//
|
||||||
|
// Created by david-swift on 06.11.24.
|
||||||
|
//
|
||||||
|
|
||||||
|
/// A section in a settings page.
|
||||||
|
public struct SettingsSection: SimpleView {
|
||||||
|
|
||||||
|
/// The section title.
|
||||||
|
var title: String
|
||||||
|
/// The section's content.
|
||||||
|
var content: Body
|
||||||
|
|
||||||
|
/// Initialize the settings section.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - title: The title.
|
||||||
|
/// - content: The content.
|
||||||
|
public init(_ title: String, @ViewBuilder content: () -> Body) {
|
||||||
|
self.title = title
|
||||||
|
self.content = content()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The view.
|
||||||
|
public var view: Body {
|
||||||
|
ModifierWrapper(
|
||||||
|
view: Text(title)
|
||||||
|
.style(.bodyStrong),
|
||||||
|
margin: (5, 5, 5, 5)
|
||||||
|
)
|
||||||
|
ModifierWrapper(
|
||||||
|
view: content,
|
||||||
|
margin: (0, 0, 0, 25)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,18 +30,15 @@ struct Demo: App {
|
|||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
|
|
||||||
@State private var selectedItem: NavigationItem = .shop
|
@State private var selectedItem: NavigationSelection<NavigationItem> = .custom(item: .shop)
|
||||||
|
@State private var comboBoxSelection: NavigationItem = .items
|
||||||
|
@State private var switchState = false
|
||||||
var app: WinUIApp
|
var app: WinUIApp
|
||||||
|
|
||||||
var view: Body {
|
var view: Body {
|
||||||
NavigationView(items: NavigationItem.allCases, selection: $selectedItem) {
|
NavigationView(items: NavigationItem.allCases, selection: $selectedItem) {
|
||||||
if selectedItem != .shop {
|
switch selectedItem {
|
||||||
Button("\(selectedItem)") {
|
case .custom(item: .shop):
|
||||||
selectedItem = .shop
|
|
||||||
}
|
|
||||||
.horizontalAlignment(.center)
|
|
||||||
.verticalAlignment(.center)
|
|
||||||
} else {
|
|
||||||
FlipView(NavigationItem.allCases) { element in
|
FlipView(NavigationItem.allCases) { element in
|
||||||
Text(element.description)
|
Text(element.description)
|
||||||
.style(.bodyStrong)
|
.style(.bodyStrong)
|
||||||
@ -57,13 +54,42 @@ struct ContentView: View {
|
|||||||
.margin(50)
|
.margin(50)
|
||||||
.verticalAlignment(.center)
|
.verticalAlignment(.center)
|
||||||
.verticalAlignment(.center)
|
.verticalAlignment(.center)
|
||||||
|
case .settings:
|
||||||
|
ScrollView {
|
||||||
|
VStack {
|
||||||
|
SettingsSection("General") {
|
||||||
|
SettingsControl("Button Control") {
|
||||||
|
Button("Control") { }
|
||||||
|
}
|
||||||
|
SettingsControl("Combo Box", description: "Selected item: \(comboBoxSelection)") {
|
||||||
|
ComboBox(NavigationItem.allCases, selection: $comboBoxSelection)
|
||||||
|
}
|
||||||
|
SettingsControl("Toggle Switch", description: "Current state: \(switchState ? "on" : "off")") {
|
||||||
|
ToggleSwitch(isOn: $switchState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SettingsSection("About") {
|
||||||
|
SettingsControl("WinUI for Swift Demo", description: "main") {
|
||||||
|
HyperlinkButton("Website", url: "https://aparoksha.dev/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.margin(50, .horizontal)
|
||||||
|
.margin(20, .vertical)
|
||||||
|
}
|
||||||
|
case let .custom(item: item):
|
||||||
|
Button("\(item)") {
|
||||||
|
selectedItem = .custom(item: .shop)
|
||||||
|
}
|
||||||
|
.horizontalAlignment(.center)
|
||||||
|
.verticalAlignment(.center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.header(settingsLabel: "Settings") {
|
.header(settingsLabel: "Settings") {
|
||||||
AppBarButton("Delete", icon: .systemIcon(unicode: "\u{E74D}")) {
|
AppBarButton("Delete", icon: .systemIcon(unicode: "\u{E74D}")) {
|
||||||
print("Delete")
|
print("Delete")
|
||||||
}
|
}
|
||||||
.visible(selectedItem != .shop)
|
.visible(selectedItem != .settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ extension AnyView {
|
|||||||
/// - edges: The affected edges.
|
/// - edges: The affected edges.
|
||||||
/// - Returns: The view.
|
/// - Returns: The view.
|
||||||
public func margin(_ value: Double, _ edges: [Edge] = .all) -> AnyView {
|
public func margin(_ value: Double, _ edges: [Edge] = .all) -> AnyView {
|
||||||
ModifierWrapper(view: self, margin: edges.set(value))
|
ModifierWrapper(view: Grid { self }, margin: edges.set(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the view's horizontal alignment.
|
/// Set the view's horizontal alignment.
|
||||||
@ -60,6 +60,13 @@ extension AnyView {
|
|||||||
ModifierWrapper(view: self, visible: visible)
|
ModifierWrapper(view: self, visible: visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the view's opacity.
|
||||||
|
/// - Parameter opacity: The opacity.
|
||||||
|
/// - Returns: The view.
|
||||||
|
public func opacity(_ opacity: Double?) -> AnyView {
|
||||||
|
ModifierWrapper(view: self, opacity: opacity)
|
||||||
|
}
|
||||||
|
|
||||||
/// Make the view match the card style.
|
/// Make the view match the card style.
|
||||||
/// - Returns: The card.
|
/// - Returns: The card.
|
||||||
public func card() -> AnyView {
|
public func card() -> AnyView {
|
||||||
|
Loading…
Reference in New Issue
Block a user