This crates adds a procedural macro to derive a focus chain for your application state. This allows the user of your iced
application to swap the focus between the input fields of your user interface using Tab
and Alt+Tab
. The order of the focus chain will be derived of the oder of the fields of your state. The crate iced_focus
serves as a workaround until iced provides it's own focusing.
Add iced_focus
to your dependencies:
toml
[dependencies]
iced_focus = { version = "0.1.0", features = ["derive"] }
iced = "0.3.0"
iced_native = "0.4.0"
Note: you also need icednative for the keyboard subscription_
Then derive the Focus
trait for your State:
```rust
use iced::textinput;
use icedfocus::Focus;
struct Example { text: String, #[focus(enable)] textinput: textinput::State, } ```
As there is no way of knowing whether a field implements a specific trait, you will have to annotate each input field with #focus(enable)
to add the field to the focus chain.
To handle the keyboard input of the user add an additional message like Focus(iced_focus::Direction)
to your applications message definition:
rust
enum Message {
TextInput(String),
// Add this:
Focus(iced_focus::Direction),
}
Add this subscription to your application ...:
rust
impl Application for Example {
...
fn subscription(&self) -> Subscription<Message> {
iced_native::subscription::events_with(|event, _status| {
if let iced_native::Event::Keyboard(iced::keyboard::Event::KeyPressed {
key_code: iced_native::keyboard::KeyCode::Tab,
modifiers,
}) = event
{
Some(Message::Focus(if modifiers.shift {
iced_focus::Direction::Backwards
} else {
iced_focus::Direction::Forwards
}))
} else {
None
}
})
}
...
}
... and handle the focus message:
```rust
impl Application for Example {
...
fn update(...) -> iced::Command
// Add this:
Message::Focus(direction) => {
let _ = self.focus(direction);
}
}
iced::Command::none()
}
...
} ```
Done! Happy focusing! 🙂
You can specify whether the field will be added to the focus chain by providing a path to a method. The method must be of kind Fn(&self) -> bool
.
```rust struct Example { text: String, enable: bool, #[focus(enable = "self.enabletextinput")] textinput: textinput::State, }
impl Example { fn enabletextinput(&self) -> bool { self.enable } } ```
Currently, only the TextInput
widget is supported as it is the only widget that supports focusing. This crate only provides a linear focus chain based on the ordering of the fields. The actual position of the element on the window in unknown to the application state.
You can derive the Focus trait for structs, tuple structs and enums:
```rust
struct ExampleTuple ( String, #[focus(enable)] text_input::State, );
enum ExampleEnum { Unit, Named { text: String, #[focus(enable)] textinput: textinput::State, }, Unnamed ( String, #[focus(enable)] text_input::State, ) } ```
You can have vecs, arrays, options or nested structs: ```rust
struct ExampleVec {
#[focus(enable)]
text_inputs: Vec
struct ExampleArray { #[focus(enable)] textinputs: [textinput::State; 10], }
struct ExampleOption {
#[focus(enable)]
text_input: Option
struct Example { #[focus(enable)] vec: ExampleVec,
#[focus(enable)]
array: ExampleArray,
#[focus(enable)]
option: ExampleOption,
} ```