Use focus() and find_focused() with text_input

Hi,

I am trying to retrieve the ID of an active text_input widget.
The text_input widget implements the Focusable trait. Therefore, I thought that I could use iced::advanced::widget::operation::focusable::find_focused() but it doesn’t return any ID.

Then I played around a bit and tried whether I could use a focus() function first to see if this is working. If so, I would expect that calling find_focus() directly afterwards produces a positive result.
I noticed that iced::widget::text_input::focus("some-id") focuses the text_input widget but iced::advanced::widget::operation::focusable::focus::<Message>(Id::new("some-id")) does not.
Is the second one not working because iced::advanced uses a different ID type than iced::widget and therefore cannot find any ID matches?

Do you have any tips what I am missing here? Or any ideas/examples how I could make iced::advanced::widget::operation::focusable::focus::<T>(...) work or how an alternative approach could look like?

Thanks a lot!


I am using iced 0.13.1
Below I pasted a short example code that I used to play around.
When the button is pressed, the console produces “Did not find a widget with ID some-id” and “Did not find a focused widget”.
And I would hope for “Focus on widget with ID some-id now” and “Found focused widget with ID some-id”.

use iced::advanced::widget::operation::Outcome;
use iced::advanced::widget::{Id, Operation};

use iced::widget::{button, column, text_input, Column};

fn main() -> iced::Result {
    iced::application("Focus Test", App::update, App::view).run()
}

#[derive(Default)]
struct App {
    input: String,
}

#[derive(Clone, Debug)]
enum Message {
    InputUpdated(String),
    FocusInput,
}

impl App {
    fn update(&mut self, message: Message) {
        match message {
            Message::InputUpdated(input) => {
                self.input = input;
            }
            Message::FocusInput => {
                // focus text_input widget
                match iced::advanced::widget::operation::focusable::focus::<Message>(Id::new(
                    "some-id"
                ))
                .finish()
                {
                    Outcome::None => println!("Did not find a widget with ID some-id"),
                    Outcome::Some(_) => println!("Focus on widget with ID some-id now"),
                    Outcome::Chain(_) => todo!(),
                }

                // expectation: find_focus function returns some-id as this widget just got focused
                match iced::advanced::widget::operation::focusable::find_focused().finish() {
                    Outcome::None => println!("Did not find a focused widget"),
                    Outcome::Some(id) => println!("Found focused widget with ID {:?}", id),
                    Outcome::Chain(_) => todo!(),
                }
            }
        }
    }

    fn view(&self) -> Column<Message> {
        column![
            text_input("", &self.input)
                .id("some-id")
                .on_input(Message::InputUpdated),
            button("Focus On Input").on_press(Message::FocusInput),
        ]
    }
}

If you look at the code, iced::widget::text_input::focus uses iced::advanced::widget::operation::focusable::focus and just wraps the Operation with a Task, which you need to return from update for it to actually have any effect.

iced::advanced::widget::operation::focusable::find_focused is analog to iced::widget::text_input::focus as it also returns an Operation, but it has no counterpart that wraps it in a Task that you can use.

As I see it, find_focused is not usable by the user and I don’t know why it’s public.

You can set the id of the text_input when you create them. Here’s a sample snippet:

use iced::widget::{
    button, column, container, focus_next, focus_previous, horizontal_rule,
    horizontal_space, row, text, text_input,
};
use iced::{self, Element, Length, Task};

fn main() -> iced::Result {
    iced::application("iced - focus example", Focus::update, Focus::view).run()
}


#[derive(Default, Debug)]
pub struct Focus {
    value: String,
}

#[derive(Debug, Clone)]
pub enum Message {
    Input(String),
    SubmitFirst,
    SubmitSecond,
    Focus(&'static str),
}

impl Focus {
    pub fn update(&mut self, message: Message) -> Task<Message> {
        match message {
            Message::Input(text) => {
                self.value = text;
                Task::none()
            }
            Message::SubmitFirst => focus_next(),
            Message::SubmitSecond => focus_previous(),
            Message::Focus(id) => text_input::focus(id),
        }
    }

    pub fn view<'a>(&'a self) -> Element<'a, Message> {
        container(
            column![
                text("Hitting Enter to submit will focus the next input field"),
                text_input("First input", &self.value)
                    .id("A")
                    .on_input(Message::Input)
                    .on_submit(Message::SubmitFirst).width(Length::Fixed(200.0)),
                text_input("Second input", &self.value)
                    .id("B")
                    .on_input(Message::Input)
                    .on_submit(Message::SubmitSecond).width(Length::Fixed(200.0)),
                horizontal_rule(1),
                text("Alternatively, click on either button to focus the respective input field"),
                row![
                    button("First").on_press(Message::Focus("A")),
                    horizontal_space(),
                    button("Second").on_press(Message::Focus("B"))],
            ]
            .width(Length::Fixed(500.0))
            .spacing(10),
        )
        .center(Length::Fill)
        .into()
    }
}

Thanks @airstrike for your example. Unfortunately, this is not exactly what I need. I don’t want change the focus state of a widget but check whether the user has focused one item of the UI.

@lufte: This was a good hint. I started to look a bit at the code behind the scences and it seems like that not only find_focused() cannot be used by the user but that no other internal code of the crate is using it.
Maybe when I have some time, I will try to come up with a wrapping function that enables find_focused() for text_input.
The idea would be that find_focus()would work similarly to focus()shown in @airstrike `s example.

1 Like

If you want to listen to focus events on text inputs, check the additions I’ve made to my fork of iced for some inspiration. You’re free to use it.

1 Like

Just for your information: I was able to add the functionality for text_inputand have create a PR.

2 Likes