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}