Refactor and fix focus management#1276
Conversation
There was a problem hiding this comment.
Thanks for undertaking this refactor. Focus management has been a longstanding problem, and I'm glad we're thinking through a design for it instead of piling on hacks.
There may well be subtleties that I'm overlooking, but the present design is more centralized and specific to notebooks that I would have expected. As the comments in type.ts explain, the application-level focus state says not only which notebook is focused, but which cell and even which popover in the notebook is focused. I have several worries about this design:
- It is specialized to notebooks, but we will have documents that are not notebooks
- It leaks information deep in the component hierarchy up to the application top-level
- It conflates data and layout: focus is attached to notebooks by ID, rather than to panes
To focus the mind, consider the concrete scenario of integrating a Petri net editor into CatColab (cf. #1279). Such an editor will need to get and lose focus coherently with the rest of the app.
I'm trying to imagine a more compositional/hierarchical approach, where a component tracks which of its immediate children has focus. We're already kind of doing this in the notebook and object/morphism editors, e.g., a notebook keeps track of which cell is focused. I propose doing the same thing one level up: the document page should keep track of which of its 1-2 panes has focus. In this approach, focus changes bubble up the component hierarchy, e.g., when a name field in a morphism editor gets focus, it notifies its cell that it has focus, which notifies its notebook, which notifies the document page. In this way, a component pushes its focus events upward.
To handle keyboard shortcuts, components must also be able to "pull" the latest focus state, since another component may have grabbed it. To do so, one must walk up the component hierarchy again, asking if each parent has focus. I would imagine this pulling and pushing could be handled by passing a getter and a setter for focus state.
What do you think?
| changeNotebook={(f) => liveDoc().changeDoc((doc) => f(doc.notebook))} | ||
| formalCellEditor={AnalysisCellEditor} | ||
| cellConstructors={cellConstructors()} | ||
| noShortcuts={true} |
There was a problem hiding this comment.
Exciting to finally be rid of this hack!
e33f92b to
9e0c9cd
Compare
|
I've introduced a |
epatters
left a comment
There was a problem hiding this comment.
Thanks Kaspar! The way you've set up the new FocusHandle abstraction is quite elegant IMO.
| if (!props.isActive) { | ||
| setActiveInput("name"); | ||
| } | ||
| }); |
There was a problem hiding this comment.
It's great that the new design avoids having to handle this tricky logic across a bunch of individual components.
This is largely a refactor of focus and keyboard shortcut management (closes #161). It also:
Escout of a completion popup.