Skip to content

Commit b8496c0

Browse files
committed
Implement disabled() modifier for disabling controls (all backends, all controls)
1 parent 7162757 commit b8496c0

File tree

25 files changed

+340
-128
lines changed

25 files changed

+340
-128
lines changed

Examples/Sources/ControlsExample/ControlsApp.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ struct ControlsApp: App {
1212
@State var exampleButtonState = false
1313
@State var exampleSwitchState = false
1414
@State var sliderValue = 5.0
15+
@State var text = ""
16+
@State var flavor: String? = nil
17+
@State var enabled = true
1518

1619
var body: some Scene {
1720
WindowGroup("ControlsApp") {
1821
#hotReloadable {
19-
VStack {
22+
VStack(spacing: 30) {
2023
VStack {
2124
Text("Button")
2225
Button("Click me!") {
@@ -47,8 +50,26 @@ struct ControlsApp: App {
4750
.frame(maxWidth: 200)
4851
Text("Value: \(String(format: "%.02f", sliderValue))")
4952
}
50-
}
53+
54+
VStack {
55+
Text("Text field")
56+
TextField("Text field", text: $text)
57+
Text("Value: \(text)")
58+
}
59+
60+
VStack {
61+
Text("Drop down")
62+
HStack {
63+
Text("Flavor: ")
64+
Picker(of: ["Vanilla", "Chocolate", "Strawberry"], selection: $flavor)
65+
}
66+
Text("You chose: \(flavor ?? "Nothing yet!")")
67+
}
68+
}.padding().disabled(!enabled)
69+
70+
Toggle(enabled ? "Disable all" : "Enable all", active: $enabled)
71+
.padding()
5172
}
52-
}
73+
}.defaultSize(width: 400, height: 600)
5374
}
5475
}

Sources/AppKitBackend/AppKitBackend.swift

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,8 @@ public final class AppKitBackend: AppBackend {
509509
public func updateButton(
510510
_ button: Widget,
511511
label: String,
512-
action: @escaping () -> Void,
513-
environment: EnvironmentValues
512+
environment: EnvironmentValues,
513+
action: @escaping () -> Void
514514
) {
515515
let button = button as! NSButton
516516
button.attributedTitle = Self.attributedString(
@@ -519,6 +519,7 @@ public final class AppKitBackend: AppBackend {
519519
)
520520
button.bezelStyle = .regularSquare
521521
button.appearance = environment.colorScheme.nsAppearance
522+
button.isEnabled = environment.isEnabled
522523
button.onAction = { _ in
523524
action()
524525
}
@@ -528,8 +529,13 @@ public final class AppKitBackend: AppBackend {
528529
return NSSwitch()
529530
}
530531

531-
public func updateSwitch(_ toggleSwitch: Widget, onChange: @escaping (Bool) -> Void) {
532+
public func updateSwitch(
533+
_ toggleSwitch: Widget,
534+
environment: EnvironmentValues,
535+
onChange: @escaping (Bool) -> Void
536+
) {
532537
let toggleSwitch = toggleSwitch as! NSSwitch
538+
toggleSwitch.isEnabled = environment.isEnabled
533539
toggleSwitch.onAction = { toggleSwitch in
534540
let toggleSwitch = toggleSwitch as! NSSwitch
535541
onChange(toggleSwitch.state == .on)
@@ -547,8 +553,14 @@ public final class AppKitBackend: AppBackend {
547553
return toggle
548554
}
549555

550-
public func updateToggle(_ toggle: Widget, label: String, onChange: @escaping (Bool) -> Void) {
556+
public func updateToggle(
557+
_ toggle: Widget,
558+
label: String,
559+
environment: EnvironmentValues,
560+
onChange: @escaping (Bool) -> Void
561+
) {
551562
let toggle = toggle as! NSButton
563+
toggle.isEnabled = environment.isEnabled
552564
toggle.title = label
553565
toggle.onAction = { toggle in
554566
let toggle = toggle as! NSButton
@@ -570,6 +582,7 @@ public final class AppKitBackend: AppBackend {
570582
minimum: Double,
571583
maximum: Double,
572584
decimalPlaces: Int,
585+
environment: EnvironmentValues,
573586
onChange: @escaping (Double) -> Void
574587
) {
575588
// TODO: Implement decimalPlaces
@@ -580,6 +593,7 @@ public final class AppKitBackend: AppBackend {
580593
let slider = slider as! NSSlider
581594
onChange(slider.doubleValue)
582595
}
596+
slider.isEnabled = environment.isEnabled
583597
}
584598

585599
public func setValue(ofSlider slider: Widget, to value: Double) {
@@ -598,6 +612,7 @@ public final class AppKitBackend: AppBackend {
598612
onChange: @escaping (Int?) -> Void
599613
) {
600614
let picker = picker as! NSPopUpButton
615+
picker.isEnabled = environment.isEnabled
601616
picker.menu?.removeAllItems()
602617
for option in options {
603618
let item = NSMenuItem()
@@ -636,6 +651,7 @@ public final class AppKitBackend: AppBackend {
636651
onSubmit: @escaping () -> Void
637652
) {
638653
let textField = textField as! NSObservableTextField
654+
textField.isEnabled = environment.isEnabled
639655
textField.placeholderString = placeholder
640656
textField.appearance = environment.colorScheme.nsAppearance
641657
textField.onEdit = { textField in
@@ -837,7 +853,8 @@ public final class AppKitBackend: AppBackend {
837853
height: Int,
838854
targetWidth: Int,
839855
targetHeight: Int,
840-
dataHasChanged: Bool
856+
dataHasChanged: Bool,
857+
environment: EnvironmentValues
841858
) {
842859
guard dataHasChanged else {
843860
return
@@ -1187,10 +1204,6 @@ public final class AppKitBackend: AppBackend {
11871204
}
11881205

11891206
public func createTapGestureTarget(wrapping child: Widget, gesture _: TapGesture) -> Widget {
1190-
if child.subviews.count >= 2 && child.subviews[1] is NSCustomTapGestureTarget {
1191-
return child
1192-
}
1193-
11941207
let container = NSView()
11951208

11961209
container.addSubview(child)
@@ -1218,15 +1231,26 @@ public final class AppKitBackend: AppBackend {
12181231
public func updateTapGestureTarget(
12191232
_ container: Widget,
12201233
gesture: TapGesture,
1234+
environment: EnvironmentValues,
12211235
action: @escaping () -> Void
12221236
) {
12231237
let tapGestureTarget = container.subviews[1] as! NSCustomTapGestureTarget
1224-
switch gesture.kind {
1225-
case .primary:
1238+
switch (gesture.kind, environment.isEnabled) {
1239+
case (_, false):
1240+
tapGestureTarget.leftClickHandler = nil
1241+
tapGestureTarget.rightClickHandler = nil
1242+
tapGestureTarget.longPressHandler = nil
1243+
case (.primary, true):
12261244
tapGestureTarget.leftClickHandler = action
1227-
case .secondary:
1245+
tapGestureTarget.rightClickHandler = nil
1246+
tapGestureTarget.longPressHandler = nil
1247+
case (.secondary, true):
1248+
tapGestureTarget.leftClickHandler = nil
12281249
tapGestureTarget.rightClickHandler = action
1229-
case .longPress:
1250+
tapGestureTarget.longPressHandler = nil
1251+
case (.longPress, true):
1252+
tapGestureTarget.leftClickHandler = nil
1253+
tapGestureTarget.rightClickHandler = nil
12301254
tapGestureTarget.longPressHandler = action
12311255
}
12321256
}
@@ -1276,7 +1300,12 @@ public final class AppKitBackend: AppBackend {
12761300
}
12771301
}
12781302

1279-
public func updatePath(_ path: Path, _ source: SwiftCrossUI.Path, pointsChanged: Bool) {
1303+
public func updatePath(
1304+
_ path: Path,
1305+
_ source: SwiftCrossUI.Path,
1306+
pointsChanged: Bool,
1307+
environment: EnvironmentValues
1308+
) {
12801309
applyStrokeStyle(source.strokeStyle, to: path)
12811310

12821311
if pointsChanged {
@@ -1412,9 +1441,13 @@ final class NSCustomTapGestureTarget: NSView {
14121441
target: self, action: #selector(leftClick))
14131442
addGestureRecognizer(gestureRecognizer)
14141443
leftClickRecognizer = gestureRecognizer
1444+
} else if leftClickHandler == nil, let leftClickRecognizer {
1445+
removeGestureRecognizer(leftClickRecognizer)
1446+
self.leftClickHandler = nil
14151447
}
14161448
}
14171449
}
1450+
14181451
var rightClickHandler: (() -> Void)? {
14191452
didSet {
14201453
if rightClickHandler != nil && rightClickRecognizer == nil {
@@ -1423,9 +1456,13 @@ final class NSCustomTapGestureTarget: NSView {
14231456
gestureRecognizer.buttonMask = 1 << 1
14241457
addGestureRecognizer(gestureRecognizer)
14251458
rightClickRecognizer = gestureRecognizer
1459+
} else if rightClickHandler == nil, let rightClickRecognizer {
1460+
removeGestureRecognizer(rightClickRecognizer)
1461+
self.rightClickHandler = nil
14261462
}
14271463
}
14281464
}
1465+
14291466
var longPressHandler: (() -> Void)? {
14301467
didSet {
14311468
if longPressHandler != nil && longPressRecognizer == nil {
@@ -1435,6 +1472,9 @@ final class NSCustomTapGestureTarget: NSView {
14351472
gestureRecognizer.minimumPressDuration = 0.5
14361473
addGestureRecognizer(gestureRecognizer)
14371474
longPressRecognizer = gestureRecognizer
1475+
} else if longPressHandler == nil, let longPressRecognizer {
1476+
removeGestureRecognizer(longPressRecognizer)
1477+
self.longPressRecognizer = nil
14381478
}
14391479
}
14401480
}

Sources/Gtk/Widgets/Widget.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ open class Widget: GObject {
9898

9999
@GObjectProperty(named: "name") public var name: String?
100100

101+
@GObjectProperty(named: "sensitive") public var sensitive: Bool
102+
101103
@GObjectProperty(named: "opacity") public var opacity: Double
102104

103105
@GObjectProperty(named: "margin-top") public var marginTop: Int

Sources/Gtk3/Widgets/Widget.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ open class Widget: GObject {
114114

115115
@GObjectProperty(named: "name") public var name: String?
116116

117+
@GObjectProperty(named: "sensitive") public var sensitive: Bool
118+
117119
@GObjectProperty(named: "opacity") public var opacity: Double
118120

119121
@GObjectProperty(named: "margin-top") public var marginTop: Int

Sources/Gtk3Backend/Gtk3Backend.swift

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,8 @@ public final class Gtk3Backend: AppBackend {
651651
height: Int,
652652
targetWidth: Int,
653653
targetHeight: Int,
654-
dataHasChanged: Bool
654+
dataHasChanged: Bool,
655+
environment: EnvironmentValues
655656
) {
656657
let imageView = imageView as! Gtk3.Image
657658

@@ -750,11 +751,12 @@ public final class Gtk3Backend: AppBackend {
750751
public func updateButton(
751752
_ button: Widget,
752753
label: String,
753-
action: @escaping () -> Void,
754-
environment: EnvironmentValues
754+
environment: EnvironmentValues,
755+
action: @escaping () -> Void
755756
) {
756757
// TODO: Update button label color using environment
757758
let button = button as! Gtk3.Button
759+
button.sensitive = environment.isEnabled
758760
button.label = label
759761
button.clicked = { _ in action() }
760762
button.css.clear()
@@ -767,9 +769,16 @@ public final class Gtk3Backend: AppBackend {
767769
return ToggleButton()
768770
}
769771

770-
public func updateToggle(_ toggle: Widget, label: String, onChange: @escaping (Bool) -> Void) {
771-
(toggle as! ToggleButton).label = label
772-
(toggle as! Gtk3.ToggleButton).toggled = { widget in
772+
public func updateToggle(
773+
_ toggle: Widget,
774+
label: String,
775+
environment: EnvironmentValues,
776+
onChange: @escaping (Bool) -> Void
777+
) {
778+
let toggle = toggle as! Gtk3.ToggleButton
779+
toggle.label = label
780+
toggle.sensitive = environment.isEnabled
781+
toggle.toggled = { widget in
773782
onChange(widget.active)
774783
}
775784
}
@@ -782,8 +791,14 @@ public final class Gtk3Backend: AppBackend {
782791
return Switch()
783792
}
784793

785-
public func updateSwitch(_ switchWidget: Widget, onChange: @escaping (Bool) -> Void) {
786-
(switchWidget as! Gtk3.Switch).notifyActive = { widget, _ in
794+
public func updateSwitch(
795+
_ switchWidget: Widget,
796+
environment: EnvironmentValues,
797+
onChange: @escaping (Bool) -> Void
798+
) {
799+
let switchWidget = switchWidget as! Gtk3.Switch
800+
switchWidget.sensitive = environment.isEnabled
801+
switchWidget.notifyActive = { widget,_ in
787802
onChange(widget.active)
788803
}
789804
}
@@ -803,9 +818,11 @@ public final class Gtk3Backend: AppBackend {
803818
minimum: Double,
804819
maximum: Double,
805820
decimalPlaces: Int,
821+
environment: EnvironmentValues,
806822
onChange: @escaping (Double) -> Void
807823
) {
808824
let slider = slider as! Scale
825+
slider.sensitive = environment.isEnabled
809826
slider.minimum = minimum
810827
slider.maximum = maximum
811828
slider.digits = decimalPlaces
@@ -830,6 +847,7 @@ public final class Gtk3Backend: AppBackend {
830847
onSubmit: @escaping () -> Void
831848
) {
832849
let textField = textField as! Entry
850+
textField.sensitive = environment.isEnabled
833851
textField.placeholderText = placeholder
834852
textField.changed = { widget in
835853
onChange(widget.text)
@@ -861,6 +879,7 @@ public final class Gtk3Backend: AppBackend {
861879
// onChange: @escaping (Int?) -> Void
862880
// ) {
863881
// let picker = picker as! DropDown
882+
// picker.sensitive = environment.isEnabled
864883

865884
// // Check whether the options need to be updated or not (avoiding unnecessary updates is
866885
// // required to prevent an infinite loop caused by the onChange handler)
@@ -1131,6 +1150,7 @@ public final class Gtk3Backend: AppBackend {
11311150
public func updateTapGestureTarget(
11321151
_ tapGestureTarget: Widget,
11331152
gesture: TapGesture,
1153+
environment: EnvironmentValues,
11341154
action: @escaping () -> Void
11351155
) {
11361156
if gesture != .primary {
@@ -1139,6 +1159,7 @@ public final class Gtk3Backend: AppBackend {
11391159
tapGestureTarget.onButtonPress = { _, buttonEvent in
11401160
let eventType = buttonEvent.type
11411161
guard
1162+
environment.isEnabled,
11421163
eventType == GDK_BUTTON_PRESS
11431164
|| eventType == GDK_2BUTTON_PRESS
11441165
|| eventType == GDK_3BUTTON_PRESS

0 commit comments

Comments
 (0)