Focus
The struct Focus can do all the focus handling for your application.
As it is essential for almost any application, it got a place in AppContext.
Usage
#![allow(unused)] fn main() { if self .w_split.is_focused() { ctx.focus().next(); } else { ctx.focus().focus( & self.w_split); } }
Just some example: This queries some widget state whether it currently has the focus and jumps to the next widget /sets the focus to the same widget.
There's always a trait
or two.
-
This trait is used both for simple widgets and for containers.
- Widgets
The main functions are focus() and area().
focus() returns a clone of a FocusFlag that is part of the widgets state. It has a hidden
Rc<>
, so this is fine.The flag is close to the widget, so it's always there when you need it. As an Rc it can be used elsewhere too, say Focus.
area() returns the widgets current screen area. Which is used for mouse focus.
- Containers
Containers use the build() function of the trait to add their component widgets.
AppState
In your application you construct the current Focus for each event.
This is necessary as
- the application state might have changed
- the terminal might have been resized
and
- it's hard to track such changes at the point where they occur.
- it's cheap enough not to bother.
- there is room for optimizations later.
#![allow(unused)] fn main() { ctx.focus = Some(FocusBuilder::for_container( & self .app)); }
If you have a AppWidget that HasFocus
you can simply use
FocusBuilder to construct the current Focus. If you then set it
in the ctx
it is immediately accessible everywhere.
Events
Focus implements HandleEvent, so event handling is simple.
#![allow(unused)] fn main() { let f = Control::from( ctx.focus_mut().handle(event, Regular) ); }
Regular
event-handling for focus is
- Tab: jump to the next widget.
- Shift-Tab: jump to the previous widget.
- Mouse click: focus that widget.
Focus is independent from rat-salsa, so it returns Outcome instead of Control, thus the conversion.
Complications
handle
returns Outcome::Changed when the focus switches to a new widget and everything has to be rendered. On the other hand the focused widget might want to use the same mouse click that switched the focus to do something else.We end up with two results we need to return from the event handler.
#![allow(unused)] fn main() { let f = Control::from(ctx.focus_mut().handle(event, Regular)); let r = self .app.crossterm(event, ctx) ?; }
Here
Ord
comes to the rescue. The values of Control are constructed in order of importance, so
#![allow(unused)] fn main() { Ok(max(f, r)) }
can save the day. If focus requires Control::Changed we return this as the minimum regardless of what the rest of event handling says.
Or you can just return a second result to the event-loop using
#![allow(unused)] fn main() { let f = ctx.focus_mut().handle(event, Regular); ctx.queue(f); }
and be done with it.