diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs index 40f01064..17948599 100644 --- a/core/src/widget/operation.rs +++ b/core/src/widget/operation.rs @@ -9,7 +9,7 @@ pub use scrollable::Scrollable; pub use text_input::TextInput; use crate::widget::Id; -use crate::{Rectangle, Vector}; +use crate::{Rectangle, Vector, window}; use std::any::Any; use std::fmt; @@ -75,6 +75,9 @@ pub trait Operation: Send { fn finish(&self) -> Outcome { Outcome::None } + + /// Track the active window id being processed if relevant to the operation + fn set_window_id(&mut self, _id: window::Id) {} } impl Operation for Box @@ -140,6 +143,10 @@ where fn finish(&self) -> Outcome { self.as_ref().finish() } + + fn set_window_id(&mut self, id: window::Id) { + self.as_mut().set_window_id(id); + } } /// The result of an [`Operation`]. @@ -243,6 +250,10 @@ where fn finish(&self) -> Outcome { Outcome::None } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } } BlackBox { operation } @@ -410,6 +421,10 @@ where })), } } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } } Map { @@ -515,6 +530,10 @@ where } } } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } } Chain { @@ -566,6 +585,10 @@ pub fn scoped( outcome => outcome, } } + + fn set_window_id(&mut self, id: window::Id) { + self.operation.set_window_id(id); + } } ScopedOperation { diff --git a/runtime/src/widget/selector.rs b/runtime/src/widget/selector.rs index 77d4eb2d..82d7e838 100644 --- a/runtime/src/widget/selector.rs +++ b/runtime/src/widget/selector.rs @@ -1,6 +1,6 @@ //! Find and query widgets in your applications. pub use iced_selector::{ - Bounded, Candidate, Selector, Target, Text, id, is_focused, + Bounded, Candidate, Selector, Target, Text, focus, id, is_focused, }; use crate::Task; diff --git a/selector/src/find.rs b/selector/src/find.rs index cfdba7b3..06f85140 100644 --- a/selector/src/find.rs +++ b/selector/src/find.rs @@ -1,3 +1,5 @@ +use iced_core::window; + use crate::Selector; use crate::core::widget::operation::{ Focusable, Outcome, Scrollable, TextInput, @@ -100,6 +102,10 @@ where fn finish(&self) -> Self::Output { self.outputs.clone() } + + fn set_window_id(&mut self, id: window::Id) { + self.selector.set_window_id(id); + } } pub trait Strategy { @@ -110,6 +116,8 @@ pub trait Strategy { fn is_done(&self) -> bool; fn finish(&self) -> Self::Output; + + fn set_window_id(&mut self, _id: window::Id) {} } #[derive(Debug)] @@ -273,4 +281,8 @@ where fn finish(&self) -> Outcome { Outcome::Some(self.strategy.finish()) } + + fn set_window_id(&mut self, id: window::Id) { + self.strategy.set_window_id(id); + } } diff --git a/selector/src/lib.rs b/selector/src/lib.rs index 5c377276..99809a78 100644 --- a/selector/src/lib.rs +++ b/selector/src/lib.rs @@ -1,5 +1,6 @@ //! Select data from the widget tree. use iced_core as core; +use iced_core::window; mod find; mod target; @@ -48,6 +49,8 @@ pub trait Selector { { FindAll::new(find::All::new(self)) } + + fn set_window_id(&mut self, _id: window::Id) {} } impl Selector for &str { @@ -171,3 +174,40 @@ pub fn is_focused() -> impl Selector { IsFocused } + +/// Returns a [`Selector`] that matches widgets that are focusable. +pub fn focus() -> impl Selector { + struct Focus { + cur_window_id: window::Id, + } + + impl Selector for Focus { + type Output = (bool, Target, window::Id); + + fn select(&mut self, candidate: Candidate<'_>) -> Option { + if let Candidate::Focusable { state, .. } = candidate { + Some(( + state.is_focused(), + Target::from(candidate), + self.cur_window_id, + )) + } else if matches!(candidate, Candidate::Scrollable { .. }) { + Some((false, Target::from(candidate), self.cur_window_id)) + } else { + None + } + } + + fn description(&self) -> String { + "is focusable and focused".to_owned() + } + + fn set_window_id(&mut self, id: iced_core::window::Id) { + self.cur_window_id = id; + } + } + + Focus { + cur_window_id: window::Id::NONE, + } +} diff --git a/widget/src/button.rs b/widget/src/button.rs index f137221e..a99dd9e2 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -267,6 +267,7 @@ struct State { is_hovered: bool, is_pressed: bool, is_focused: bool, + needs_redraw: bool, } impl<'a, Message, Theme, Renderer> Widget @@ -368,6 +369,12 @@ where viewport, ); + let state = tree.state.downcast_mut::(); + if state.needs_redraw { + state.needs_redraw = false; + shell.request_redraw(); + } + if shell.is_event_captured() { return; } @@ -777,11 +784,11 @@ impl Default for Style { /// /// impl Catalog for MyTheme { /// type Class<'a> = ButtonClass; -/// +/// /// fn default<'a>() -> Self::Class<'a> { /// ButtonClass::default() /// } -/// +/// /// /// fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { /// let mut style = Style::default(); @@ -1000,10 +1007,12 @@ impl operation::Focusable for State { } fn focus(&mut self) { + self.needs_redraw |= !self.is_focused; self.is_focused = true; } fn unfocus(&mut self) { + self.needs_redraw |= self.is_focused; self.is_focused = false; } } diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 61a18bd5..8065bb8a 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -676,14 +676,6 @@ where let translation = state.translation(self.direction, bounds, content_bounds); - operation.scrollable( - Some(&self.id), - bounds, - content_bounds, - translation, - state, - ); - operation.traverse(&mut |operation| { self.content.as_widget_mut().operate( &mut tree.children[0], @@ -696,6 +688,14 @@ where operation, ); }); + // XXX must be done after traversing to perform DFS + operation.scrollable( + Some(&self.id), + bounds, + content_bounds, + translation, + state, + ); } fn update( diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 21a2b38f..791bacff 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -2519,6 +2519,8 @@ where while let Some(mut operation) = current_operation.take() { for (id, ui) in interfaces.iter_mut() { if let Some(window) = window_manager.get_mut(*id) { + operation.as_mut().set_window_id(*id); + ui.operate(&window.renderer, operation.as_mut()); } }