diff options
author | 2022-08-04 03:55:41 +0200 | |
---|---|---|
committer | 2022-08-04 03:55:41 +0200 | |
commit | 13dd1ca0a83cc95eea52e2106da9dc1ee1f37958 (patch) | |
tree | 4b142110e23fe45b8d5d21935034c951f548d7e8 /native/src | |
parent | 6eb3dd7e5edc8847875c288c41d1dec8b1dad06e (diff) | |
download | iced-13dd1ca0a83cc95eea52e2106da9dc1ee1f37958.tar.gz iced-13dd1ca0a83cc95eea52e2106da9dc1ee1f37958.tar.bz2 iced-13dd1ca0a83cc95eea52e2106da9dc1ee1f37958.zip |
Implement `scrollable::snap_to` operation
Diffstat (limited to 'native/src')
-rw-r--r-- | native/src/element.rs | 2 | ||||
-rw-r--r-- | native/src/widget.rs | 1 | ||||
-rw-r--r-- | native/src/widget/action.rs | 10 | ||||
-rw-r--r-- | native/src/widget/operation.rs | 173 | ||||
-rw-r--r-- | native/src/widget/operation/focusable.rs | 149 | ||||
-rw-r--r-- | native/src/widget/operation/scrollable.rs | 30 | ||||
-rw-r--r-- | native/src/widget/scrollable.rs | 42 | ||||
-rw-r--r-- | native/src/widget/state.rs | 6 | ||||
-rw-r--r-- | native/src/widget/text_input.rs | 5 |
9 files changed, 238 insertions, 180 deletions
diff --git a/native/src/element.rs b/native/src/element.rs index 01b71aa4..8b994d73 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -274,7 +274,7 @@ where fn focusable( &mut self, - state: &mut dyn widget::state::Focusable, + state: &mut dyn widget::operation::Focusable, id: Option<&widget::Id>, ) { self.operation.focusable(state, id); diff --git a/native/src/widget.rs b/native/src/widget.rs index 56ba28c8..8890b8e7 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -27,7 +27,6 @@ pub mod rule; pub mod scrollable; pub mod slider; pub mod space; -pub mod state; pub mod svg; pub mod text; pub mod text_input; diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs index 69723358..21032dbb 100644 --- a/native/src/widget/action.rs +++ b/native/src/widget/action.rs @@ -1,5 +1,5 @@ -use crate::widget::state; -use crate::widget::{Id, Operation}; +use crate::widget::operation::{self, Operation}; +use crate::widget::Id; use iced_futures::MaybeSend; @@ -72,7 +72,11 @@ where .container(id, operate_on_children); } - fn focusable(&mut self, state: &mut dyn state::Focusable, id: Option<&Id>) { + fn focusable( + &mut self, + state: &mut dyn operation::Focusable, + id: Option<&Id>, + ) { self.operation.focusable(state, id); } } diff --git a/native/src/widget/operation.rs b/native/src/widget/operation.rs index caf7ba1c..4a075da9 100644 --- a/native/src/widget/operation.rs +++ b/native/src/widget/operation.rs @@ -1,4 +1,9 @@ -use crate::widget::state; +pub mod focusable; +pub mod scrollable; + +pub use focusable::Focusable; +pub use scrollable::Scrollable; + use crate::widget::Id; pub trait Operation<T> { @@ -8,12 +13,9 @@ pub trait Operation<T> { operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ); - fn focusable( - &mut self, - _state: &mut dyn state::Focusable, - _id: Option<&Id>, - ) { - } + fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {} + + fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {} fn finish(&self) -> Outcome<T> { Outcome::None @@ -25,160 +27,3 @@ pub enum Outcome<T> { Some(T), Chain(Box<dyn Operation<T>>), } - -pub fn focus<T>(target: Id) -> impl Operation<T> { - struct Focus { - target: Id, - } - - impl<T> Operation<T> for Focus { - fn focusable( - &mut self, - state: &mut dyn state::Focusable, - id: Option<&Id>, - ) { - match id { - Some(id) if id == &self.target => { - state.focus(); - } - _ => { - state.unfocus(); - } - } - } - - fn container( - &mut self, - _id: Option<&Id>, - operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), - ) { - operate_on_children(self) - } - } - - Focus { target } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct FocusCount { - focused: Option<usize>, - total: usize, -} - -pub fn count_focusable<T, O>(f: fn(FocusCount) -> O) -> impl Operation<T> -where - O: Operation<T> + 'static, -{ - struct CountFocusable<O> { - count: FocusCount, - next: fn(FocusCount) -> O, - } - - impl<T, O> Operation<T> for CountFocusable<O> - where - O: Operation<T> + 'static, - { - fn focusable( - &mut self, - state: &mut dyn state::Focusable, - _id: Option<&Id>, - ) { - if state.is_focused() { - self.count.focused = Some(self.count.total); - } - - self.count.total += 1; - } - - fn container( - &mut self, - _id: Option<&Id>, - operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), - ) { - operate_on_children(self) - } - - fn finish(&self) -> Outcome<T> { - Outcome::Chain(Box::new((self.next)(self.count))) - } - } - - CountFocusable { - count: FocusCount::default(), - next: f, - } -} - -pub fn focus_previous<T>() -> impl Operation<T> { - struct FocusPrevious { - count: FocusCount, - current: usize, - } - - impl<T> Operation<T> for FocusPrevious { - fn focusable( - &mut self, - state: &mut dyn state::Focusable, - _id: Option<&Id>, - ) { - if self.count.total == 0 { - return; - } - - match self.count.focused { - None if self.current == self.count.total - 1 => state.focus(), - Some(0) if self.current == 0 => state.unfocus(), - Some(0) => {} - Some(focused) if focused == self.current => state.unfocus(), - Some(focused) if focused - 1 == self.current => state.focus(), - _ => {} - } - - self.current += 1; - } - - fn container( - &mut self, - _id: Option<&Id>, - operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), - ) { - operate_on_children(self) - } - } - - count_focusable(|count| FocusPrevious { count, current: 0 }) -} - -pub fn focus_next<T>() -> impl Operation<T> { - struct FocusNext { - count: FocusCount, - current: usize, - } - - impl<T> Operation<T> for FocusNext { - fn focusable( - &mut self, - state: &mut dyn state::Focusable, - _id: Option<&Id>, - ) { - match self.count.focused { - None if self.current == 0 => state.focus(), - Some(focused) if focused == self.current => state.unfocus(), - Some(focused) if focused + 1 == self.current => state.focus(), - _ => {} - } - - self.current += 1; - } - - fn container( - &mut self, - _id: Option<&Id>, - operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), - ) { - operate_on_children(self) - } - } - - count_focusable(|count| FocusNext { count, current: 0 }) -} diff --git a/native/src/widget/operation/focusable.rs b/native/src/widget/operation/focusable.rs new file mode 100644 index 00000000..20a73291 --- /dev/null +++ b/native/src/widget/operation/focusable.rs @@ -0,0 +1,149 @@ +use crate::widget::operation::{Operation, Outcome}; +use crate::widget::Id; + +pub trait Focusable { + fn is_focused(&self) -> bool; + fn focus(&mut self); + fn unfocus(&mut self); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct Count { + focused: Option<usize>, + total: usize, +} + +pub fn focus<T>(target: Id) -> impl Operation<T> { + struct Focus { + target: Id, + } + + impl<T> Operation<T> for Focus { + fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { + match id { + Some(id) if id == &self.target => { + state.focus(); + } + _ => { + state.unfocus(); + } + } + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), + ) { + operate_on_children(self) + } + } + + Focus { target } +} + +pub fn count<T, O>(f: fn(Count) -> O) -> impl Operation<T> +where + O: Operation<T> + 'static, +{ + struct CountFocusable<O> { + count: Count, + next: fn(Count) -> O, + } + + impl<T, O> Operation<T> for CountFocusable<O> + where + O: Operation<T> + 'static, + { + fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) { + if state.is_focused() { + self.count.focused = Some(self.count.total); + } + + self.count.total += 1; + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), + ) { + operate_on_children(self) + } + + fn finish(&self) -> Outcome<T> { + Outcome::Chain(Box::new((self.next)(self.count))) + } + } + + CountFocusable { + count: Count::default(), + next: f, + } +} + +pub fn focus_previous<T>() -> impl Operation<T> { + struct FocusPrevious { + count: Count, + current: usize, + } + + impl<T> Operation<T> for FocusPrevious { + fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) { + if self.count.total == 0 { + return; + } + + match self.count.focused { + None if self.current == self.count.total - 1 => state.focus(), + Some(0) if self.current == 0 => state.unfocus(), + Some(0) => {} + Some(focused) if focused == self.current => state.unfocus(), + Some(focused) if focused - 1 == self.current => state.focus(), + _ => {} + } + + self.current += 1; + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), + ) { + operate_on_children(self) + } + } + + count(|count| FocusPrevious { count, current: 0 }) +} + +pub fn focus_next<T>() -> impl Operation<T> { + struct FocusNext { + count: Count, + current: usize, + } + + impl<T> Operation<T> for FocusNext { + fn focusable(&mut self, state: &mut dyn Focusable, _id: Option<&Id>) { + match self.count.focused { + None if self.current == 0 => state.focus(), + Some(focused) if focused == self.current => state.unfocus(), + Some(focused) if focused + 1 == self.current => state.focus(), + _ => {} + } + + self.current += 1; + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), + ) { + operate_on_children(self) + } + } + + count(|count| FocusNext { count, current: 0 }) +} diff --git a/native/src/widget/operation/scrollable.rs b/native/src/widget/operation/scrollable.rs new file mode 100644 index 00000000..ed609d67 --- /dev/null +++ b/native/src/widget/operation/scrollable.rs @@ -0,0 +1,30 @@ +use crate::widget::{Id, Operation}; + +pub trait Scrollable { + fn snap_to(&mut self, percentage: f32); +} + +pub fn snap_to<T>(target: Id, percentage: f32) -> impl Operation<T> { + struct SnapTo { + target: Id, + percentage: f32, + } + + impl<T> Operation<T> for SnapTo { + fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { + if Some(&self.target) == id { + state.snap_to(self.percentage); + } + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), + ) { + operate_on_children(self) + } + } + + SnapTo { target, percentage } +} diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 91c13eb5..b7a0b6ee 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -5,11 +5,12 @@ use crate::mouse; use crate::overlay; use crate::renderer; use crate::touch; +use crate::widget; +use crate::widget::operation::{self, Operation}; use crate::widget::tree::{self, Tree}; -use crate::widget::Operation; use crate::{ - Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, - Shell, Size, Vector, Widget, + Background, Clipboard, Color, Command, Element, Layout, Length, Point, + Rectangle, Shell, Size, Vector, Widget, }; use std::{f32, u32}; @@ -31,6 +32,7 @@ where Renderer: crate::Renderer, Renderer::Theme: StyleSheet, { + id: Option<Id>, height: Length, scrollbar_width: u16, scrollbar_margin: u16, @@ -48,6 +50,7 @@ where /// Creates a new [`Scrollable`]. pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self { Scrollable { + id: None, height: Length::Shrink, scrollbar_width: 10, scrollbar_margin: 0, @@ -58,6 +61,12 @@ where } } + /// Sets the [`Id`] of the [`Scrollable`]. + pub fn id(mut self, id: Id) -> Self { + self.id = Some(id); + self + } + /// Sets the height of the [`Scrollable`]. pub fn height(mut self, height: Length) -> Self { self.height = height; @@ -157,6 +166,10 @@ where layout: Layout<'_>, operation: &mut dyn Operation<Message>, ) { + let state = tree.state.downcast_mut::<State>(); + + operation.scrollable(state, self.id.as_ref().map(|id| &id.0)); + operation.container(None, &mut |operation| { self.content.as_widget().operate( &mut tree.children[0], @@ -303,6 +316,23 @@ where } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Id(widget::Id); + +impl Id { + pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self { + Self(widget::Id::new(id)) + } + + pub fn unique() -> Self { + Self(widget::Id::unique()) + } +} + +pub fn snap_to<Message: 'static>(id: Id, percentage: f32) -> Command<Message> { + Command::widget(operation::scrollable::snap_to(id.0, percentage)) +} + /// Computes the layout of a [`Scrollable`]. pub fn layout<Renderer>( renderer: &Renderer, @@ -790,6 +820,12 @@ impl Default for State { } } +impl operation::Scrollable for State { + fn snap_to(&mut self, percentage: f32) { + State::snap_to(self, percentage); + } +} + /// The local state of a [`Scrollable`]. #[derive(Debug, Clone, Copy)] enum Offset { diff --git a/native/src/widget/state.rs b/native/src/widget/state.rs index d1984a71..8b137891 100644 --- a/native/src/widget/state.rs +++ b/native/src/widget/state.rs @@ -1,5 +1 @@ -pub trait Focusable { - fn is_focused(&self) -> bool; - fn focus(&mut self); - fn unfocus(&mut self); -} + diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index a81cfaed..e0216a5b 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -21,7 +21,6 @@ use crate::text::{self, Text}; use crate::touch; use crate::widget; use crate::widget::operation::{self, Operation}; -use crate::widget::state; use crate::widget::tree::{self, Tree}; use crate::{ Clipboard, Color, Command, Element, Layout, Length, Padding, Point, @@ -329,7 +328,7 @@ impl Id { } pub fn focus<Message: 'static>(id: Id) -> Command<Message> { - Command::widget(operation::focus(id.0)) + Command::widget(operation::focusable::focus(id.0)) } /// Computes the layout of a [`TextInput`]. @@ -982,7 +981,7 @@ impl State { } } -impl state::Focusable for State { +impl operation::Focusable for State { fn is_focused(&self) -> bool { State::is_focused(self) } |