admin管理员组

文章数量:1297129

I use iced_0.13.1 to build up my app and want to realize a draggable item.

A minimal(ish) reproduceable example:

use iced::{
    advanced::{image, mouse},
    widget::{
        canvas::{self, Geometry, Program},
        column, mouse_area, Canvas,
    },
    Color, Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector,
};

use iced::Task as Command;

fn main() -> iced::Result {
    iced::application("A draggable picture", MyApp::update, MyApp::view)
        .run()
}

impl Default for MyApp {
    fn default() -> Self {
        MyApp::new().0
    }
}

#[derive(Debug, Clone)]
enum Message {
    DragStarted(),
    DragEnded(),
}

struct MyApp {
    is_dragging: bool,
    image_size: Size,
}

impl MyApp {
    fn new() -> (Self, Command<Message>) {
        (
            Self {
                is_dragging: false,
                image_size: Size {
                    width: 300.,
                    height: 200.,
                },
            },
            Command::none(),
        )
    }

    fn update(&mut self, message: Message) -> Command<Message> {
        match message {
            Message::DragStarted() => {
                self.is_dragging = true;
            }
            Message::DragEnded() => {
                self.is_dragging = false;
            }
        }
        Command::none()
    }

    fn view(&self) -> Element<Message> {
        let draggable_image = mouse_area(
            Canvas::new(DraggableImage::new(Rectangle::with_size(self.image_size)))
                .width(self.image_size.width)
                .height(self.image_size.height),
        )
        .on_press(Message::DragStarted())
        .on_release(Message::DragEnded());

        column![
            draggable_image,
        ]
        .height(Length::Fill)
        .width(Length::Fill)
        .into()
    }
}

struct DraggableImage {
    cache: canvas::Cache,
    image: image::Handle,
    image_size: Rectangle,
}

impl DraggableImage {
    fn new(image_size: Rectangle) -> Self {
        Self {
            cache: canvas::Cache::default(),
            image: image::Handle::from_bytes(include_bytes!("../ferris.png").as_slice()),
            image_size,
        }
    }
}

impl<Message> Program<Message> for DraggableImage {
    type State = ();

    fn draw(
        &self,
        _state: &Self::State,
        renderer: &Renderer,
        _theme: &Theme,
        bounds: iced::Rectangle,
        _cursor: iced::advanced::mouse::Cursor,
    ) -> Vec<Geometry<Renderer>> {
        let pic = self.cache.draw(renderer, bounds.size(), |frame| {
            frame.with_save(|frame| {
                //? Uncomment it to visualize the bounds.
                // frame.fill_rectangle(Point::ORIGIN, frame.size(), Color::BLACK);
                // Move the picture.
                frame.translate(Vector::new(300., 200.));
                frame.fill_rectangle(Point::ORIGIN, frame.size(), Color::BLACK);
                // Finally, render the image.
                frame.draw_image(self.image_size, &self.image);
            });
        });
        vec![pic]
    }

    fn mouse_interaction(
        &self,
        _state: &Self::State,
        bounds: iced::Rectangle,
        cursor: iced::advanced::mouse::Cursor,
    ) -> iced::advanced::mouse::Interaction {
        if cursor.is_over(bounds) {
            mouse::Interaction::Pointer
        } else {
            mouse::Interaction::Idle
        }
    }
}

While iced doesn't provide a method like pub fn position(self, Point) -> Element<'a, Message, Theme, Renderer>(if that, I could use subscription to handle it), I have tried to customize a canvas item and then move it manually.

frame.translate(Vector::new(300., 200.));

However, the mouse_interaction, on_press, on_release are still bound to the bounds of DraggableImage at top-left corner, not the picture itself. So, how to realize a method manually to move canvas (such as a pressable circle, not just a shape) or widgets (such as a button) manually. horizontal_space().height(Length::FillPortion()) or vertical_space().width(Length::FillPortion()) might not be a good option, either.

本文标签: user interfaceHow to create draggable widgets in iced013xStack Overflow