improv: operation and select for directional navigation
This commit is contained in:
parent
d0f221c48b
commit
880f2c7565
7 changed files with 98 additions and 12 deletions
|
|
@ -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<T = ()>: Send {
|
|||
fn finish(&self) -> Outcome<T> {
|
||||
Outcome::None
|
||||
}
|
||||
|
||||
/// Track the active window id being processed if relevant to the operation
|
||||
fn set_window_id(&mut self, _id: window::Id) {}
|
||||
}
|
||||
|
||||
impl<T, O> Operation<O> for Box<T>
|
||||
|
|
@ -140,6 +143,10 @@ where
|
|||
fn finish(&self) -> Outcome<O> {
|
||||
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<O> {
|
||||
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<T: 'static>(
|
|||
outcome => outcome,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_window_id(&mut self, id: window::Id) {
|
||||
self.operation.set_window_id(id);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedOperation {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<S::Output> {
|
||||
Outcome::Some(self.strategy.finish())
|
||||
}
|
||||
|
||||
fn set_window_id(&mut self, id: window::Id) {
|
||||
self.strategy.set_window_id(id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Output = Target> {
|
|||
|
||||
IsFocused
|
||||
}
|
||||
|
||||
/// Returns a [`Selector`] that matches widgets that are focusable.
|
||||
pub fn focus() -> impl Selector<Output = (bool, Target, window::Id)> {
|
||||
struct Focus {
|
||||
cur_window_id: window::Id,
|
||||
}
|
||||
|
||||
impl Selector for Focus {
|
||||
type Output = (bool, Target, window::Id);
|
||||
|
||||
fn select(&mut self, candidate: Candidate<'_>) -> Option<Self::Output> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ struct State {
|
|||
is_hovered: bool,
|
||||
is_pressed: bool,
|
||||
is_focused: bool,
|
||||
needs_redraw: bool,
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||
|
|
@ -368,6 +369,12 @@ where
|
|||
viewport,
|
||||
);
|
||||
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue