freya_edit/
event.rs

1use std::ops::Mul;
2
3use freya_core::{
4    elements::paragraph::ParagraphHolderInner,
5    prelude::*,
6};
7use torin::prelude::CursorPoint;
8
9use crate::{
10    EditableConfig,
11    rope_editor::RopeEditor,
12    text_editor::{
13        TextEditor,
14        TextEvent,
15    },
16};
17
18pub enum EditableEvent<'a> {
19    Release,
20    Move {
21        location: CursorPoint,
22        editor_id: usize,
23        holder: &'a ParagraphHolder,
24    },
25    Down {
26        location: CursorPoint,
27        editor_id: usize,
28        holder: &'a ParagraphHolder,
29    },
30    KeyDown {
31        key: &'a Key,
32        modifiers: Modifiers,
33    },
34    KeyUp {
35        key: &'a Key,
36    },
37}
38
39impl EditableEvent<'_> {
40    pub fn process<'a, 'b>(
41        self,
42        mut editor: impl MutView<'b, RopeEditor>,
43        mut dragging: impl MutView<'b, TextDragging>,
44        config: &'_ EditableConfig,
45    ) {
46        match self {
47            EditableEvent::Down {
48                location,
49                editor_id,
50                holder,
51            } => {
52                let holder = holder.0.borrow();
53                let ParagraphHolderInner {
54                    paragraph,
55                    scale_factor,
56                } = holder.as_ref().unwrap();
57
58                dragging
59                    .write()
60                    .set_cursor_coords(location.mul(*scale_factor));
61
62                let mut text_editor = editor.write();
63                text_editor.clear_selection();
64
65                let char_position = paragraph.get_glyph_position_at_coordinate(
66                    location.mul(*scale_factor).to_i32().to_tuple(),
67                );
68                let new_cursor =
69                    text_editor.measure_new_cursor(char_position.position as usize, editor_id);
70
71                // Only update and clear the selection if the cursor has changed
72                if *text_editor.cursor() != new_cursor {
73                    *text_editor.cursor_mut() = new_cursor;
74                    if let TextDragging::FromCursorToPoint { cursor: from, .. } = &*dragging.peek()
75                    {
76                        let to = text_editor.cursor_pos();
77                        text_editor.set_selection((*from, to));
78                    } else {
79                        text_editor.clear_selection();
80                    }
81                }
82            }
83            EditableEvent::Move {
84                location,
85                editor_id,
86                holder,
87            } => {
88                if let Some(origin) = dragging.peek().get_cursor_coords() {
89                    let paragraph = holder.0.borrow();
90                    let ParagraphHolderInner {
91                        paragraph,
92                        scale_factor,
93                    } = paragraph.as_ref().unwrap();
94
95                    let origin_position = origin;
96                    let dist_position = location.mul(*scale_factor);
97
98                    // Calculate the start of the highlighting
99                    let origin_char = paragraph
100                        .get_glyph_position_at_coordinate(origin_position.to_i32().to_tuple());
101                    // Calculate the end of the highlighting
102                    let dist_char = paragraph
103                        .get_glyph_position_at_coordinate(dist_position.to_i32().to_tuple());
104                    let from = origin_char.position as usize;
105                    let to = dist_char.position as usize;
106
107                    let current_cursor = editor.peek().cursor().clone();
108                    let current_selection = editor.peek().get_selection();
109
110                    let maybe_new_cursor = editor.peek().measure_new_cursor(to, editor_id);
111                    let maybe_new_selection =
112                        editor.peek().measure_new_selection(from, to, editor_id);
113
114                    // Update the text selection if it has changed
115                    if let Some(current_selection) = current_selection {
116                        if current_selection != maybe_new_selection {
117                            let mut text_editor = editor.write();
118                            text_editor.set_selection(maybe_new_selection);
119                        }
120                    } else {
121                        let mut text_editor = editor.write();
122                        text_editor.set_selection(maybe_new_selection);
123                    }
124
125                    // Update the cursor if it has changed
126                    if current_cursor != maybe_new_cursor {
127                        let mut text_editor = editor.write();
128                        *text_editor.cursor_mut() = maybe_new_cursor;
129                    }
130                }
131            }
132            EditableEvent::Release => {
133                let dragging = &mut *dragging.write();
134                match dragging {
135                    TextDragging::FromCursorToPoint { shift, clicked, .. } if *shift => {
136                        *clicked = false;
137                    }
138                    _ => {
139                        *dragging = TextDragging::None;
140                    }
141                }
142            }
143            EditableEvent::KeyDown { key, modifiers } => {
144                match key {
145                    // Handle dragging
146                    Key::Shift => {
147                        let dragging = &mut *dragging.write();
148                        match dragging {
149                            TextDragging::FromCursorToPoint {
150                                shift: shift_pressed,
151                                ..
152                            } => {
153                                *shift_pressed = true;
154                            }
155                            TextDragging::None => {
156                                *dragging = TextDragging::FromCursorToPoint {
157                                    shift: true,
158                                    clicked: false,
159                                    cursor: editor.peek().cursor_pos(),
160                                    dist: None,
161                                }
162                            }
163                            _ => {}
164                        }
165                    }
166                    // Handle editing
167                    _ => {
168                        editor.write_if(|mut ditor| {
169                            let event = ditor.process_key(
170                                key,
171                                &modifiers,
172                                config.allow_tabs,
173                                config.allow_changes,
174                                config.allow_clipboard,
175                            );
176                            if event.contains(TextEvent::TEXT_CHANGED) {
177                                *dragging.write() = TextDragging::None;
178                            }
179                            !event.is_empty()
180                        });
181                    }
182                }
183            }
184            EditableEvent::KeyUp { key } => {
185                if *key == Key::Shift {
186                    if let TextDragging::FromCursorToPoint { shift, .. } = &mut *dragging.write() {
187                        *shift = false;
188                    }
189                } else {
190                    *dragging.write() = TextDragging::None;
191                }
192            }
193        };
194    }
195}
196
197/// Indicates the type of text dragging being done.
198#[derive(Debug, PartialEq, Clone)]
199pub enum TextDragging {
200    None,
201    FromPointToPoint {
202        src: CursorPoint,
203    },
204    FromCursorToPoint {
205        shift: bool,
206        clicked: bool,
207        cursor: usize,
208        dist: Option<CursorPoint>,
209    },
210}
211
212impl TextDragging {
213    pub fn has_cursor_coords(&self) -> bool {
214        match self {
215            Self::None => false,
216            Self::FromPointToPoint { .. } => true,
217            Self::FromCursorToPoint { dist, .. } => dist.is_some(),
218        }
219    }
220
221    pub fn set_cursor_coords(&mut self, cursor: CursorPoint) {
222        match self {
223            Self::FromPointToPoint { src } => *src = cursor,
224            Self::FromCursorToPoint {
225                dist, shift: true, ..
226            } => *dist = Some(cursor),
227            _ => *self = Self::FromPointToPoint { src: cursor },
228        }
229    }
230
231    pub fn get_cursor_coords(&self) -> Option<CursorPoint> {
232        match self {
233            Self::None => None,
234            Self::FromPointToPoint { src } => Some(*src),
235            Self::FromCursorToPoint { dist, clicked, .. } => {
236                if *clicked {
237                    *dist
238                } else {
239                    None
240                }
241            }
242        }
243    }
244}