Skip to content

Commit aff908a

Browse files
committed
Implement onSubmit and submitScope modifiers for TextField handling
1 parent 82aa7db commit aff908a

File tree

7 files changed

+58
-11
lines changed

7 files changed

+58
-11
lines changed

Sources/AppKitBackend/AppKitBackend.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,14 +537,16 @@ public final class AppKitBackend: AppBackend {
537537
_ textField: Widget,
538538
placeholder: String,
539539
environment: EnvironmentValues,
540-
onChange: @escaping (String) -> Void
540+
onChange: @escaping (String) -> Void,
541+
onSubmit: @escaping () -> Void
541542
) {
542543
let textField = textField as! NSObservableTextField
543544
textField.placeholderString = placeholder
544545
textField.appearance = environment.colorScheme.nsAppearance
545546
textField.onEdit = { textField in
546547
onChange(textField.stringValue)
547548
}
549+
textField.onSubmit = onSubmit
548550
}
549551

550552
public func getContent(ofTextField textField: Widget) -> String {
@@ -1055,7 +1057,7 @@ final class NSCustomMenuItem: NSMenuItem {
10551057
// than the existing associated keys based approach. And probably more efficient too.
10561058
// Source: https://stackoverflow.com/a/36983811
10571059
final class Action: NSObject {
1058-
private let action: () -> Void
1060+
var action: () -> Void
10591061

10601062
init(_ action: @escaping () -> Void) {
10611063
self.action = action
@@ -1197,6 +1199,17 @@ class NSObservableTextField: NSTextField {
11971199
}
11981200

11991201
var onEdit: ((NSTextField) -> Void)?
1202+
var _onSubmitAction = Action({})
1203+
var onSubmit: () -> Void {
1204+
get {
1205+
_onSubmitAction.action
1206+
}
1207+
set {
1208+
_onSubmitAction.action = newValue
1209+
action = #selector(_onSubmitAction.run)
1210+
target = _onSubmitAction
1211+
}
1212+
}
12001213
}
12011214

12021215
// Source: https://gist.github.com/sindresorhus/3580ce9426fff8fafb1677341fca4815

Sources/Gtk3Backend/Gtk3Backend.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,13 +589,17 @@ public final class Gtk3Backend: AppBackend {
589589
_ textField: Widget,
590590
placeholder: String,
591591
environment: EnvironmentValues,
592-
onChange: @escaping (String) -> Void
592+
onChange: @escaping (String) -> Void,
593+
onSubmit: @escaping () -> Void
593594
) {
594595
let textField = textField as! Entry
595596
textField.placeholderText = placeholder
596597
textField.changed = { widget in
597598
onChange(widget.text)
598599
}
600+
textField.activate = { _ in
601+
onSubmit()
602+
}
599603

600604
textField.css.clear()
601605
textField.css.set(properties: Self.cssProperties(for: environment, isControl: true))

Sources/GtkBackend/GtkBackend.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,13 +593,17 @@ public final class GtkBackend: AppBackend {
593593
_ textField: Widget,
594594
placeholder: String,
595595
environment: EnvironmentValues,
596-
onChange: @escaping (String) -> Void
596+
onChange: @escaping (String) -> Void,
597+
onSubmit: @escaping () -> Void
597598
) {
598599
let textField = textField as! Entry
599600
textField.placeholderText = placeholder
600601
textField.changed = { widget in
601602
onChange(widget.text)
602603
}
604+
textField.activate = { _ in
605+
onSubmit()
606+
}
603607

604608
textField.css.clear()
605609
textField.css.set(properties: Self.cssProperties(for: environment, isControl: true))

Sources/SwiftCrossUI/Backend/AppBackend.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,17 @@ public protocol AppBackend {
335335
func createTextField() -> Widget
336336
/// Sets the placeholder label and change handler of an editable text field. The new
337337
/// change handler replaces any existing change handlers, and is called whenever the
338-
/// displayed value changes.
338+
/// displayed value changes. `onSubmit` gets called when the user hits Enter/Return,
339+
/// or whatever the backend decides counts as submission of the field.
339340
///
340341
/// The backend shouldn't wait until the user finishes typing to call the change handler;
341342
/// it should allow live access to the value.
342343
func updateTextField(
343344
_ textField: Widget,
344345
placeholder: String,
345346
environment: EnvironmentValues,
346-
onChange: @escaping (String) -> Void
347+
onChange: @escaping (String) -> Void,
348+
onSubmit: @escaping () -> Void
347349
)
348350
/// Sets the value of an editable text field.
349351
func setContent(ofTextField textField: Widget, to content: String)
@@ -650,7 +652,8 @@ extension AppBackend {
650652
_ textField: Widget,
651653
placeholder: String,
652654
environment: EnvironmentValues,
653-
onChange: @escaping (String) -> Void
655+
onChange: @escaping (String) -> Void,
656+
onSubmit: @escaping () -> Void
654657
) {
655658
todo()
656659
}

Sources/SwiftCrossUI/Environment/EnvironmentValues.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public struct EnvironmentValues {
3131
foregroundColor ?? colorScheme.defaultForegroundColor
3232
}
3333

34+
/// Called when a text field gets submitted (usually due to the user
35+
/// pressing Enter/Return).
36+
public var onSubmit: (() -> Void)?
37+
3438
/// Called by view graph nodes when they resize due to an internal state
3539
/// change and end up changing size. Each view graph node sets its own
3640
/// handler when passing the environment on to its children, setting up
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
extension View {
2+
/// Adds an action to perform when the user submits a text field within this
3+
/// view (generally via pressing the Enter/Return key).
4+
public func onSubmit(perform action: @escaping () -> Void) -> some View {
5+
EnvironmentModifier(self) { environment in
6+
environment.with(\.onSubmit, action)
7+
}
8+
}
9+
10+
/// Prevents text field submissions from propagating to this view's
11+
/// ancestors.
12+
public func submitScope() -> some View {
13+
EnvironmentModifier(self) { environment in
14+
environment.with(\.onSubmit, nil)
15+
}
16+
}
17+
}

Sources/SwiftCrossUI/Views/TextField.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ public struct TextField: ElementaryView, View {
2626
backend.updateTextField(
2727
widget,
2828
placeholder: placeholder,
29-
environment: environment
30-
) { newValue in
31-
self.value?.wrappedValue = newValue
32-
}
29+
environment: environment,
30+
onChange: { newValue in
31+
self.value?.wrappedValue = newValue
32+
},
33+
onSubmit: environment.onSubmit ?? {}
34+
)
3335
if let value = value?.wrappedValue, value != backend.getContent(ofTextField: widget) {
3436
backend.setContent(ofTextField: widget, to: value)
3537
}

0 commit comments

Comments
 (0)