1use std::ops::Mul;
2
3use freya_core::{
4 elements::paragraph::{
5 ParagraphCursorExt,
6 ParagraphHolderInner,
7 },
8 prelude::*,
9};
10use keyboard_types::NamedKey;
11use torin::prelude::CursorPoint;
12
13use crate::{
14 EditableConfig,
15 EditorLine,
16 TextSelection,
17 text_editor::{
18 TextEditor,
19 TextEvent,
20 },
21};
22
23#[derive(Debug)]
24pub enum EditableEvent<'a> {
25 Release,
26 Move {
27 location: CursorPoint,
28 editor_line: EditorLine,
29 holder: &'a ParagraphHolder,
30 },
31 Down {
32 location: CursorPoint,
33 editor_line: EditorLine,
34 holder: &'a ParagraphHolder,
35 },
36 KeyDown {
37 key: &'a Key,
38 modifiers: Modifiers,
39 editor_line: Option<EditorLine>,
40 holder: Option<&'a ParagraphHolder>,
41 },
42 KeyUp {
43 key: &'a Key,
44 },
45}
46
47impl EditableEvent<'_> {
48 pub fn process<T: TextEditor>(
49 self,
50 mut editor: Writable<T>,
51 mut dragging: Writable<TextDragging>,
52 config: &'_ EditableConfig,
53 ) {
54 match self {
55 EditableEvent::Down {
56 location,
57 editor_line,
58 holder,
59 } => {
60 let location = holder.visible_location(location);
61 let holder = holder.0.borrow();
62 let ParagraphHolderInner {
63 paragraph,
64 scale_factor,
65 ..
66 } = holder.as_ref().unwrap();
67
68 let mut text_editor = editor.write();
69
70 if dragging.peek().shift || dragging.peek().clicked {
71 text_editor.selection_mut().set_as_range();
72 } else {
73 text_editor.clear_selection();
74 }
75
76 let press_type = EventsCombos::<()>::pressed(location);
77 let press_type = if press_type.is_double() && config.select_all_on_double_click {
78 PressEventType::Quadruple
79 } else {
80 press_type
81 };
82 let char_position =
83 paragraph.cursor_index_at_point(location.mul(*scale_factor).to_tuple());
84 let press_selection = text_editor.measure_selection(char_position, editor_line);
85 let new_selection = match press_type {
86 PressEventType::Single => press_selection,
87 PressEventType::Double => TextSelection::new_range(
88 text_editor.find_word_boundaries(press_selection.pos()),
89 ),
90 PressEventType::Triple => TextSelection::new_range(
91 text_editor.find_line_boundaries(press_selection.pos()),
92 ),
93 PressEventType::Quadruple => {
94 TextSelection::new_range((0, text_editor.len_utf16_cu()))
95 }
96 };
97
98 dragging
99 .write()
100 .start_selection(press_type, new_selection.clone());
101 if *text_editor.selection() != new_selection {
102 *text_editor.selection_mut() = new_selection;
103 }
104 }
105 EditableEvent::Move {
106 location,
107 editor_line,
108 holder,
109 } => {
110 let location = holder.visible_location(location);
111 if dragging.peek().clicked {
112 EventsCombos::<()>::moved(location);
113
114 let paragraph = holder.0.borrow();
115 let ParagraphHolderInner {
116 paragraph,
117 scale_factor,
118 ..
119 } = paragraph.as_ref().unwrap();
120
121 let dist_position = location.mul(*scale_factor);
122
123 let to = paragraph.cursor_index_at_point(dist_position.to_tuple());
125
126 if editor.peek().get_selection().is_none() {
127 editor.write().selection_mut().set_as_range();
128 }
129
130 let current_selection = editor.peek().selection().clone();
131
132 let new_selection =
133 dragging
134 .peek()
135 .measure_selection(&*editor.peek(), to, editor_line);
136
137 if current_selection != new_selection {
139 let mut text_editor = editor.write();
140 *text_editor.selection_mut() = new_selection;
141 }
142 }
143 }
144 EditableEvent::Release => {
145 dragging.write().clicked = false;
146 EventsCombos::<()>::released();
147 }
148 EditableEvent::KeyDown {
149 key,
150 modifiers,
151 editor_line,
152 holder,
153 } => {
154 match key {
155 Key::Named(NamedKey::Shift) => {
157 dragging.write().shift = true;
158 }
159 _ => {
161 editor.write_if(|mut editor| {
162 let event = editor.process_key(
163 key,
164 &modifiers,
165 editor_line,
166 holder,
167 config.allow_tabs,
168 config.allow_changes,
169 config.allow_read_clipboard,
170 config.allow_write_clipboard,
171 );
172 if event.contains(TextEvent::TEXT_CHANGED) {
173 *dragging.write() = TextDragging::default();
174 }
175 !event.is_empty()
176 });
177 }
178 }
179 }
180 EditableEvent::KeyUp { key, .. } => {
181 if *key == Key::Named(NamedKey::Shift) {
182 dragging.write().shift = false;
183 }
184 }
185 };
186 }
187}
188
189#[derive(Debug, PartialEq, Clone, Default)]
190pub struct TextDragging {
191 pub shift: bool,
192 pub clicked: bool,
193 multi_click_selection: Option<(PressEventType, TextSelection)>,
194}
195
196impl TextDragging {
197 pub fn start_selection(&mut self, press_type: PressEventType, selection: TextSelection) {
199 self.clicked = true;
200 self.multi_click_selection = if press_type.is_single() {
201 None
202 } else {
203 Some((press_type, selection))
204 };
205 }
206
207 pub fn measure_selection<T: TextEditor>(
209 &self,
210 editor: &T,
211 to: usize,
212 editor_line: EditorLine,
213 ) -> TextSelection {
214 let selection = editor.measure_selection(to, editor_line);
215 let Some((press_type, anchor)) = &self.multi_click_selection else {
216 return selection;
217 };
218 let position = selection.pos();
219 if press_type.is_quadruple() || (position >= anchor.start() && position <= anchor.end()) {
220 return anchor.clone();
221 }
222 let (start, end) = match press_type {
223 PressEventType::Double => editor.find_word_boundaries(position),
224 PressEventType::Triple => editor.find_line_boundaries(position),
225 _ => return selection,
226 };
227 if position < anchor.start() {
228 TextSelection::new_range((anchor.end(), start))
229 } else {
230 TextSelection::new_range((anchor.start(), end))
231 }
232 }
233}