1#[cfg(feature = "crossterm")]
2use crossterm::event::{MouseButton, MouseEvent, MouseEventKind};
3use derive_getters::Getters;
4use ratatui_core::layout::Rect;
5
6#[derive(Clone, Debug, Default, Getters)]
7pub struct PopupState {
8 pub(crate) area: Option<Rect>,
10 pub(crate) drag_state: DragState,
12}
13
14#[derive(Clone, Debug, Default)]
15pub enum DragState {
16 #[default]
17 NotDragging,
18 Dragging {
19 col_offset: u16,
20 row_offset: u16,
21 },
22}
23
24impl PopupState {
25 pub fn move_up(&mut self, amount: u16) {
26 self.move_by(0, -i32::from(amount));
27 }
28
29 pub fn move_down(&mut self, amount: u16) {
30 self.move_by(0, i32::from(amount));
31 }
32
33 pub fn move_left(&mut self, amount: u16) {
34 self.move_by(-i32::from(amount), 0);
35 }
36
37 pub fn move_right(&mut self, amount: u16) {
38 self.move_by(i32::from(amount), 0);
39 }
40
41 pub fn move_by(&mut self, x: i32, y: i32) {
43 if let Some(area) = self.area {
44 self.area.replace(Rect {
45 x: i32::from(area.x)
46 .saturating_add(x)
47 .try_into()
48 .unwrap_or(area.x),
49 y: i32::from(area.y)
50 .saturating_add(y)
51 .try_into()
52 .unwrap_or(area.y),
53 ..area
54 });
55 }
56 }
57
58 pub const fn move_to(&mut self, x: u16, y: u16) {
60 if let Some(area) = self.area {
61 self.area.replace(Rect { x, y, ..area });
62 }
63 }
64
65 pub fn mouse_down(&mut self, col: u16, row: u16) {
67 if let Some(area) = self.area {
68 if area.contains((col, row).into()) {
69 self.drag_state = DragState::Dragging {
70 col_offset: col.saturating_sub(area.x),
71 row_offset: row.saturating_sub(area.y),
72 };
73 }
74 }
75 }
76
77 pub const fn mouse_up(&mut self, _col: u16, _row: u16) {
79 self.drag_state = DragState::NotDragging;
80 }
81
82 pub const fn mouse_drag(&mut self, col: u16, row: u16) {
84 if let DragState::Dragging {
85 col_offset,
86 row_offset,
87 } = self.drag_state
88 {
89 if let Some(area) = self.area {
90 let x = col.saturating_sub(col_offset);
91 let y = row.saturating_sub(row_offset);
92 self.area.replace(Rect { x, y, ..area });
93 }
94 }
95 }
96
97 #[cfg(feature = "crossterm")]
98 pub fn handle_mouse_event(&mut self, event: MouseEvent) {
99 match event.kind {
100 MouseEventKind::Down(MouseButton::Left) => self.mouse_down(event.column, event.row),
101 MouseEventKind::Up(MouseButton::Left) => self.mouse_up(event.column, event.row),
102 MouseEventKind::Drag(MouseButton::Left) => self.mouse_drag(event.column, event.row),
103 _ => {}
104 }
105 }
106}