diff options
| author | pml68 <contact@pml68.dev> | 2025-06-13 00:26:44 +0200 |
|---|---|---|
| committer | pml68 <contact@pml68.dev> | 2025-06-13 00:26:44 +0200 |
| commit | a0501112cf3d74b9c79f7a985283ca2f41f5a7d3 (patch) | |
| tree | 581042549e95602ec495317b260a4c45d965f352 /crates | |
| parent | chore: update deps (diff) | |
| download | iced-builder-a0501112cf3d74b9c79f7a985283ca2f41f5a7d3.tar.gz | |
chore: extract `iced_drop` into separate repo (`pml68/iced_drop`)
Diffstat (limited to '')
| -rw-r--r-- | crates/iced_drop/Cargo.toml | 7 | ||||
| -rw-r--r-- | crates/iced_drop/LICENSE | 21 | ||||
| -rw-r--r-- | crates/iced_drop/README.md | 73 | ||||
| -rw-r--r-- | crates/iced_drop/src/lib.rs | 55 | ||||
| -rw-r--r-- | crates/iced_drop/src/widget.rs | 2 | ||||
| -rw-r--r-- | crates/iced_drop/src/widget/droppable.rs | 565 | ||||
| -rw-r--r-- | crates/iced_drop/src/widget/operation.rs | 1 | ||||
| -rw-r--r-- | crates/iced_drop/src/widget/operation/drop.rs | 88 |
8 files changed, 0 insertions, 812 deletions
diff --git a/crates/iced_drop/Cargo.toml b/crates/iced_drop/Cargo.toml deleted file mode 100644 index 9b20632..0000000 --- a/crates/iced_drop/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "iced_drop" -version = "0.1.0" -edition = "2024" - -[dependencies] -iced.workspace = true diff --git a/crates/iced_drop/LICENSE b/crates/iced_drop/LICENSE deleted file mode 100644 index 89d9fee..0000000 --- a/crates/iced_drop/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 jhannyj - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/crates/iced_drop/README.md b/crates/iced_drop/README.md deleted file mode 100644 index be854f2..0000000 --- a/crates/iced_drop/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# iced_drop - updated to `iced` 0.14-dev - -A small library which provides a custom widget and operation to make drag and drop easier to implement in [iced](https://github.com/iced-rs/iced/tree/master) - -## Usage - -To add drag and drog functionality, first define two messages with the following format - -```rust -enum Message { - Drop(iced::Point, iced::Rectangle) - HandleZones(Vec<(iced::advanced::widget::Id, iced::Rectangle)>) -} -``` - -The `Drop` message will be sent when the droppable is being dragged, and the left mouse button is released. This message provides the mouse position and layout boundaries of the droppable at the release point. - -The `HandleZones` message will be sent after an operation that finds the drop zones under the mouse position. It provides the Id and bounds for each drop zone. - -Next, create create a droppable in the view method and assign the on_drop message. The dropopable function takes an `impl Into<Element>` object, so it's easy to make a droppable from any iced widget. - -```rust -iced_drop::droppable("Drop me!").on_drop(Message::Drop); -``` - -Next, create a "drop zone." A drop zone is any widget that operates like a container andhas some assigned Id. It's important that the widget is assigned some Id or it won't be recognized as a drop zone. - -```rust -iced::widget::container("Drop zone") - .id(iced::widget::container::Id::new("drop_zone")); -``` - -Finally, handle the updates of the drop messages - -```rust -match message { - Message::Drop(cursor_pos, _) => { - return iced_drop::zones_on_point( - Message::HandleZonesFound, - point, - None, - None, - ); - } - Message::HandleZones(zones) => { - println!("{:?}", zones) - } -} -``` - -On Drop, we return a widget operation that looks for drop zones under the cursor_pos. When this operation finishes, it returns the zones found and sends the `HandleZones` message. In this example, we only defined one zone, so the zones vector will either be empty if the droppable was not dropped on the zone, or it will contain the `drop_zone` - -## Examples - -There are two examples: color, todo. - -The color example is a very basic drag/drop showcase where the user can drag colors into zones and change the zone's color. I would start here. - -[Link to video](https://drive.google.com/file/d/1K1CCi2Lc90IUyDufsvoUBZmUCbeg6_Fi/view?usp=sharing) - -To run this examples: `cargo run -p color` - -The todo example is a basic todo board application similar to Trello. This is a much much more complex example as it handles custom highlighting and nested droppables, but it just shows you can make some pretty cool things with iced. - -[Link to video](https://drive.google.com/file/d/1MLOCk4Imd_oUnrTj_psbpYbwua976HmR/view?usp=sharing) - -To run this example try: `cargo run -p todo` - -Note: the todo example might also be a good example on how one can use operations. Check examples/todo/src/operation.rs. I didn't find any other examples of this in the iced repo except for the built in focus operations. - -## Future Development - -Right now it's a little annoying having to work with iced's Id type. At some point, I will work on a drop_zone widget that can take some generic clonable type as an id, and I will create a seperate find_zones operation that will return a list of this custom Id. This should make it easier to determine which drop zones were found. diff --git a/crates/iced_drop/src/lib.rs b/crates/iced_drop/src/lib.rs deleted file mode 100644 index c1e1b03..0000000 --- a/crates/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/crates/iced_drop/src/widget.rs b/crates/iced_drop/src/widget.rs deleted file mode 100644 index 6b3fed2..0000000 --- a/crates/iced_drop/src/widget.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod droppable; -pub mod operation; diff --git a/crates/iced_drop/src/widget/droppable.rs b/crates/iced_drop/src/widget/droppable.rs deleted file mode 100644 index 0e93bd0..0000000 --- a/crates/iced_drop/src/widget/droppable.rs +++ /dev/null @@ -1,565 +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<'b>, - renderer: &Renderer, - _viewport: &iced::Rectangle, - _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, - _viewport, - _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 cursor.is_over(layout.bounds()) { - if self.on_drop.is_none() { - mouse::Interaction::NotAllowed - } else if let Action::Drag(_, _) = state.action { - mouse::Interaction::Grabbing - } else { - mouse::Interaction::Pointer - } - } else { - 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), - ); - } -} diff --git a/crates/iced_drop/src/widget/operation.rs b/crates/iced_drop/src/widget/operation.rs deleted file mode 100644 index 3d7dcff..0000000 --- a/crates/iced_drop/src/widget/operation.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod drop; diff --git a/crates/iced_drop/src/widget/operation/drop.rs b/crates/iced_drop/src/widget/operation/drop.rs deleted file mode 100644 index ead412c..0000000 --- a/crates/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 }, - } -} |
