freya_components/
selectable_text.rs1use std::borrow::Cow;
2
3use freya_core::prelude::*;
4use freya_edit::*;
5
6#[derive(Debug, Default, PartialEq, Clone, Copy)]
8pub enum SelectableTextStatus {
9 #[default]
11 Idle,
12 Hovering,
14}
15
16#[derive(Clone, PartialEq)]
17pub struct SelectableText {
18 value: Cow<'static, str>,
19 key: DiffKey,
20}
21
22impl KeyExt for SelectableText {
23 fn write_key(&mut self) -> &mut DiffKey {
24 &mut self.key
25 }
26}
27
28impl SelectableText {
29 pub fn new(value: impl Into<Cow<'static, str>>) -> Self {
30 Self {
31 value: value.into(),
32 key: DiffKey::None,
33 }
34 }
35}
36
37impl Render for SelectableText {
38 fn render(&self) -> impl IntoElement {
39 let holder = use_state(ParagraphHolder::default);
40 let mut editable = use_editable(
41 || self.value.to_string(),
42 move || EditableConfig::new().with_allow_changes(false),
43 );
44 let mut status = use_state(SelectableTextStatus::default);
45 let focus = use_focus();
46 let mut drag_origin = use_state(|| None);
47
48 if self.value.as_ref() != editable.editor().read().rope() {
49 editable.editor_mut().write().set(self.value.as_ref());
50 editable.editor_mut().write().editor_history().clear();
51 }
52
53 let highlights = editable
54 .editor()
55 .read()
56 .get_visible_selection(EditorLine::SingleParagraph);
57
58 let on_pointer_down = move |e: Event<PointerEventData>| {
59 e.stop_propagation();
60 drag_origin.set(Some(e.global_location() - e.element_location()));
61 editable.process_event(EditableEvent::Down {
62 location: e.element_location(),
63 editor_line: EditorLine::SingleParagraph,
64 holder: &holder.read(),
65 });
66 focus.request_focus();
67 };
68
69 let on_global_mouse_move = move |e: Event<MouseEventData>| {
70 if focus.is_focused()
71 && let Some(drag_origin) = drag_origin()
72 {
73 let mut element_location = e.element_location;
74 element_location.x -= drag_origin.x;
75 element_location.y -= drag_origin.y;
76 editable.process_event(EditableEvent::Move {
77 location: e.element_location,
78 editor_line: EditorLine::SingleParagraph,
79 holder: &holder.read(),
80 });
81 }
82 };
83
84 let on_global_mouse_down = move |_| {
85 editable.editor_mut().write().clear_selection();
86 };
87
88 let on_pointer_enter = move |_| {
89 *status.write() = SelectableTextStatus::Hovering;
90 };
91
92 let on_pointer_leave = move |_| {
93 *status.write() = SelectableTextStatus::default();
94 };
95
96 let on_mouse_up = move |_| {
97 editable.process_event(EditableEvent::Release);
98 };
99
100 let on_key_down = move |e: Event<KeyboardEventData>| {
101 editable.process_event(EditableEvent::KeyDown {
102 key: &e.key,
103 modifiers: e.modifiers,
104 });
105 };
106
107 let on_key_up = move |e: Event<KeyboardEventData>| {
108 editable.process_event(EditableEvent::KeyUp { key: &e.key });
109 };
110
111 let on_global_mouse_up = move |_| {
112 match *status.read() {
113 SelectableTextStatus::Idle if focus.is_focused() => {
114 editable.process_event(EditableEvent::Release);
115 }
116 SelectableTextStatus::Hovering => {
117 editable.process_event(EditableEvent::Release);
118 }
119 _ => {}
120 };
121
122 if drag_origin.read().is_some() {
123 drag_origin.set(None);
124 } else if focus.is_focused() {
125 focus.request_unfocus();
126 }
127 };
128
129 paragraph()
130 .a11y_focusable(true)
131 .holder(holder.read().clone())
132 .a11y_focusable(true)
133 .cursor_color(Color::BLACK)
134 .a11y_id(focus.a11y_id())
135 .highlights(highlights.map(|h| vec![h]))
136 .on_mouse_up(on_mouse_up)
137 .on_global_mouse_move(on_global_mouse_move)
138 .on_global_mouse_down(on_global_mouse_down)
139 .on_pointer_down(on_pointer_down)
140 .on_pointer_enter(on_pointer_enter)
141 .on_pointer_leave(on_pointer_leave)
142 .on_global_mouse_up(on_global_mouse_up)
143 .on_key_down(on_key_down)
144 .on_key_up(on_key_up)
145 .span(Span::new(editable.editor().read().to_string()))
146 }
147
148 fn render_key(&self) -> DiffKey {
149 self.key.clone().or(self.default_key())
150 }
151}