New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gesture,widget: Text selection in Editor widgets #3
Conversation
theclapp
commented
Dec 16, 2020
•
edited
edited
- Click-and-drag to select text.
- Cut/Copy selected text via Shortcut-X/C
- Paste text via Shortcut-V
- Paste overwrites selected text
- Typing overwrites selected text
- A "select all" hotkey, Shortcut-A
- Selection via keyboard via Shift-Arrows
- Touch support
- Double-click selects a word
- Triple-click selects a line
- Shift-click selects from the caret to the clicked position, or adjusts the current selection.
0429fd7
to
5801d2d
Compare
Force-pushed with signed commits. Appreciation to @whereswaldon in #2 (comment) for laying out how to do that. |
Haven't looked closer yet, but I'd love a test or two to demonstrate and lock in the new behaviour :) |
@eliasnaur I added a test case and did some other refactoring. PTAL. Thanks. |
2cf26f4
to
1b24a63
Compare
Rebased from |
Sorry for the delay. I intend to review this soon, but given its complexity I'd like to release the new renderer first. |
1b24a63
to
f0c838f
Compare
There's obviously more to do for this functionality: If I have time to work on any of that, would you prefer that I just added it to this branch (probably making the change larger and presumably harder to review), or work on that in a different branch (branched from this one)? Happy to do either, but would prefer to optimize in favor of getting it merged sooner. |
f0c838f
to
e75c0c4
Compare
Please add new work as separate commits on top of the others. The combined change will be larger, but that's ok as long as the commits are logically separate units. |
c21b8ad
to
3da1578
Compare
Rebased to current main (with the compute renderer) and force-pushed. Also, earlier, moved the feature list from an earlier comment to the PR description as a checklist. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent work, I can't wait to have selections in Gio!
This is the first round of review comments. The comments are mainly about making Editor always have a selection (or, equivalently, adding width to the caret), and making the zero value (zero characters selected) useful in the existing operations (move, delete etc.).
Pushed some new code. Marked several comments as "resolved". The rest are still outstanding. |
You mentioned "this is the first round of review comments", so maybe you meant to address this later, but just in case: The selection color is hardcoded to |
Specifying the color is best left for the theme, which in your case probably means export paintSelection as PaintSelection, calling it from material.Editor. Just like PaintText, PaintSelection would just use op.PaintOp and not specify a color. |
I agree.
I'm not sure I know how to do that. (I'm not saying it's impossible, just that I don't know how.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which in your case probably means export paintSelection as PaintSelection, calling it from material.Editor. Just like PaintText, PaintSelection would just use op.PaintOp and not specify a color.
I'm not sure I know how to do that. (I'm not saying it's impossible, just that I don't know how.)
paintSelection
callspaint.FillShape
, which takes a color explicitly.
See Editor.PaintText that uses raw clip and PaintOps for painting the text with whatever material is current.
And where would the color come from in the first place? Can I just add
SelectionColor
toEditorStyle
, and default it toTheme.Palette.ContrastBg
in widget/material.Editor()
SGTM
and pass it to
PaintText
, where it's used in thepaintSelection
color? Also, makingpaintSelection
more stand-alone would appear to require iterating overe.shapes
again (unless I'm missing something), which seems wasteful.
I don't mind the waste for now; it's only iterating the visible lines of text once more. We can get clever in a follow-up, for example by storing into e.selectedShapes just the e.shapes that need selection painted (usually none), or by constructing the selection area as one large clip operation.
e4e5f7a
to
570b048
Compare
Pushed some new code, marked some more conversations as resolved. Still more work to be done, but getting there. Pushing the selection-aware code down as far as it could go does in fact make the code cleaner and more streamlined. Thanks for the suggestion. |
I think I've addressed all comments / objections / suggestions so far. PTAL. Thank you for your feedback. |
So, for example, a set of N commits that does refactoring only, setting the stage for the selection code, possibly changing the API (e.g. changing Move to MoveCaret, and adding SetCaret), but adding no actual selection code or api, and then another set of N commits that does add the selection code, but makes no (or as few as possible) other changes. So the first set would, for example, change Yes? So ... would doing all that in place make the comments in this PR hard to read afterwards, or does GH know how to take care of all that? And if the former, maybe I should open a new PR with a new branch with the reordered / combined / split commits? Any advice? I haven't done anything like that before. |
Just for posterity: I suspect others might benefit from this as much as I did, which has sections on squashing, splitting, and reordering commits: https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History |
One of the things I'm wondering, from you (@eliasnaur) or anyone else that happens to be following along, is: would the reorganization of the commits you're requesting be easier with an interactive rebase, with (I suspect) rather a lot of commit splitting and conflict resolution, or would it be easier if I just diffed the current branch against main, and created brand new commits in a new branch, with (I suspect) way fewer problems with commit splitting and conflict resolution. I mean, to be sure, the whole diff is 1842 lines, but at least I'd only have to go over them approximately once each, instead of quite possibly several times each. Like I said, looking for advice. /cc @mvdan @whereswaldon if you have any advice. If not, no worries & thanks anyway. 😄 |
Personally, I just rebase and force-push to PRs :) Whatever you do, "see older versions of this PR" is practically impossible on GitHub, so I go for the simpler approach of fewer commits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the things I'm wondering, from you (@eliasnaur) or anyone else that happens to be following along, is: would the reorganization of the commits you're requesting be easier with an interactive rebase, with (I suspect) rather a lot of commit splitting and conflict resolution, or would it be easier if I just diffed the current branch against main, and created brand new commits in a new branch, with (I suspect) way fewer problems with commit splitting and conflict resolution.
I mean, to be sure, the whole diff is 1842 lines, but at least I'd only have to go over them approximately once each, instead of quite possibly several times each. Like I said, looking for advice.
FWIW, I rarely start over from a diff to main, and use git rebase -i main
almost exclusively to re-order, squash/fixup, edit commits and commit messages.
I guess my comments are nothing new here, but I also just use |
Exactly (I'm shamelessly stealing the "setting the stage" metaphor). The goal is to minimize and isolate the exciting changes, to reduce review fatigue and for easier debugging in case a future bisect blames one of your commits.
Yes.
Like Daniel, I don't have much hope for retaining the comment history. Even if we did retain history, there's little linking commits to the GitHub PRs. If you feel generous, please do summarize relevant history in the commit messages that end up merged. |
758a2bd
to
92e8dfc
Compare
Rebased from main before big commit reorganization. |
3ac8449
to
d688673
Compare
d3d8d6a
to
ddb58bf
Compare
I've reorganized the commits into just two, one with just refactoring, and one with just selection code, as requested. PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for rebasing the commits ,much appreciated.
I have a single concern about SetCaret left, otherwise LGTM.
- Move caret from editBuffer.caret to Editor.caret.pos.ofs and related refactoring. Move other fields in Editor.caret into Editor.caret.pos. - Refactor several functions to change a position passed into them, rather than changing e.rr.caret directly. - Add editBuffer.Seek(). - Remove editBuffer.dump(). - Change Editor.Move to MoveCaret. - Add Editor.SetCaret. - Updated tests. Signed-off-by: Larry Clapp <larry@theclapp.org>
- Allow dragging to be on both horizontal and vertical axes at once. - Split Editor.caret.pos into caret.start and caret.stop. caret.start is the old caret.pos, and is both the position of the caret, and also the start of selected text. caret.end is the end of the selected text. Start can be after end, e.g. after after Shift-DownArrow. - Update caret.end after a mouse drag, and various shifted keys (Shift-UpArrow, Shift-DownArrow, etc). - Change Shortcut-C to copy only the selected text, not the whole editor text. - Add Shortcut-X to copy and delete selected text, and Shortcut-A to select all text. - The various Insert/Delete/etc functions now overwrite or delete the selection, as appropriate. - Change MoveCaret to accept a distance for selection end, as well. Change SetCaret to accept a selection end offset. - Add SelectionLen to get the selection length, Selection to get selection offsets, SelectedText to get the selected text, and ClearSelection to clear the selection. - Add a rudimentary selection unit test, and extend the deleteWord unit test with some text selection cases. - Add SelectionColor to material.EditorStyle, which defaults to Theme.Palette.ContrastBg. Signed-off-by: Larry Clapp <larry@theclapp.org>
ddb58bf
to
6502c73
Compare
Trying out the latest commit 6502c73 I noticed that using arrow keys with highlighted text does not clear the highlight, as I would expect, but instead slides the highlight, which is a behaviour I've never seen before! |
I'm seeing this too. It's an interesting effect, for sure :) |
Yep. Good catch! This is only for the left & right arrows; up & down clear the selection as expected. It was like this before the commit reorganization, too, apparently. This is a direct result of the @eliasnaur, what are your thoughts? As we've discussed, we want the api to be selection-agnostic as far down as possible. The options as I see them:
const NoSelection = math.MaxInt32
func (e *Editor) MoveCaret(startDelta, endDelta int) {
if endDelta == NoSelection { e.ClearSelection() }
// ... 2-4 still require selection-specific code in the left/right arrow code, though, and also user code would have to do the same thing in a similar context. I think I prefer option 2, with the option of changing to option 1 when/if we ever export the other movement functions (moveWord/moveLine/etc). |
I believe you mean when Shift is not pressed?
I prefer option 2 as well. I don't see a need for MoveCaret to precisely map to keyboard combinations, so a ClearSelection when moving without shift is fine.
I think we can fold moveWord/Line into a generalization of MoveCaret. See my #3 (comment) sketch. |
Right. Oops.
Awesome.
For a later PR. |
Make the left and right arrow keys dismiss the selection. (Up and down already did.) Add a test case for all four. Signed-off-by: Larry Clapp <larry@theclapp.org>
Fixed. Test case added, too. |
Great work, all merged! Thank you for your patience. |
- This is the 1st commit message: ScrollTo, PagePrev/Next, etc This is a combination of 5 commits. - layout: add List.ScrollTo, ScrollPages, and 2 more Also add PagePrev & PageNext. Include tests for all. - app,app/internal/window,io/system: macOS menus A strawman interface to the macOS menuing system. - Make menus compile on other architectures - app/internal/window: Add modifiers to macOS menu event - Update to latest Gio main - This is the commit message gioui#2: Delete app/internal/wm/window.go No longer used in main branch. - This is the commit message gioui#3: Move menu changes into their own files. - This is the commit message gioui#4: io/key,widget: Make editor hotkey filter more specific Only look for up/down/pageup/pagedown if the editor is multiline. Only look for enter/return if the editor is Submit == true. Fix modifier for delete-backward & forward. Implement some key.Set builder functions and use them in the above code. Fixes: https://todo.sr.ht/~eliasnaur/gio/399 - This is the commit message gioui#5: widget: Tweak editor hotkey event listener For delete-forward and backward, only listen for ShortAlt. Only listen for Short-[C,X] (copy selection and cut selection) when there's a selection. - This is the commit message gioui#6: Add system.MenuEvent to Window.processEvent - This is the commit message gioui#7: key: rename BuildKeySet and BuildKeyGroup to BuildKeyset and BuildKeygroup. - This is the commit message gioui#8: Sleep 2ms after onClose. Signed-off-by: Larry Clapp <larry@theclapp.org>
- This is the 1st commit message: ScrollTo, PagePrev/Next, etc This is a combination of 5 commits. - layout: add List.ScrollTo, ScrollPages, and 2 more Also add PagePrev & PageNext. Include tests for all. - app,app/internal/window,io/system: macOS menus A strawman interface to the macOS menuing system. - Make menus compile on other architectures - app/internal/window: Add modifiers to macOS menu event - Update to latest Gio main - This is the commit message gioui#2: Delete app/internal/wm/window.go No longer used in main branch. - This is the commit message gioui#3: Move menu changes into their own files. - This is the commit message gioui#4: io/key,widget: Make editor hotkey filter more specific Only look for up/down/pageup/pagedown if the editor is multiline. Only look for enter/return if the editor is Submit == true. Fix modifier for delete-backward & forward. Implement some key.Set builder functions and use them in the above code. Fixes: https://todo.sr.ht/~eliasnaur/gio/399 - This is the commit message gioui#5: widget: Tweak editor hotkey event listener For delete-forward and backward, only listen for ShortAlt. Only listen for Short-[C,X] (copy selection and cut selection) when there's a selection. - This is the commit message gioui#6: Add system.MenuEvent to Window.processEvent - This is the commit message gioui#7: key: rename BuildKeySet and BuildKeyGroup to BuildKeyset and BuildKeygroup. - This is the commit message gioui#8: Sleep 2ms after onClose. Signed-off-by: Larry Clapp <larry@theclapp.org>