Skip to content

feat(core): allow focusing of ancestors of renderables#930

Open
garthdw wants to merge 7 commits intoanomalyco:mainfrom
garthdw:feat/support-hierarchy-focusing
Open

feat(core): allow focusing of ancestors of renderables#930
garthdw wants to merge 7 commits intoanomalyco:mainfrom
garthdw:feat/support-hierarchy-focusing

Conversation

@garthdw
Copy link
Copy Markdown
Contributor

@garthdw garthdw commented Apr 6, 2026

Only focusable ancestors are focused.

Opt in behaviour at the renderer level and needing to mark direct ancestors as focusable.

Screencast_20260406_145554.mp4

For #929

@garthdw garthdw marked this pull request as draft April 6, 2026 13:42
@garthdw
Copy link
Copy Markdown
Contributor Author

garthdw commented Apr 6, 2026

Dammit, nothing worse than that quick code change before a PR to break tests and then make you end up with more brutal code. :/

@garthdw garthdw marked this pull request as ready for review April 6, 2026 14:06
@kommander
Copy link
Copy Markdown
Collaborator

Interesting. I think that makes sense. I don't know how opentui users currently use it. The renderer knows only about exactly one focused renderable. It could however be an additional tree attribute that is propagated? Wonder how the browser and apps there handle that.

If you want a box around a Textarea to react to the Textarea focus, it can subscribe to that state.

I consider only Renderables that handle key events to be focusable. So style/ui only elements would not set focusable=true on themselves.

@garthdw
Copy link
Copy Markdown
Contributor Author

garthdw commented Apr 7, 2026

So looks like html has a :focus-within or at least firefox does.

Screen.Recording.2026-04-07.at.21.05.55.mov

I'll explore some alternatives to how to represent this behaviour.

@garthdw garthdw force-pushed the feat/support-hierarchy-focusing branch from 41f4134 to 41ed48c Compare April 7, 2026 21:07
@garthdw
Copy link
Copy Markdown
Contributor Author

garthdw commented Apr 7, 2026

I have implemented it a different way.

I'm toying between doing it as just focusable on parent elements or if we should add a separate property, I just can't think of a good name for it.

while (parent) {
if (parent._hasFocusedDescendant !== hasFocus) {
parent._hasFocusedDescendant = hasFocus
parent.requestRender()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be enough to call this once after the while loop, as multiple calls will just be ignored anyway when a render was already requested.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was also cautious about the multiple calls. The benefit we get is the mark as dirty, but if we don't need that and can't foresee any other issues, I'm happy to move it out of the loop and call it on itself.

public blur(): void {
if (!this._focused || !this._focusable) return

this._ctx.blurRenderable(this)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realised earlier this week that renderer.focusedRenderable basically never gets reset. It's also not reset when the renderable is destroyed. So in the renderable destroy method it should probably be blurred as well. Would be cool to get this new blur behaviour for renderer level focus/blur test covered.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So "luckily" this change will help with the destroy. It calls blur when destroying.

Let me add test coverage though.

garthdw and others added 6 commits April 9, 2026 18:57
Only focusable ancestors are focused.

Opt in behaviour.
We now just build the focusedRenderables from scratch

While knowing if we are initiator of a focus or just part of it.
Walk the tree and set if it has dependants.

Remove the focus state in renderer.ts
@garthdw garthdw force-pushed the feat/support-hierarchy-focusing branch from 50662f2 to 86122d4 Compare April 9, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants