Refactored cursor and selection manipulation, and more!#146
Conversation
Issue elcritch#145 textboxes.nim - Implemented cursor + anchor model for selection - Refactored almost all cursor movement procedurs into shiftCursor - Created an Orient enum with enumerators for shiftCursor (e.g. Left, NextWord, TheEnd) - Created placeCursor procedure for direct placement by index - Created clearSelection procedure - Created growSelection procedure - Rewrote delete and created deleteSelected - Rewrote insert, cursorUp, cursorDown, `selection=`, and findLine - Organized the cursor and selection procedures - Wrote documentation for various procedures - Change NextWord / PreviousWord functionality to always jump to the beginning of each word input.nim - Ensured the cursor blinks - Created activate() procedure - Rewrote delete-previous-word logic - Switched word skipping to Meta+Arrow-keys (this is much more common) - Switched word selection to Meta+Shift+Arrow-keys - Eliminated cursor alignment problem by connecting layoutResize slot tinput.nim - Ensured input is activated, and the cursor is visible on init - Ensured cursor is placed at TheEnd on init tinput2.nim - Ensured input is activated, and the cursor is visible on init - Ensured all numbers are editable ttextboxes.nim - Updated tests based on new model / procedures
Totally believable – I've spent so many hours dealing with subtle details with font layout, grid layout, events, etc. It's pretty rewarding though working on it at the fundamental level! But it can be a slog at times. I'm looking forward to giving it a whirl!
Sweet! Did you happen to add tests for that as well? I wanna make sure we test those so refactoring or bug fixing is easier in the future. They're a pain but the LLMs are pretty good at writing them.
Fine by me, and yeah generally we'll need some sort of configurable input filters. I think HTML input filters might be the best model. It wasn't clear to me what the best way to add them would be. As you probably saw, there's the raw key inputs, and then input creates a second set of higher level events. That makes sense. What's unclear now is how best to have overrides that the end-user widget be can intercept and modify or cancel the high level events. Then again having a callback or regex could be another approach, as signals and slots don't have direct returns. haha, yep you can see what I've been working on for a couple of years. I wanted to get the fundamentals set well, even if not perfect now so they can evolve. Also, trying to bridge the best between HTML and QT/GTK is an interesting challenge. It wasn't an original goal of Figuro but has evolved into that. ;) |
|
|
||
| if not connected(self, doKeyCommand, self, keyCommand): | ||
| connect(self, doKeyCommand, self, Input.keyCommand) | ||
| if not connected(self, doUpdateInput, self): |
There was a problem hiding this comment.
I like moving this to initialize, though it goes back to the question of how best to add a filter. One way is this if not connected(self, doUpdateInput, ...) which would allow the user of Input to connect their own doUpdateInput handler first. I never followed through trying it out though, but did add the is connected logic in Sigils for this sort of scenario.
Dropping it for now makes sense though. Just wanted to point it out to you.
There was a problem hiding this comment.
I forgot to ask about this. What's the difference between the initialize slot and using onInit in the draw slot?
I'm not clear about how the slot override by users works in terms of timing. Are you saying that connecting things in this initialize slot will block users from overriding those connections?
There was a problem hiding this comment.
I forgot to ask about this. What's the difference between the initialize slot and using onInit in the draw slot?
Generally speaking initialize is used when implementing a widget, while onInit is for customizing an existing widget like Input when it's instantiated.
I added initialize first, but it's more hassle to add, so I added onInit as well. Technically, doInitialize & initialize are a signal and slot, while onInit is part of the the preDraw callback.
Hmmm, maybe I should make a diagram. I'm also thinking instead of preDraw calling that stage setup?
Widget Lifecycle
- Widget Allocated and
preNodeinternal API runs - Widget
preDrawcallback is called (e.g. Setup) -- this is what the user adds - Widget
doDrawsignal emitted that callsdrawand other slots. - Widget
postDrawcallback is optionally called (unused currently) - Widget
destroy(not implemented yet, I think)
There was a problem hiding this comment.
I'm not clear about how the slot override by users works in terms of timing. Are you saying that connecting things in this initialize slot will block users from overriding those connections?
Hmmm, yes I think that would as initialize runs first. However, it's not clear what is the best way to override the doInput signals. I also experimented with a separate overrideInput or something.
Suggestions are welcome!
There was a problem hiding this comment.
How does this look?
https://github.com/elcritch/figuro/blob/f78059fcc0ae010376f9cd5b76cac6d5924791f2/README.md
All the details probably don't need to be there, but I don't know what's useful to you or others.
There was a problem hiding this comment.
What does the preNode step refer to? And what's the condition were initialize is skipped? When the user has used onInit?
| this.updateInput(rune) | ||
| this.text.cursorNext() | ||
| onInit: | ||
| this.activate() |
There was a problem hiding this comment.
Nice, so the onInit use cases is making sense now?
| discard | ||
| elif down.matches({KAlt, KShift}): | ||
| case pressed.getKey | ||
| of KeyLeft: |
There was a problem hiding this comment.
Hmmm, do we loose the alt-left alt-right handling of did you move it elsewhere? It'd need to be refactored to handle ctrl-left on windows/linux anyways.
There was a problem hiding this comment.
The word skipping and word selecting are Ctrl (Meta) based instead of Alt now. This is how things usually are set up on Windows/Linux. It's been a while since I've used macOS so I can't recall how it usually works on it.
There was a problem hiding this comment.
Okay, yeah MacOS uses alt. But that can be changed later.
There was a problem hiding this comment.
Ah ok. Yea, this is something to have configurable by users anyhow.
There was a problem hiding this comment.
Ah ok. Yea, this is something to have configurable by users anyhow.
Yeah, that'd be best. The current setup doesn't allow that aside from configuring Meta.
BTW to clarify this point, I mean the signals and events and core objects. Being able to make it easy to use yet extensible is an interesting challenge. QT, GTK and the rest always seem to "hardcode" it into a big object hierarchy while HTML makes it almost impossible to customize. I also like Mac's Cocoa design where the input handler can be overriden at runtime. I used it to run Vim or Emacs plugins for years. |
Issue #145
textboxes.nim
selection=, and findLineinput.nim
tinput.nim
tinput2.nim
ttextboxes.nim
So I'm finally, mostly confident that everything works. It might be hard to believe I spent many hours every day for a full week on this. There's so much to understand and consider (as you already know).
There is, however, at least one remaining issue to deal with. Originally the
insertprocedure wouldn't allow any text to be added beyond what was originally set, when in Overwrite mode. That means it couldn't be used for the common insert/overwrite toggle feature. I've rewritten it to allow for that now, but that means that in tinput2 you can write numbers past the established range (00:00:00). So it seems like we need another option that can be set, such asLimitRange. Let me know if you have any ideas about that, and of course anything else in this PR.