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 EditableMode::MultipleLinesSingleEditor,
44 );
45 let mut status = use_state(SelectableTextStatus::default);
46 let focus = use_focus();
47 let mut drag_origin = use_state(|| None);
48
49 if self.value.as_ref() != editable.editor().read().rope() {
50 editable.editor_mut().write().set(self.value.as_ref());
51 editable.editor_mut().write().editor_history().clear();
52 }
53
54 let highlights = editable.editor().read().get_visible_selection(0);
55
56 let on_pointer_down = move |e: Event<PointerEventData>| {
57 e.stop_propagation();
58 drag_origin.set(Some(e.global_location() - e.element_location()));
59 editable.process_event(EditableEvent::Down {
60 location: e.element_location(),
61 editor_id: 0,
62 holder: &holder.read(),
63 });
64 focus.request_focus();
65 };
66
67 let on_global_mouse_move = move |e: Event<MouseEventData>| {
68 if focus.is_focused()
69 && let Some(drag_origin) = drag_origin()
70 {
71 let mut element_location = e.element_location;
72 element_location.x -= drag_origin.x;
73 element_location.y -= drag_origin.y;
74 editable.process_event(EditableEvent::Move {
75 location: e.element_location,
76 editor_id: 0,
77 holder: &holder.read(),
78 });
79 }
80 };
81
82 let on_global_mouse_down = move |_| {
83 editable.editor_mut().write().clear_selection();
84 };
85
86 let on_pointer_enter = move |_| {
87 *status.write() = SelectableTextStatus::Hovering;
88 };
89
90 let on_pointer_leave = move |_| {
91 *status.write() = SelectableTextStatus::default();
92 };
93
94 let on_mouse_up = move |_| {
95 editable.process_event(EditableEvent::Release);
96 };
97
98 let on_key_down = move |e: Event<KeyboardEventData>| {
99 editable.process_event(EditableEvent::KeyDown {
100 key: &e.key,
101 modifiers: e.modifiers,
102 });
103 };
104
105 let on_key_up = move |e: Event<KeyboardEventData>| {
106 editable.process_event(EditableEvent::KeyUp { key: &e.key });
107 };
108
109 let on_global_mouse_up = move |_| {
110 match *status.read() {
111 SelectableTextStatus::Idle if focus.is_focused() => {
112 editable.process_event(EditableEvent::Release);
113 }
114 SelectableTextStatus::Hovering => {
115 editable.process_event(EditableEvent::Release);
116 }
117 _ => {}
118 };
119
120 if drag_origin.read().is_some() {
121 drag_origin.set(None);
122 } else if focus.is_focused() {
123 focus.request_unfocus();
124 }
125 };
126
127 paragraph()
128 .holder(holder.read().clone())
129 .a11y_focusable(true)
130 .cursor_color(Color::BLACK)
131 .a11y_id(focus.a11y_id())
132 .highlights(highlights.map(|h| vec![h]))
133 .on_mouse_up(on_mouse_up)
134 .on_global_mouse_move(on_global_mouse_move)
135 .on_global_mouse_down(on_global_mouse_down)
136 .on_pointer_down(on_pointer_down)
137 .on_pointer_enter(on_pointer_enter)
138 .on_pointer_leave(on_pointer_leave)
139 .on_global_mouse_up(on_global_mouse_up)
140 .on_key_down(on_key_down)
141 .on_key_up(on_key_up)
142 .span(Span::new(editable.editor().read().to_string()))
143 }
144
145 fn render_key(&self) -> DiffKey {
146 self.key.clone().or(self.default_key())
147 }
148}