Add clipboard shortcuts to immediate text fields - #41
struckchure wants to merge 2 commits into
Conversation
Cmd/Ctrl+C, X and V now copy, cut and paste in the focused text field of the immediate renderer, with pasted newlines flattened to spaces in single-line fields. Char events are filtered by modifier so the chord's letter is not also inserted as text: macOS delivers 'v' for Cmd+V and X11 delivers it for Ctrl+V. Ctrl+Alt is deliberately allowed through because Windows reports AltGr that way and AltGr characters are genuine text. macOS Backspace (reported as 0x7F) is dropped as well. The clipboard is a single lazily-created instance: on X11 the clipboard window owns the CLIPBOARD selection, so a per-keystroke instance that is destroyed right after copying would hand ownership back to nobody.
medvednikov
left a comment
There was a problem hiding this comment.
Reviewed the changed input handlers, editor state updates, existing custom-renderer tests, and V's clipboard implementations. Two actionable findings are inline: cut must preserve the selection when the clipboard write fails, and the newly required character-event modifier argument needs to be supplied by the existing test caller.
Validation: source-level review only; I could not run V compilation or platform clipboard tests in this environment. The one-argument handle_key_down calls in the same test already exist in the base commit and are not counted as regressions introduced by this PR.
| cb.copy(selected) | ||
| editor.replace_selection('') |
There was a problem hiding this comment.
[P2] Preserve selected text when the clipboard write fails
clipboard.Clipboard.copy() returns a success boolean, but this branch deletes the selection regardless of that result. For example, V's Windows backend returns false when it cannot acquire the clipboard lock or SetClipboardData fails; Ctrl+X would then remove the user's text and fire the change event without putting that text on the clipboard. Please only call replace_selection and publish the editor/change updates after a successful copy (for example, return true without changing the editor when !cb.copy(selected)). A failed-copy regression test should assert that both the text and selection are preserved.
There was a problem hiding this comment.
Fixed in e1c0875. Cut now only edits the field after cb.copy() returns true; on failure the text and selection stay untouched and no change event fires.
To make that testable without the system clipboard, the shortcut handler reads through a small TextClipboard interface (copy/paste) and tests install a fake via g_clipboard_override. test_cut_keeps_the_text_when_the_clipboard_rejects_it asserts the text, the selection range and the absence of a change event; I verified it fails against the previous unconditional cut.
| } | ||
|
|
||
| fn handle_char_input(ch u32) { | ||
| fn handle_char_input(ch u32, modifiers u32) { |
There was a problem hiding this comment.
[P2] Update the existing test caller for the new required argument
test_custom_text_editor_replaces_owned_state_after_caret_moves in ui/ui_custom_test.v still calls handle_char_input(x) with one argument. With ui2_custom_rendering enabled, this signature change introduces an additional test-compilation error because modifiers is now required. Please update that call to handle_char_input(x, 0) and exercise the modifier-filtering cases here as well (Ctrl/Super shortcut characters are suppressed, Ctrl+Alt text is retained, and DEL is ignored). The reported -check ui/ verification does not exercise this _test.v caller.
There was a problem hiding this comment.
Done in e1c0875, with one wrinkle: ui_custom_test.v can't compile regardless of the argument count. It sorts before ui_immediate.c.v, so it never sees the renderer globals (g_text_values, g_focused_field, …), it doesn't import gg for the key codes, and its typed map[string]bool{ ... } literals are rejected by 0.5.2 too. It isn't in CI, which is why nobody noticed.
Rather than patch the one call in a file that still can't build, I moved the caret-move test into a new ui/ui_text_clipboard_immediate_test.v that follows the ui_pointer_capture_immediate_test.v pattern (sorts after the renderer, @[has_globals], same platform guard). Besides the original assertion it covers:
handle_char_input: Ctrl+V / Cmd+V / Ctrl+Shift+V letters are dropped, DEL (127) and BS (8) are dropped, Ctrl+Alt@(AltGr) and Shift+aare kept, and change events fire only for real insertions- copy/cut/paste through the fake clipboard, newline flattening in single-line fields vs. text areas, empty-paste being a no-op, and chords without the primary modifier not touching the clipboard
CI runs the new file on the same matrix entries as the scrolling test. I left the rest of ui_custom_test.v alone since that's unrelated to this PR; happy to fix it up separately.
clipboard.Clipboard.copy reports failure (the Windows backend returns false when it cannot lock the clipboard or SetClipboardData fails), but Ctrl+X removed the selected text regardless, so a failed cut destroyed the user's text without putting it anywhere. The cut now only edits the field after a successful copy. The shortcut handler reads the clipboard through a small TextClipboard interface, and tests install a fake via g_clipboard_override. The new ui_text_clipboard_immediate_test.v covers copy, cut, paste, the failed copy, newline flattening in single-line fields, the modifier filtering in handle_char_input (Ctrl/Super chord letters and DEL dropped, Ctrl+Alt text kept), and the caret-move test that used to live in ui_custom_test.v with a now-stale one-argument call. That file sorts before ui_immediate.c.v so it cannot see the renderer globals at all; the test moved to a file that can. CI runs the new test on the same matrix entries as the scrolling test. Co-Authored-By: Claude <noreply@anthropic.com>
Summary
handle_char_inputnow takes the event modifiers and drops the chord's letter, which macOS (Cmd+V→'v') and X11 (Ctrl+V→'v') both deliver as a char event. Ctrl+Alt passes through because Windows reports AltGr that way and AltGr characters (@,€, …) are real text. macOS Backspace (0x7F) is dropped too.system_clipboard()). On X11 the clipboard window holds CLIPBOARD selection ownership, so a per-keystrokenew()/destroy()would surrender it immediately and also leak a display connection and listener thread per press.Test plan
v fmt -verifyandv -d ui2_custom_rendering -check ui/passv; Backspace does not insertDEL