blinc_layout/
text_selection.rs1use std::sync::{Arc, Mutex, OnceLock};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum SelectionSource {
12 TextInput,
14 TextArea,
16 StaticText,
18}
19
20#[derive(Debug, Clone, Default)]
22pub struct TextSelection {
23 pub text: Option<String>,
25 pub source: Option<SelectionSource>,
27 pub can_cut: bool,
29}
30
31impl TextSelection {
32 pub fn empty() -> Self {
34 Self::default()
35 }
36
37 pub fn new(text: String, source: SelectionSource, can_cut: bool) -> Self {
39 Self {
40 text: Some(text),
41 source: Some(source),
42 can_cut,
43 }
44 }
45
46 pub fn has_selection(&self) -> bool {
48 self.text.as_ref().is_some_and(|t| !t.is_empty())
49 }
50
51 pub fn selected_text(&self) -> Option<&str> {
53 self.text.as_deref()
54 }
55
56 pub fn clear(&mut self) {
58 self.text = None;
59 self.source = None;
60 self.can_cut = false;
61 }
62}
63
64pub type SharedTextSelection = Arc<Mutex<TextSelection>>;
66
67pub fn global_selection() -> SharedTextSelection {
72 static GLOBAL_SELECTION: OnceLock<SharedTextSelection> = OnceLock::new();
73 Arc::clone(GLOBAL_SELECTION.get_or_init(|| Arc::new(Mutex::new(TextSelection::empty()))))
74}
75
76pub fn set_selection(text: String, source: SelectionSource, can_cut: bool) {
80 let selection = global_selection();
81 let mut guard = selection.lock().unwrap();
82 *guard = TextSelection::new(text, source, can_cut);
83}
84
85pub fn clear_selection() {
89 let selection = global_selection();
90 let mut guard = selection.lock().unwrap();
91 guard.clear();
92}
93
94pub fn get_selected_text() -> Option<String> {
96 let selection = global_selection();
97 let guard = selection.lock().unwrap();
98 guard.text.clone()
99}
100
101pub fn can_cut_selection() -> bool {
103 let selection = global_selection();
104 let guard = selection.lock().unwrap();
105 guard.can_cut
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_selection_empty() {
114 let sel = TextSelection::empty();
115 assert!(!sel.has_selection());
116 assert!(sel.selected_text().is_none());
117 }
118
119 #[test]
120 fn test_selection_with_text() {
121 let sel = TextSelection::new("hello".to_string(), SelectionSource::TextInput, true);
122 assert!(sel.has_selection());
123 assert_eq!(sel.selected_text(), Some("hello"));
124 assert!(sel.can_cut);
125 }
126
127 #[test]
128 fn test_global_selection() {
129 clear_selection();
131 assert!(get_selected_text().is_none());
132
133 set_selection("test text".to_string(), SelectionSource::TextInput, true);
135 assert_eq!(get_selected_text(), Some("test text".to_string()));
136 assert!(can_cut_selection());
137
138 clear_selection();
140 assert!(get_selected_text().is_none());
141 }
142}