diff options
| author | pml68 <contact@pml68.dev> | 2025-04-13 03:40:38 +0200 |
|---|---|---|
| committer | pml68 <contact@pml68.dev> | 2025-04-15 23:52:42 +0200 |
| commit | 495985f449e46b24e6b734d3aa9e135a779a8b77 (patch) | |
| tree | f2908b3a1776458e81de63c6d2461b9fc4cec13f /iced_drop/src | |
| parent | feat(material_theme): implement `pick_list::Catalog` (diff) | |
| download | iced-builder-495985f449e46b24e6b734d3aa9e135a779a8b77.tar.gz | |
refactor: move `material_theme` and `iced_drop` into separate crates dir
Diffstat (limited to 'iced_drop/src')
| -rw-r--r-- | iced_drop/src/lib.rs | 55 | ||||
| -rw-r--r-- | iced_drop/src/widget.rs | 2 | ||||
| -rw-r--r-- | iced_drop/src/widget/droppable.rs | 574 | ||||
| -rw-r--r-- | iced_drop/src/widget/operation.rs | 1 | ||||
| -rw-r--r-- | iced_drop/src/widget/operation/drop.rs | 88 |
5 files changed, 0 insertions, 720 deletions
diff --git a/iced_drop/src/lib.rs b/iced_drop/src/lib.rs deleted file mode 100644 index c1e1b03..0000000 --- a/iced_drop/src/lib.rs +++ /dev/null @@ -1,55 +0,0 @@ -pub mod widget; - -use iced::advanced::graphics::futures::MaybeSend; -use iced::advanced::renderer; -use iced::advanced::widget::{operate, Id}; -use iced::task::Task; -use iced::{Element, Point, Rectangle}; -use widget::droppable::*; -use widget::operation::drop; - -pub fn droppable<'a, Message, Theme, Renderer>( - content: impl Into<Element<'a, Message, Theme, Renderer>>, -) -> Droppable<'a, Message, Theme, Renderer> -where - Message: Clone, - Renderer: renderer::Renderer, -{ - Droppable::new(content) -} - -pub fn zones_on_point<T, MF>( - msg: MF, - point: Point, - options: Option<Vec<Id>>, - depth: Option<usize>, -) -> Task<T> -where - T: Send + 'static, - MF: Fn(Vec<(Id, Rectangle)>) -> T + MaybeSend + Sync + Clone + 'static, -{ - operate(drop::find_zones( - move |bounds| bounds.contains(point), - options, - depth, - )) - .map(move |id| msg(id)) -} - -pub fn find_zones<Message, MF, F>( - msg: MF, - filter: F, - options: Option<Vec<Id>>, - depth: Option<usize>, -) -> Task<Message> -where - Message: Send + 'static, - MF: Fn(Vec<(Id, Rectangle)>) -> Message - + MaybeSend - + Sync - + Clone - + 'static, - F: Fn(&Rectangle) -> bool + Send + 'static, -{ - operate(drop::find_zones(filter, options, depth)).map(move |id| msg(id)) -} diff --git a/iced_drop/src/widget.rs b/iced_drop/src/widget.rs deleted file mode 100644 index 6b3fed2..0000000 --- a/iced_drop/src/widget.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod droppable; -pub mod operation; diff --git a/iced_drop/src/widget/droppable.rs b/iced_drop/src/widget/droppable.rs deleted file mode 100644 index 947cf5b..0000000 --- a/iced_drop/src/widget/droppable.rs +++ /dev/null @@ -1,574 +0,0 @@ -//! Encapsulates a widget that can be dragged and dropped. -use std::fmt::Debug; -use std::vec; - -use iced::advanced::widget::{Operation, Tree, Widget}; -use iced::advanced::{self, Layout, layout, mouse, overlay, renderer}; -use iced::{Element, Point, Rectangle, Size, Vector}; - -/// An element that can be dragged and dropped on a [`DropZone`] -pub struct Droppable< - 'a, - Message, - Theme = iced::Theme, - Renderer = iced::Renderer, -> where - Message: Clone, - Renderer: renderer::Renderer, -{ - content: Element<'a, Message, Theme, Renderer>, - id: Option<iced::advanced::widget::Id>, - on_click: Option<Message>, - on_drop: Option<Box<dyn Fn(Point, Rectangle) -> Message + 'a>>, - on_drag: Option<Box<dyn Fn(Point, Rectangle) -> Message + 'a>>, - on_cancel: Option<Message>, - drag_mode: Option<(bool, bool)>, - drag_overlay: bool, - drag_hide: bool, - drag_center: bool, - drag_size: Option<Size>, - reset_delay: usize, - status: Option<Status>, -} - -impl<'a, Message, Theme, Renderer> Droppable<'a, Message, Theme, Renderer> -where - Message: Clone, - Renderer: renderer::Renderer, -{ - /// Creates a new [`Droppable`]. - pub fn new( - content: impl Into<Element<'a, Message, Theme, Renderer>>, - ) -> Self { - Self { - content: content.into(), - id: None, - on_click: None, - on_drop: None, - on_drag: None, - on_cancel: None, - drag_mode: Some((true, true)), - drag_overlay: true, - drag_hide: false, - drag_center: false, - drag_size: None, - reset_delay: 0, - status: None, - } - } - - /// Sets the unique identifier of the [`Droppable`]. - pub fn id(mut self, id: iced::advanced::widget::Id) -> Self { - self.id = Some(id); - self - } - - /// Sets the message that will be produced when the [`Droppable`] is clicked. - pub fn on_click(mut self, message: Message) -> Self { - self.on_click = Some(message); - self - } - - /// Sets the message that will be produced when the [`Droppable`] is dropped on a [`DropZone`]. - /// - /// Unless this is set, the [`Droppable`] will be disabled. - pub fn on_drop<F>(mut self, message: F) -> Self - where - F: Fn(Point, Rectangle) -> Message + 'a, - { - self.on_drop = Some(Box::new(message)); - self - } - - /// Sets the message that will be produced when the [`Droppable`] is dragged. - pub fn on_drag<F>(mut self, message: F) -> Self - where - F: Fn(Point, Rectangle) -> Message + 'a, - { - self.on_drag = Some(Box::new(message)); - self - } - - /// Sets the message that will be produced when the user right clicks while dragging the [`Droppable`]. - pub fn on_cancel(mut self, message: Message) -> Self { - self.on_cancel = Some(message); - self - } - - /// Sets whether the [`Droppable`] should be drawn under the cursor while dragging. - pub fn drag_overlay(mut self, drag_overlay: bool) -> Self { - self.drag_overlay = drag_overlay; - self - } - - /// Sets whether the [`Droppable`] should be hidden while dragging. - pub fn drag_hide(mut self, drag_hide: bool) -> Self { - self.drag_hide = drag_hide; - self - } - - /// Sets whether the [`Droppable`] should be centered on the cursor while dragging. - pub fn drag_center(mut self, drag_center: bool) -> Self { - self.drag_center = drag_center; - self - } - - // Sets whether the [`Droppable`] can be dragged along individual axes. - pub fn drag_mode(mut self, drag_x: bool, drag_y: bool) -> Self { - self.drag_mode = Some((drag_x, drag_y)); - self - } - - /// Sets whether the [`Droppable`] should be be resized to a given size while dragging. - pub fn drag_size(mut self, hide_size: Size) -> Self { - self.drag_size = Some(hide_size); - self - } - - /// Sets the number of frames/layout calls to wait before resetting the size of the [`Droppable`] after dropping. - /// - /// This is useful for cases where the [`Droppable`] is being moved to a new location after some widget operation. - /// In this case, the [`Droppable`] will mainting the 'drag_size' for the given number of frames before resetting to its original size. - /// This prevents the [`Droppable`] from 'jumping' back to its original size before the new location is rendered which - /// prevents flickering. - /// - /// Warning: this should only be set if there's is some noticeble flickering when the [`Droppable`] is dropped. That is, if the - /// [`Droppable`] returns to its original size before it's moved to it's new location. - pub fn reset_delay(mut self, reset_delay: usize) -> Self { - self.reset_delay = reset_delay; - self - } -} - -impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer> - for Droppable<'a, Message, Theme, Renderer> -where - Message: Clone, - Renderer: renderer::Renderer, -{ - fn state(&self) -> iced::advanced::widget::tree::State { - advanced::widget::tree::State::new(State::default()) - } - - fn tag(&self) -> iced::advanced::widget::tree::Tag { - advanced::widget::tree::Tag::of::<State>() - } - - fn children(&self) -> Vec<iced::advanced::widget::Tree> { - vec![advanced::widget::Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut iced::advanced::widget::Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn size(&self) -> iced::Size<iced::Length> { - self.content.as_widget().size() - } - - fn update( - &mut self, - tree: &mut iced::advanced::widget::Tree, - event: &iced::Event, - layout: iced::advanced::Layout<'_>, - cursor: iced::advanced::mouse::Cursor, - _renderer: &Renderer, - _clipboard: &mut dyn iced::advanced::Clipboard, - shell: &mut iced::advanced::Shell<'_, Message>, - _viewport: &iced::Rectangle, - ) { - // handle the on event of the content first, in case that the droppable is nested - self.content.as_widget_mut().update( - &mut tree.children[0], - event, - layout, - cursor, - _renderer, - _clipboard, - shell, - _viewport, - ); - // this should really only be captured if the droppable is nested or it contains some other - // widget that captures the event - if shell.is_event_captured() { - return; - } - - if let Some(on_drop) = self.on_drop.as_deref() { - let state = tree.state.downcast_mut::<State>(); - if let iced::Event::Mouse(mouse) = event { - match mouse { - mouse::Event::ButtonPressed(btn) => { - if *btn == mouse::Button::Left - && cursor.is_over(layout.bounds()) - { - // select the droppable and store the position of the widget before dragging - state.action = - Action::Select(cursor.position().unwrap()); - let bounds = layout.bounds(); - state.widget_pos = bounds.position(); - state.overlay_bounds.width = bounds.width; - state.overlay_bounds.height = bounds.height; - - if let Some(on_click) = self.on_click.clone() { - shell.publish(on_click); - } - shell.capture_event(); - } else if *btn == mouse::Button::Right { - if let Action::Drag(_, _) = state.action { - shell.invalidate_layout(); - state.action = Action::None; - if let Some(on_cancel) = self.on_cancel.clone() - { - shell.publish(on_cancel); - } - } - } - } - mouse::Event::CursorMoved { mut position } => match state - .action - { - Action::Select(start) | Action::Drag(start, _) => { - // calculate the new position of the widget after dragging - - if let Some((drag_x, drag_y)) = self.drag_mode { - position = Point { - x: if drag_x { - position.x - } else { - start.x - }, - y: if drag_y { - position.y - } else { - start.y - }, - }; - } - - state.action = Action::Drag(start, position); - // update the position of the overlay since the cursor was moved - if self.drag_center { - state.overlay_bounds.x = position.x - - state.overlay_bounds.width / 2.0; - state.overlay_bounds.y = position.y - - state.overlay_bounds.height / 2.0; - } else { - state.overlay_bounds.x = - state.widget_pos.x + position.x - start.x; - state.overlay_bounds.y = - state.widget_pos.y + position.y - start.y; - } - // send on drag msg - if let Some(on_drag) = self.on_drag.as_deref() { - let message = - (on_drag)(position, state.overlay_bounds); - shell.publish(message); - } - - shell.request_redraw(); - } - _ => (), - }, - mouse::Event::ButtonReleased(mouse::Button::Left) => { - match state.action { - Action::Select(_) => { - state.action = Action::None; - } - Action::Drag(_, current) => { - // send on drop msg - let message = - (on_drop)(current, state.overlay_bounds); - shell.publish(message); - - if self.reset_delay == 0 { - state.action = Action::None; - } else { - state.action = - Action::Wait(self.reset_delay); - } - } - _ => (), - } - } - _ => {} - } - } - } - - let current_status = if cursor.is_over(layout.bounds()) { - if self.on_drop.is_none() { - Status::Disabled - } else { - let state = tree.state.downcast_ref::<State>(); - - if let Action::Drag(_, _) = state.action { - Status::Dragged - } else { - Status::Hovered - } - } - } else { - Status::Active - }; - - if let iced::Event::Window(iced::window::Event::RedrawRequested(_now)) = - event - { - self.status = Some(current_status); - } else if self.status.is_some_and(|status| status != current_status) { - shell.request_redraw(); - } - } - - fn layout( - &self, - tree: &mut iced::advanced::widget::Tree, - renderer: &Renderer, - limits: &iced::advanced::layout::Limits, - ) -> iced::advanced::layout::Node { - let state: &mut State = tree.state.downcast_mut::<State>(); - let content_node = self.content.as_widget().layout( - &mut tree.children[0], - renderer, - limits, - ); - - // Adjust the size of the original widget if it's being dragged or we're wating to reset the size - if let Some(new_size) = self.drag_size { - match state.action { - Action::Drag(_, _) => { - return iced::advanced::layout::Node::with_children( - new_size, - content_node.children().to_vec(), - ); - } - Action::Wait(reveal_index) => { - if reveal_index <= 1 { - state.action = Action::None; - } else { - state.action = Action::Wait(reveal_index - 1); - } - - return iced::advanced::layout::Node::with_children( - new_size, - content_node.children().to_vec(), - ); - } - _ => (), - } - } - - content_node - } - - fn operate( - &self, - tree: &mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn Operation, - ) { - let state = tree.state.downcast_mut::<State>(); - operation.custom(self.id.as_ref(), layout.bounds(), state); - operation.container( - self.id.as_ref(), - layout.bounds(), - &mut |operation| { - self.content.as_widget().operate( - &mut tree.children[0], - layout, - renderer, - operation, - ); - }, - ); - } - - fn draw( - &self, - tree: &iced::advanced::widget::Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: iced::advanced::Layout<'_>, - cursor: iced::advanced::mouse::Cursor, - viewport: &iced::Rectangle, - ) { - let state: &State = tree.state.downcast_ref::<State>(); - if let Action::Drag(_, _) = state.action { - if self.drag_hide { - return; - } - } - - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - layout, - cursor, - &viewport, - ); - } - - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - _translation: Vector, - ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { - let state: &mut State = tree.state.downcast_mut::<State>(); - if self.drag_overlay { - if let Action::Drag(_, _) = state.action { - return Some(overlay::Element::new(Box::new(Overlay { - content: &self.content, - tree: &mut tree.children[0], - overlay_bounds: state.overlay_bounds, - }))); - } - } - self.content.as_widget_mut().overlay( - &mut tree.children[0], - layout, - renderer, - _translation, - ) - } - - fn mouse_interaction( - &self, - tree: &iced::advanced::widget::Tree, - layout: iced::advanced::Layout<'_>, - cursor: iced::advanced::mouse::Cursor, - _viewport: &iced::Rectangle, - _renderer: &Renderer, - ) -> iced::advanced::mouse::Interaction { - let child_interact = self.content.as_widget().mouse_interaction( - &tree.children[0], - layout, - cursor, - _viewport, - _renderer, - ); - - if child_interact != mouse::Interaction::default() { - return child_interact; - } - - let state = tree.state.downcast_ref::<State>(); - - if self.on_drop.is_none() && cursor.is_over(layout.bounds()) { - return mouse::Interaction::NotAllowed; - } - - if let Action::Drag(_, _) = state.action { - return mouse::Interaction::Grabbing; - } - - if cursor.is_over(layout.bounds()) { - return mouse::Interaction::Pointer; - } - - mouse::Interaction::default() - } -} - -impl<'a, Message, Theme, Renderer> From<Droppable<'a, Message, Theme, Renderer>> - for Element<'a, Message, Theme, Renderer> -where - Message: 'a + Clone, - Theme: 'a, - Renderer: 'a + renderer::Renderer, -{ - fn from( - droppable: Droppable<'a, Message, Theme, Renderer>, - ) -> Element<'a, Message, Theme, Renderer> { - Element::new(droppable) - } -} - -#[derive(Default, Clone, Copy, PartialEq, Debug)] -pub struct State { - widget_pos: Point, - overlay_bounds: Rectangle, - action: Action, -} - -#[derive(Default, Clone, Copy, PartialEq, Debug)] -pub enum Status { - #[default] - Active, - Hovered, - Dragged, - Disabled, -} - -#[derive(Default, Clone, Copy, PartialEq, Debug)] -pub enum Action { - #[default] - None, - /// (point clicked) - Select(Point), - /// (start pos, current pos) - Drag(Point, Point), - /// (frames to wait) - Wait(usize), -} - -struct Overlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - content: &'b Element<'a, Message, Theme, Renderer>, - tree: &'b mut advanced::widget::Tree, - overlay_bounds: Rectangle, -} - -impl<'a, 'b, Message, Theme, Renderer> - overlay::Overlay<Message, Theme, Renderer> - for Overlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: renderer::Renderer, -{ - fn layout(&mut self, renderer: &Renderer, _bounds: Size) -> layout::Node { - Widget::<Message, Theme, Renderer>::layout( - self.content.as_widget(), - self.tree, - renderer, - &layout::Limits::new(Size::ZERO, self.overlay_bounds.size()), - ) - .move_to(self.overlay_bounds.position()) - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - inherited_style: &renderer::Style, - layout: Layout<'_>, - cursor_position: mouse::Cursor, - ) { - Widget::<Message, Theme, Renderer>::draw( - self.content.as_widget(), - self.tree, - renderer, - theme, - inherited_style, - layout, - cursor_position, - &Rectangle::with_size(Size::INFINITY), - ); - } - - fn is_over( - &self, - _layout: Layout<'_>, - _renderer: &Renderer, - _cursor_position: Point, - ) -> bool { - false - } -} diff --git a/iced_drop/src/widget/operation.rs b/iced_drop/src/widget/operation.rs deleted file mode 100644 index 3d7dcff..0000000 --- a/iced_drop/src/widget/operation.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod drop; diff --git a/iced_drop/src/widget/operation/drop.rs b/iced_drop/src/widget/operation/drop.rs deleted file mode 100644 index ead412c..0000000 --- a/iced_drop/src/widget/operation/drop.rs +++ /dev/null @@ -1,88 +0,0 @@ -use iced::advanced::widget::operation::{Outcome, Scrollable}; -use iced::advanced::widget::{Id, Operation}; -use iced::{Rectangle, Vector}; - -/// Produces an [`Operation`] that will find the drop zones that pass a filter on the zone's bounds. -/// For any drop zone to be considered, the Element must have some Id. -/// If `options` is `None`, all drop zones will be considered. -/// Depth determines how how deep into nested drop zones to go. -/// If 'depth' is `None`, nested dropzones will be fully explored -pub fn find_zones<F>( - filter: F, - options: Option<Vec<Id>>, - depth: Option<usize>, -) -> impl Operation<Vec<(Id, Rectangle)>> -where - F: Fn(&Rectangle) -> bool + Send + 'static, -{ - struct FindDropZone<F> { - filter: F, - options: Option<Vec<Id>>, - zones: Vec<(Id, Rectangle)>, - max_depth: Option<usize>, - c_depth: usize, - offset: Vector, - } - - impl<F> Operation<Vec<(Id, Rectangle)>> for FindDropZone<F> - where - F: Fn(&Rectangle) -> bool + Send + 'static, - { - fn container( - &mut self, - id: Option<&Id>, - bounds: iced::Rectangle, - operate_on_children: &mut dyn FnMut( - &mut dyn Operation<Vec<(Id, Rectangle)>>, - ), - ) { - match id { - Some(id) => { - let is_option = match &self.options { - Some(options) => options.contains(id), - None => true, - }; - let bounds = bounds - self.offset; - if is_option && (self.filter)(&bounds) { - self.c_depth += 1; - self.zones.push((id.clone(), bounds)); - } - } - None => (), - } - let goto_next = match &self.max_depth { - Some(m_depth) => self.c_depth < *m_depth, - None => true, - }; - if goto_next { - operate_on_children(self); - } - } - - fn finish(&self) -> Outcome<Vec<(Id, Rectangle)>> { - Outcome::Some(self.zones.clone()) - } - - fn scrollable( - &mut self, - _id: Option<&Id>, - bounds: Rectangle, - _content_bounds: Rectangle, - translation: Vector, - _state: &mut dyn Scrollable, - ) { - if (self.filter)(&bounds) { - self.offset = self.offset + translation; - } - } - } - - FindDropZone { - filter, - options, - zones: vec![], - max_depth: depth, - c_depth: 0, - offset: Vector { x: 0.0, y: 0.0 }, - } -} |
