Input system overhaul
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 14.6k
- Forks
- 734
- Avg merge
- 8h 40m
- Merged PRs (30d)
- 4
Description
I am making this issue to discuss a possible way of implementing the next input system.
Right now, input system binds the shortcuts directly in code, which makes it hard to change for the end user, as well as creates some complications when adopting the editor to platforms with *unusual* terminal behaviours (e.g. macOS).
## How input system can be implemented?
### Constraints
As was already discussed [here](https://github.com/microsoft/edit/issues/180#issuecomment-2899309145), inputs can:
- Be declared outside of the main binary (see: extensions plan)
- Work on condition (see: menubar)
- Overlay each other (e.g. multiline textarea in modal and enter key)
Additionally, following factors can influence the decision:
- No callbacks by design
- Immediate render mode
So, what can be done? Solution that I came up with looks like this:
### Overview
We already have two systems that will help us: Previous node tree and focus.
What I want to do, is to insert into node tree "Action scope boundaries", that will have scopes associated with them.
When the key is pressed, we are going to start at the node that is focused, and find the closest action boundary that has this shortcut/key bound for its scope, and fire the action associated with this shortcut.
On the render pass, we can return the action only when we are in the same scope again.
Code that is expected if this will be the chosen method:
```rs
fn setup(tui: &mut Tui) {
tui.declare_scope("menu-a", &[("back", vk::ESC)]);
}
fn render_menu(ctx: &mut Context, state: &mut State) {
ctx.scope_begin("menu-a", 12);
// Normal rendering logic here...
if ctx.consume_action("back") {
state.menu_open = false;
}
ctx.scope_end();
}
```
### Pros and cons
#### Pros:
- Does not require any enums/hardcoding
- Reasonably easy to create keymap overrides
- Does not require callbacks, works reasonably well for immediate mode rendering
#### Cons:
- Complicates consumption of input
Let me expand on that.
### Complications
Let's define two UI elements:
```rs
fn list_render(ctx: &mut Context, state: &mut State) {
ctx.scope_begin("list", 1);
for i in 1..11 {
checkbox_render(ctx, state, i);
}
ctx.scope_end();
}
fn checkbox_render(ctx: &mut Context, state: &mut State, n: usize) {
ctx.scope_begin("checkbox", n)
ctx.scope_end();
}
```
Let's assume that both "list" and "checkbox" scopes have DEL button bound. For checkbox, DEL deletes the checkbox sometimes (for now let's say 50% of the time), for list it deletes the list itself (always).
Let's consider this state (I will write it in xml, just for ease of understanding), including action boundaries (tag `asb`):
```xml
```
Human pressed DEL, with the focus on the checkbox, and the checkbox have not been deleted. That means that now we need to propagate the action up, because it's not handled. But, for the system to give you back DEL bound to the "list" scope, it needs to either:
- Be sure that checkbox with key 1 *did not handle* the key
- checkbox with key 1 never appeared in the render sequence
First one is easy to verify, but No. 2 cannot be guaranteed unless we closed the scope. So the only solution it to only allow developers to put key consumption AFTER all the children were rendered, and `panic!` if someone tried to first consume the key and then the child scope we were targeting with focus appeared down the render chain.
When I thought of this, I reasoned that this will make hiding/deleting elements harder (sometimes).
Well, this is not only option. But the second option requires us to be able to abandon rerenders, which makes everything much more complicated and probably undesirable.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the current input bindings and the previous node tree and focus systems, then review the discussion in issue #180. Compare the proposed action scope boundaries, scope_begin/scope_end, and consume_action flow against the listed constraints and complications. Done requires an agreed input-system design; this issue does not yet identify an implementation file or test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100