freya_edit/
event.rs

1use std::ops::Mul;
2
3use freya_core::{
4    elements::paragraph::ParagraphHolderInner,
5    prelude::*,
6};
7use keyboard_types::NamedKey;
8use torin::prelude::CursorPoint;
9
10use crate::{
11    EditableConfig,
12    EditorLine,
13    TextSelection,
14    rope_editor::RopeEditor,
15    text_editor::{
16        TextEditor,
17        TextEvent,
18    },
19};
20
21pub enum EditableEvent<'a> {
22    Release,
23    Move {
24        location: CursorPoint,
25        editor_line: EditorLine,
26        holder: &'a ParagraphHolder,
27    },
28    Down {
29        location: CursorPoint,
30        editor_line: EditorLine,
31        holder: &'a ParagraphHolder,
32    },
33    KeyDown {
34        key: &'a Key,
35        modifiers: Modifiers,
36    },
37    KeyUp {
38        key: &'a Key,
39    },
40}
41
42impl EditableEvent<'_> {
43    pub fn process<'a, 'b>(
44        self,
45        mut editor: impl MutView<'b, RopeEditor>,
46        mut dragging: impl MutView<'b, TextDragging>,
47        config: &'_ EditableConfig,
48    ) {
49        match self {
50            EditableEvent::Down {
51                location,
52                editor_line,
53                holder,
54            } => {
55                let holder = holder.0.borrow();
56                let ParagraphHolderInner {
57                    paragraph,
58                    scale_factor,
59                } = holder.as_ref().unwrap();
60
61                let mut text_editor = editor.write();
62
63                if dragging.peek().shift || dragging.peek().clicked {
64                    text_editor.selection_mut().set_as_range();
65                } else {
66                    text_editor.clear_selection();
67                }
68
69                dragging.write().clicked = true;
70
71                match EventsCombos::pressed(location) {
72                    PressEventType::Triple => {
73                        let current_selection = text_editor.selection().clone();
74
75                        let char_position = paragraph.get_glyph_position_at_coordinate(
76                            location.mul(*scale_factor).to_i32().to_tuple(),
77                        );
78                        let press_selection = text_editor
79                            .measure_selection(char_position.position as usize, editor_line);
80
81                        // Get the line start char and its length
82                        let line = text_editor.rope().char_to_line(press_selection.pos());
83                        let line_char = text_editor.rope().line_to_char(line);
84                        let line_len = text_editor.rope().line(line).len_utf16_cu();
85                        let new_selection =
86                            TextSelection::new_range((line_char, line_char + line_len));
87
88                        // Select the whole line
89                        if current_selection != new_selection {
90                            *text_editor.selection_mut() = new_selection;
91                        }
92                    }
93                    PressEventType::Double => {
94                        let current_selection = text_editor.selection().clone();
95
96                        let char_position = paragraph.get_glyph_position_at_coordinate(
97                            location.mul(*scale_factor).to_i32().to_tuple(),
98                        );
99                        let press_selection = text_editor
100                            .measure_selection(char_position.position as usize, editor_line);
101
102                        // Find word boundaries
103                        let range = text_editor.find_word_boundaries(press_selection.pos());
104                        let new_selection = TextSelection::new_range(range);
105
106                        // Select the word
107                        if current_selection != new_selection {
108                            *text_editor.selection_mut() = new_selection;
109                        }
110                    }
111                    PressEventType::Single => {
112                        let current_selection = text_editor.selection().clone();
113
114                        let char_position = paragraph.get_glyph_position_at_coordinate(
115                            location.mul(*scale_factor).to_i32().to_tuple(),
116                        );
117                        let new_selection = text_editor
118                            .measure_selection(char_position.position as usize, editor_line);
119
120                        // Move the cursor
121                        if current_selection != new_selection {
122                            *text_editor.selection_mut() = new_selection;
123                        }
124                    }
125                }
126            }
127            EditableEvent::Move {
128                location,
129                editor_line,
130                holder,
131            } => {
132                if dragging.peek().clicked {
133                    let paragraph = holder.0.borrow();
134                    let ParagraphHolderInner {
135                        paragraph,
136                        scale_factor,
137                    } = paragraph.as_ref().unwrap();
138
139                    let dist_position = location.mul(*scale_factor);
140
141                    // Calculate the end of the highlighting
142                    let dist_char = paragraph
143                        .get_glyph_position_at_coordinate(dist_position.to_i32().to_tuple());
144                    let to = dist_char.position as usize;
145
146                    if !editor.peek().selection.is_range() {
147                        editor.write().selection_mut().set_as_range();
148                    }
149
150                    let current_selection = editor.peek().selection().clone();
151
152                    let new_selection = editor.peek().measure_selection(to, editor_line);
153
154                    // Update the cursor if it has changed
155                    if current_selection != new_selection {
156                        let mut text_editor = editor.write();
157                        *text_editor.selection_mut() = new_selection;
158                    }
159                }
160            }
161            EditableEvent::Release => {
162                dragging.write().clicked = false;
163            }
164            EditableEvent::KeyDown { key, modifiers } => {
165                match key {
166                    // Handle dragging
167                    Key::Named(NamedKey::Shift) => {
168                        dragging.write().shift = true;
169                    }
170                    // Handle editing
171                    _ => {
172                        editor.write_if(|mut editor| {
173                            let event = editor.process_key(
174                                key,
175                                &modifiers,
176                                config.allow_tabs,
177                                config.allow_changes,
178                                config.allow_clipboard,
179                            );
180                            if event.contains(TextEvent::TEXT_CHANGED) {
181                                *dragging.write() = TextDragging::default();
182                            }
183                            !event.is_empty()
184                        });
185                    }
186                }
187            }
188            EditableEvent::KeyUp { key, .. } => {
189                if *key == Key::Named(NamedKey::Shift) {
190                    dragging.write().shift = false;
191                }
192            }
193        };
194    }
195}
196
197#[derive(Debug, PartialEq, Clone, Default)]
198pub struct TextDragging {
199    pub shift: bool,
200    pub clicked: bool,
201}