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            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}