Skip to main content

items_drag/
items_drag.rs

1use prism::chart::{
2    Chart,
3    series::{line_series, point, point_series},
4};
5
6use iced::{Element, Length, Task, Theme, widget::container};
7
8fn main() -> Result<(), iced::Error> {
9    iced::application(App::title, App::update, App::view)
10        .theme(App::theme)
11        .antialiasing(true)
12        .run_with(App::new)
13}
14
15#[derive(Debug, Clone)]
16enum Message {
17    OnMove(Option<usize>, Option<iced::Point>),
18    MouseDown(Option<usize>, Option<iced::Point>),
19    MouseUp(Option<iced::Point>),
20}
21
22#[derive(Debug)]
23struct App {
24    handles: Vec<Handle>,
25    hovered_item: Option<usize>,
26    dragging: Dragging,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30enum ItemId {
31    PointList,
32}
33
34#[derive(Debug, Default)]
35enum Dragging {
36    CouldStillBeClick(usize, iced::Point),
37    ForSure(usize, iced::Point),
38    #[default]
39    None,
40}
41
42#[derive(Debug, Clone)]
43struct Handle {
44    coords: (f32, f32),
45    style: point::Style,
46}
47
48impl Handle {
49    fn new(coords: (f32, f32)) -> Self {
50        Self {
51            coords,
52            style: point::Style::default(),
53        }
54    }
55}
56
57impl App {
58    pub fn new() -> (Self, Task<Message>) {
59        let data: Vec<_> = [(0.0, 0.0), (1.0, 1.0), (2.0, 1.0), (3.0, 0.0)]
60            .into_iter()
61            .map(Handle::new)
62            .collect();
63
64        (
65            Self {
66                handles: data,
67                hovered_item: None,
68                dragging: Dragging::None,
69            },
70            Task::none(),
71        )
72    }
73
74    pub fn title(&self) -> String {
75        "pliced".to_string()
76    }
77
78    pub fn update(&mut self, msg: Message) -> Task<Message> {
79        match msg {
80            Message::MouseDown(id, pos) => {
81                let Dragging::None = self.dragging else {
82                    return Task::none();
83                };
84
85                if let (Some(id), Some(pos)) = (id, pos) {
86                    self.dragging = Dragging::CouldStillBeClick(id, pos);
87                }
88            }
89            Message::OnMove(id, pos) => {
90                if id.is_none() {
91                    if let Some(handle) = self.hovered_item.and_then(|id| self.handles.get_mut(id))
92                    {
93                        handle.style = point::Style::default()
94                    }
95                }
96
97                self.hovered_item = id;
98
99                let Some(pos) = pos else {
100                    return Task::none();
101                };
102
103                match self.dragging {
104                    Dragging::CouldStillBeClick(id, prev_pos) => {
105                        if prev_pos == pos {
106                            return Task::none();
107                        } else {
108                            if let Some(handle) = self.handles.get_mut(id) {
109                                handle.coords.0 -= prev_pos.x - pos.x;
110                            }
111                            self.dragging = Dragging::ForSure(id, pos);
112                        }
113                    }
114                    Dragging::ForSure(id, prev_pos) => {
115                        if let Some(handle) = self.handles.get_mut(id) {
116                            handle.coords.0 -= prev_pos.x - pos.x;
117                        }
118                        self.dragging = Dragging::ForSure(id, pos);
119                    }
120                    Dragging::None => {}
121                }
122            }
123            Message::MouseUp(pos) => {
124                let Some(pos) = pos else {
125                    return Task::none();
126                };
127
128                match self.dragging {
129                    Dragging::CouldStillBeClick(id, _point) => {
130                        if let Some(handle) = self.handles.get_mut(id) {
131                            handle.style = point::Style::default();
132                        }
133                        self.hovered_item = None;
134                        self.dragging = Dragging::None;
135                    }
136                    Dragging::ForSure(id, prev_pos) => {
137                        if let Some(handle) = self.handles.get_mut(id) {
138                            handle.coords.0 -= prev_pos.x - pos.x;
139                            handle.style = point::Style::default();
140                        }
141                        self.dragging = Dragging::None;
142                    }
143                    Dragging::None => {}
144                }
145            }
146        }
147
148        let yellow: iced::Color = iced::Color::from_rgb8(238, 230, 0);
149        let green: iced::Color = iced::Color::from_rgb8(50, 205, 50);
150
151        match self.dragging {
152            Dragging::CouldStillBeClick(id, _point) | Dragging::ForSure(id, _point) => {
153                if let Some(handle) = self.handles.get_mut(id) {
154                    handle.style = point::Style {
155                        color: Some(green),
156                        radius: 10.0,
157                        ..Default::default()
158                    }
159                }
160            }
161            Dragging::None => {
162                if let Some(handle) = self.hovered_item.and_then(|id| self.handles.get_mut(id)) {
163                    handle.style = point::Style {
164                        color: Some(yellow),
165                        radius: 8.0,
166                        ..Default::default()
167                    }
168                }
169            }
170        }
171
172        Task::none()
173    }
174
175    pub fn view(&self) -> Element<'_, Message> {
176        let palette = self.theme().palette();
177        container(
178            Chart::new()
179                .width(Length::Fill)
180                .height(Length::Fill)
181                .x_range(-0.5..=3.5)
182                .y_range(-0.5..=1.5)
183                .push_series(line_series(self.handles.iter()).color(palette.primary))
184                .push_series(
185                    point_series(self.handles.iter())
186                        .color(palette.danger)
187                        .style_for_each(|_index, handle| handle.style.clone())
188                        .with_id(ItemId::PointList),
189                )
190                .on_press(|state| {
191                    let id = state.items().and_then(|l| l.first().map(|i| i.1));
192                    Message::MouseDown(id, state.get_offset())
193                })
194                .on_move(|state| {
195                    let id = state.items().and_then(|l| l.first().map(|i| i.1));
196                    Message::OnMove(id, state.get_offset())
197                })
198                .on_release(|state| Message::MouseUp(state.get_offset())),
199        )
200        .into()
201    }
202
203    pub fn theme(&self) -> Theme {
204        Theme::TokyoNight
205    }
206}
207
208impl From<&Handle> for (f32, f32) {
209    fn from(handle: &Handle) -> Self {
210        handle.coords
211    }
212}