From 1ae3b5e96a09d32e28d9506082ede9769e74cb0f Mon Sep 17 00:00:00 2001 From: Leonie Theobald Date: Tue, 5 Nov 2024 14:06:36 +0100 Subject: [PATCH 1/3] Implement `is_focused` selector and unify `selector` API --- examples/delineate/src/main.rs | 14 ++++++++------ runtime/src/widget/selector.rs | 33 +++++++++++++++------------------ selector/src/lib.rs | 25 +++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/examples/delineate/src/main.rs b/examples/delineate/src/main.rs index 97bd7d62..d4bc8a9a 100644 --- a/examples/delineate/src/main.rs +++ b/examples/delineate/src/main.rs @@ -28,8 +28,8 @@ enum Message { MouseMoved(Point), WindowResized, Scrolled, - OuterFound(Option), - InnerFound(Option), + OuterFound(Option), + InnerFound(Option), } impl Example { @@ -41,16 +41,18 @@ impl Example { Task::none() } Message::Scrolled | Message::WindowResized => Task::batch(vec![ - selector::delineate(OUTER_CONTAINER).map(Message::OuterFound), - selector::delineate(INNER_CONTAINER).map(Message::InnerFound), + selector::find(OUTER_CONTAINER).map(Message::OuterFound), + selector::find(INNER_CONTAINER).map(Message::InnerFound), ]), Message::OuterFound(outer) => { - self.outer_bounds = outer; + self.outer_bounds = + outer.as_ref().and_then(selector::Target::visible_bounds); Task::none() } Message::InnerFound(inner) => { - self.inner_bounds = inner; + self.inner_bounds = + inner.as_ref().and_then(selector::Target::visible_bounds); Task::none() } diff --git a/runtime/src/widget/selector.rs b/runtime/src/widget/selector.rs index a6f306c5..77d4eb2d 100644 --- a/runtime/src/widget/selector.rs +++ b/runtime/src/widget/selector.rs @@ -1,28 +1,25 @@ //! Find and query widgets in your applications. -pub use iced_selector::{Bounded, Candidate, Selector, Target, Text}; - -use crate::core::Rectangle; +pub use iced_selector::{ + Bounded, Candidate, Selector, Target, Text, id, is_focused, +}; use crate::Task; -use crate::core::widget; use crate::task; -/// Finds a widget by the given [`widget::Id`]. -pub fn find_by_id(id: impl Into) -> Task> { - task::widget(id.into().find()) -} - -/// Finds a widget that contains the given text. -pub fn find_by_text(text: impl Into) -> Task> { - task::widget(Selector::find(text.into())) -} - -/// Finds the visible bounds of the first [`Selector`] target. -pub fn delineate(selector: S) -> Task> +/// Finds a widget matching the given [`Selector`]. +pub fn find(selector: S) -> Task> where S: Selector + Send + 'static, - S::Output: Bounded + Clone + Send + 'static, + S::Output: Send + Clone + 'static, { task::widget(selector.find()) - .map(|target| target.as_ref().and_then(Bounded::visible_bounds)) +} + +/// Finds all widgets matching the given [`Selector`]. +pub fn find_all(selector: S) -> Task> +where + S: Selector + Send + 'static, + S::Output: Send + Clone + 'static, +{ + task::widget(selector.find_all()) } diff --git a/selector/src/lib.rs b/selector/src/lib.rs index e5703df5..1f90ef8e 100644 --- a/selector/src/lib.rs +++ b/selector/src/lib.rs @@ -146,3 +146,28 @@ where pub fn id(id: impl Into) -> impl Selector { id.into() } + +/// Returns a [`Selector`] that matches widgets that are currently focused. +pub fn is_focused() -> impl Selector { + struct IsFocused; + + impl Selector for IsFocused { + type Output = Target; + + fn select(&mut self, candidate: Candidate<'_>) -> Option { + if let Candidate::Focusable { state, .. } = candidate + && state.is_focused() + { + Some(Target::from(candidate)) + } else { + None + } + } + + fn description(&self) -> String { + "is focused".to_string() + } + } + + IsFocused +} From 85032784924b7aa2c0e41745d0227ba8ea8a6a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 18 Nov 2025 23:05:30 +0100 Subject: [PATCH 2/3] Use `to_owned` instead of `to_string` in `iced_selector` --- selector/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selector/src/lib.rs b/selector/src/lib.rs index 1f90ef8e..5c377276 100644 --- a/selector/src/lib.rs +++ b/selector/src/lib.rs @@ -165,7 +165,7 @@ pub fn is_focused() -> impl Selector { } fn description(&self) -> String { - "is focused".to_string() + "is focused".to_owned() } } From f1b0d558df1d1bef04e0babd06de46b6fa236944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 18 Nov 2025 23:15:47 +0100 Subject: [PATCH 3/3] Fix broken `find_by_id` link in documentation --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b3933670..18f08a36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -327,7 +327,7 @@ //! Tasks can also be used to interact with the iced runtime. Some modules //! expose functions that create tasks for different purposes—like [changing //! window settings](window#functions), [focusing a widget](widget::operation::focus_next), or -//! [querying its visible bounds](widget::selector::find_by_id). +//! [querying its visible bounds](widget::selector::find). //! //! Like futures and streams, tasks expose [a monadic interface](Task::then)—but they can also be //! [mapped](Task::map), [chained](Task::chain), [batched](Task::batch), [canceled](Task::abortable),