Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 61 additions & 55 deletions Examples/Sources/ControlsExample/ControlsApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,75 +22,81 @@ struct ControlsApp: App {
var body: some Scene {
WindowGroup("ControlsApp") {
#hotReloadable {
VStack(spacing: 30) {
VStack {
Text("Button")
Button("Click me!") {
count += 1
}
Text("Count: \(count)")
}
.padding(.bottom, 20)

#if !canImport(UIKitBackend)
ScrollView {
VStack(spacing: 30) {
VStack {
Text("Toggle button")
Toggle("Toggle me!", active: $exampleButtonState)
.toggleStyle(.button)
Text("Currently enabled: \(exampleButtonState)")
Text("Button")
Button("Click me!") {
count += 1
}
Text("Count: \(count)")
}
.padding(.bottom, 20)
#endif

VStack {
Text("Toggle switch")
Toggle("Toggle me:", active: $exampleSwitchState)
.toggleStyle(.switch)
Text("Currently enabled: \(exampleSwitchState)")
}
#if !canImport(UIKitBackend)
VStack {
Text("Toggle button")
Toggle("Toggle me!", active: $exampleButtonState)
.toggleStyle(.button)
Text("Currently enabled: \(exampleButtonState)")
}
.padding(.bottom, 20)
#endif

#if !canImport(UIKitBackend)
VStack {
Text("Checkbox")
Toggle("Toggle me:", active: $exampleCheckboxState)
.toggleStyle(.checkbox)
Text("Currently enabled: \(exampleCheckboxState)")
Text("Toggle switch")
Toggle("Toggle me:", active: $exampleSwitchState)
.toggleStyle(.switch)
Text("Currently enabled: \(exampleSwitchState)")
}
#endif

VStack {
Text("Slider")
Slider($sliderValue, minimum: 0, maximum: 10)
.frame(maxWidth: 200)
Text("Value: \(String(format: "%.02f", sliderValue))")
}
#if !canImport(UIKitBackend)
VStack {
Text("Checkbox")
Toggle("Toggle me:", active: $exampleCheckboxState)
.toggleStyle(.checkbox)
Text("Currently enabled: \(exampleCheckboxState)")
}
#endif

VStack {
Text("Slider")
Slider($sliderValue, minimum: 0, maximum: 10)
.frame(maxWidth: 200)
Text("Value: \(String(format: "%.02f", sliderValue))")
}

VStack {
Text("Text field")
TextField("Text field", text: $text)
Text("Value: \(text)")
}
VStack {
Text("Text field")
TextField("Text field", text: $text)
Text("Value: \(text)")
}

Toggle("Enable ProgressView resizability", active: $isProgressViewResizable)
Slider($progressViewSize, minimum: 10, maximum: 100)
ProgressView()
.resizable(isProgressViewResizable)
.frame(width: progressViewSize, height: progressViewSize)
Toggle("Enable ProgressView resizability", active: $isProgressViewResizable)
Slider($progressViewSize, minimum: 10, maximum: 100)
Button("Randomize progress view size") {
progressViewSize = Int.random(in: 10...100)
}
ProgressView()
.resizable(isProgressViewResizable)
.frame(width: progressViewSize, height: progressViewSize)

VStack {
Text("Drop down")
HStack {
Text("Flavor: ")
Picker(of: ["Vanilla", "Chocolate", "Strawberry"], selection: $flavor)
VStack {
Text("Drop down")
HStack {
Text("Flavor: ")
Picker(
of: ["Vanilla", "Chocolate", "Strawberry"], selection: $flavor)
}
Text("You chose: \(flavor ?? "Nothing yet!")")
}
Text("You chose: \(flavor ?? "Nothing yet!")")
}
}.padding().disabled(!enabled)
}.padding().disabled(!enabled)

Toggle(enabled ? "Disable all" : "Enable all", active: $enabled)
.padding()
Toggle(enabled ? "Disable all" : "Enable all", active: $enabled)
.padding()
}
.frame(minHeight: 600)
}

}.defaultSize(width: 400, height: 600)
}
}
32 changes: 31 additions & 1 deletion Sources/AppKitBackend/AppKitBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,15 @@ public final class AppKitBackend: AppBackend {
}

public func naturalSize(of widget: Widget) -> SIMD2<Int> {
if let spinner = widget.subviews.first as? NSProgressIndicator,
spinner.style == .spinning
{
let size = spinner.intrinsicContentSize
return SIMD2(
Int(size.width),
Int(size.height)
)
}
let size = widget.intrinsicContentSize
return SIMD2(
Int(size.width),
Expand Down Expand Up @@ -1181,11 +1190,32 @@ public final class AppKitBackend: AppBackend {
}

public func createProgressSpinner() -> Widget {
let container = NSView()
let spinner = NSProgressIndicator()
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.isIndeterminate = true
spinner.style = .spinning
spinner.startAnimation(nil)
container.addSubview(spinner)
return container
}

public func setProgressSpinnerSize(
_ widget: Widget,
_ size: SIMD2<Int>
) {
guard Int(widget.frame.size.height) != size.y else { return }
setSize(of: widget, to: size)
let spinner = NSProgressIndicator()
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.isIndeterminate = true
spinner.style = .spinning
spinner.startAnimation(nil)
return spinner
spinner.widthAnchor.constraint(equalToConstant: CGFloat(size.x)).isActive = true
spinner.heightAnchor.constraint(equalToConstant: CGFloat(size.y)).isActive = true

widget.subviews = []
widget.addSubview(spinner)
}

public func createProgressBar() -> Widget {
Expand Down
16 changes: 16 additions & 0 deletions Sources/SwiftCrossUI/Backend/AppBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,15 @@ public protocol AppBackend: Sendable {
/// Creates an indeterminate progress spinner.
func createProgressSpinner() -> Widget

/// Changes the Spinner's Size.
/// Required due to AppKitBackend needing special treatment.
/// Forward to ``AppBackend/setSize(of widget: Widget, to size: SIMD2<Int>)``
/// on other Backends.
func setProgressSpinnerSize(
_ widget: Widget,
_ size: SIMD2<Int>
)

/// Creates a progress bar.
func createProgressBar() -> Widget
/// Updates a progress bar to reflect the given progress (between 0 and 1), and the
Expand Down Expand Up @@ -1028,6 +1037,13 @@ extension AppBackend {
todo()
}

public func setProgressSpinnerSize(
_ widget: Widget,
_ size: SIMD2<Int>
) {
setSize(of: widget, to: size)
}

public func createProgressBar() -> Widget {
todo()
}
Expand Down
17 changes: 9 additions & 8 deletions Sources/SwiftCrossUI/Views/ProgressView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public struct ProgressView<Label: View>: View {
}

/// Makes the ProgressView resize to fit the available space.
/// Only affects `Kind.spinner`.
/// Only affects ``Kind/spinner``.
public func resizable(_ isResizable: Bool = true) -> Self {
var progressView = self
progressView.isSpinnerResizable = isResizable
Expand Down Expand Up @@ -111,6 +111,7 @@ extension ProgressView where Label == Text {

struct ProgressSpinnerView: ElementaryView {
let isResizable: Bool

init(isResizable: Bool = false) {
self.isResizable = isResizable
}
Expand All @@ -130,26 +131,26 @@ struct ProgressSpinnerView: ElementaryView {
guard isResizable else {
// Required to reset its size when resizability
// gets changed at runtime
backend.setSize(of: widget, to: naturalSize)
backend.setProgressSpinnerSize(widget, naturalSize)
return ViewUpdateResult.leafView(size: ViewSize(fixedSize: naturalSize))
}
let min = max(min(proposedSize.x, proposedSize.y), 10)
let minimumDimension = max(min(proposedSize.x, proposedSize.y), 0)
let size = SIMD2(
min,
min
minimumDimension,
minimumDimension
)
if !dryRun {
// Doesn't change the rendered size of ProgressSpinner
// on UIKitBackend, but still sets container size to
// (width: n, height: n) n = min(proposedSize.x, proposedSize.y)
backend.setSize(of: widget, to: size)
backend.setProgressSpinnerSize(widget, size)
}
return ViewUpdateResult.leafView(
size: ViewSize(
size: size,
idealSize: naturalSize,
minimumWidth: 10,
minimumHeight: 10,
minimumWidth: 0,
minimumHeight: 0,
maximumWidth: nil,
maximumHeight: nil
)
Expand Down
Loading