freya_components/
selectable_text.rs

1use std::borrow::Cow;
2
3use freya_core::prelude::*;
4use freya_edit::*;
5
6/// Current status of the SelectableText.
7#[derive(Debug, Default, PartialEq, Clone, Copy)]
8pub enum SelectableTextStatus {
9    /// Default state.
10    #[default]
11    Idle,
12    /// Mouse is hovering the text.
13    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}