egui_charts/ui/widget_bar/panels/object_tree/
state.rs1use std::collections::{BTreeSet, HashSet};
6
7use super::types::SourceType;
8
9#[derive(Clone, Debug, Default)]
11pub struct ObjectTreeState {
12 pub folders: BTreeSet<Vec<String>>,
15 pub collapsed_folders: BTreeSet<Vec<String>>,
17 pub creating_folder: Option<Vec<String>>,
19
20 pub visible_ids: Vec<usize>,
23 pub selected_ids: HashSet<usize>,
25 pub last_selected_id: Option<usize>,
27 pub selection_anchor: Option<usize>,
29
30 pub expanded_groups: HashSet<String>,
33 pub expanded_items: HashSet<usize>,
35 pub data_window_expanded: bool,
37
38 pub filter_text: String,
41 pub filter_type: Option<SourceType>,
43
44 pub dragging_id: Option<usize>,
47 pub drag_start_pos: Option<egui::Pos2>,
49 pub drop_target: Option<usize>,
51
52 pub renaming_id: Option<usize>,
55 pub rename_text: String,
57
58 pub hovered_id: Option<usize>,
61}
62
63impl ObjectTreeState {
64 pub fn new() -> Self {
66 let mut state = Self::default();
67 state.expanded_groups.insert("Indicators".to_string());
69 state.expanded_groups.insert("Drawings".to_string());
70 state.expanded_groups.insert("Templates".to_string());
71 state.data_window_expanded = true;
72 state
73 }
74
75 pub fn select(&mut self, id: usize) {
79 self.selected_ids.clear();
80 self.selected_ids.insert(id);
81 self.last_selected_id = Some(id);
82 self.selection_anchor = Some(id);
83 }
84
85 pub fn toggle_selection(&mut self, id: usize) {
87 if self.selected_ids.contains(&id) {
88 self.selected_ids.remove(&id);
89 } else {
90 self.selected_ids.insert(id);
91 }
92 self.last_selected_id = Some(id);
93 }
94
95 pub fn range_select(&mut self, id: usize, ordered_ids: &[usize]) {
97 if let Some(anchor) = self.selection_anchor {
98 let anchor_idx = ordered_ids.iter().position(|&x| x == anchor);
99 let target_idx = ordered_ids.iter().position(|&x| x == id);
100
101 if let (Some(start), Some(end)) = (anchor_idx, target_idx) {
102 let (min, max) = if start < end {
103 (start, end)
104 } else {
105 (end, start)
106 };
107
108 self.selected_ids.clear();
109 for &item_id in &ordered_ids[min..=max] {
110 self.selected_ids.insert(item_id);
111 }
112 }
113 } else {
114 self.select(id);
115 }
116 self.last_selected_id = Some(id);
117 }
118
119 pub fn select_all(&mut self, all_ids: &[usize]) {
121 self.selected_ids = all_ids.iter().copied().collect();
122 }
123
124 pub fn clear_selection(&mut self) {
126 self.selected_ids.clear();
127 self.last_selected_id = None;
128 }
129
130 pub fn is_selected(&self, id: usize) -> bool {
132 self.selected_ids.contains(&id)
133 }
134
135 pub fn selection_count(&self) -> usize {
137 self.selected_ids.len()
138 }
139
140 pub fn toggle_group(&mut self, group: &str) {
144 if self.expanded_groups.contains(group) {
145 self.expanded_groups.remove(group);
146 } else {
147 self.expanded_groups.insert(group.to_string());
148 }
149 }
150
151 pub fn is_group_expanded(&self, group: &str) -> bool {
153 self.expanded_groups.contains(group)
154 }
155
156 pub fn toggle_item(&mut self, id: usize) {
158 if self.expanded_items.contains(&id) {
159 self.expanded_items.remove(&id);
160 } else {
161 self.expanded_items.insert(id);
162 }
163 }
164
165 pub fn is_item_expanded(&self, id: usize) -> bool {
167 self.expanded_items.contains(&id)
168 }
169
170 pub fn expand_all_groups(&mut self) {
172 self.collapsed_folders.clear();
173 self.expanded_groups.insert("Indicators".to_string());
174 self.expanded_groups.insert("Drawings".to_string());
175 self.expanded_groups.insert("Templates".to_string());
176 }
177
178 pub fn collapse_all_groups(&mut self) {
180 self.expanded_groups.clear();
181 self.collapsed_folders = self.folders.clone();
182 }
183
184 pub fn set_filter(&mut self, text: impl Into<String>) {
188 self.filter_text = text.into();
189 }
190
191 pub fn clear_filter(&mut self) {
193 self.filter_text.clear();
194 self.filter_type = None;
195 }
196
197 pub fn has_filter(&self) -> bool {
199 !self.filter_text.is_empty() || self.filter_type.is_some()
200 }
201
202 pub fn start_rename(&mut self, id: usize, current_name: &str) {
206 self.renaming_id = Some(id);
207 self.rename_text = current_name.to_string();
208 }
209
210 pub fn cancel_rename(&mut self) {
212 self.renaming_id = None;
213 self.rename_text.clear();
214 }
215
216 pub fn finish_rename(&mut self) -> Option<(usize, String)> {
218 if let Some(id) = self.renaming_id.take() {
219 let name = std::mem::take(&mut self.rename_text);
220 if !name.is_empty() {
221 return Some((id, name));
222 }
223 }
224 None
225 }
226
227 pub fn start_drag(&mut self, id: usize, pos: egui::Pos2) {
231 self.dragging_id = Some(id);
232 self.drag_start_pos = Some(pos);
233 }
234
235 pub fn set_drop_target(&mut self, target: Option<usize>) {
237 self.drop_target = target;
238 }
239
240 pub fn end_drag(&mut self) -> Option<(usize, usize)> {
242 let result = match (self.dragging_id, self.drop_target) {
243 (Some(src), Some(dst)) if src != dst => Some((src, dst)),
244 _ => None,
245 };
246 self.dragging_id = None;
247 self.drag_start_pos = None;
248 self.drop_target = None;
249 result
250 }
251
252 pub fn is_dragging(&self) -> bool {
254 self.dragging_id.is_some()
255 }
256
257 pub fn sync_selected_drawing(&mut self, selected_drawing: Option<usize>) {
261 if let Some(drawing_id) = selected_drawing
262 && !self.selected_ids.contains(&drawing_id)
263 {
264 self.selected_ids.clear();
265 self.selected_ids.insert(drawing_id);
266 self.last_selected_id = Some(drawing_id);
267 }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn test_selection() {
277 let mut state = ObjectTreeState::new();
278
279 state.select(1);
280 assert!(state.is_selected(1));
281 assert_eq!(state.selection_count(), 1);
282
283 state.toggle_selection(2);
284 assert!(state.is_selected(1));
285 assert!(state.is_selected(2));
286 assert_eq!(state.selection_count(), 2);
287
288 state.toggle_selection(1);
289 assert!(!state.is_selected(1));
290 assert!(state.is_selected(2));
291 }
292
293 #[test]
294 fn test_range_select() {
295 let mut state = ObjectTreeState::new();
296 let ids = vec![1, 2, 3, 4, 5];
297
298 state.select(2);
299 state.range_select(4, &ids);
300
301 assert!(state.is_selected(2));
302 assert!(state.is_selected(3));
303 assert!(state.is_selected(4));
304 assert!(!state.is_selected(1));
305 assert!(!state.is_selected(5));
306 }
307}