1use tracing::{info, warn};
2
3use super::attributes;
4use crate::history::Record;
5use crate::result::trace_ok_err;
6use crate::tools_data::annotations::{ClipboardData, InstanceAnnotations};
7use crate::tools_data::attributes_data::{self, AttrVal};
8use crate::tools_data::{vis_from_lfoption, InstanceAnnotate, LabelInfo};
9use crate::util::Visibility;
10use crate::world::InstanceAnnoAccess;
11use crate::ShapeI;
12use crate::{
13 events::Events,
14 history::History,
15 world,
16 world::{MetaDataAccess, World},
17};
18use rvimage_domain::PtF;
19use std::mem;
20
21pub(super) fn make_track_changes_str(actor: &'static str) -> String {
22 format!("{actor}_TRACK_CHANGE")
23}
24pub(super) fn insert_attribute(
25 mut world: World,
26 name: &str,
27 value: AttrVal,
28 default_value: AttrVal,
29 filepath: Option<&str>,
30) -> World {
31 let mut old_attr_name = String::new();
32 let mut old_attr_val = AttrVal::Bool(false);
33
34 if let Ok(attr_data) = world::get_mut(&mut world, attributes::ACTOR_NAME, "Attr data missing") {
35 let populate_new_attr = attr_data.specifics.attributes().map(|a| {
37 a.attr_names()
38 .iter()
39 .any(|attr_name| attr_name.as_str() == name)
40 }) != Ok(true);
41
42 trace_ok_err(attr_data.specifics.attributes_mut().map(|d| {
44 let attr_options = attributes_data::Options {
45 is_export_triggered: false,
46 is_addition_triggered: populate_new_attr,
47 rename_src_idx: None,
48 is_update_triggered: false,
49 export_only_opened_folder: false,
50 removal_idx: None,
51 };
52 old_attr_name.clone_from(&d.new_attr_name);
53 old_attr_val.clone_from(&d.new_attr_val);
54 d.new_attr_name = name.to_string();
55 d.new_attr_val = default_value;
56 d.options = attr_options;
57 }));
58 }
59
60 (world, _) = attributes::Attributes {}.events_tf(world, History::default(), &Events::default());
62
63 if let Ok(attr_data) = world::get_mut(&mut world, attributes::ACTOR_NAME, "Attr data missing") {
65 let attr_options = attributes_data::Options {
66 is_export_triggered: false,
67 is_addition_triggered: false,
68 rename_src_idx: None,
69 is_update_triggered: true,
70 export_only_opened_folder: false,
71 removal_idx: None,
72 };
73 trace_ok_err(attr_data.specifics.attributes_mut().map(|d| {
74 d.options = attr_options;
75 let attr_map = if let Some(filepath) = filepath {
76 d.attr_map_mut(filepath)
77 } else {
78 d.current_attr_map.as_mut()
79 };
80 if let Some(attr_map) = attr_map {
81 attr_map.insert(name.to_string(), value);
82 } else {
83 warn!("no attrmap found");
84 }
85 }));
86 }
87 (world, _) = attributes::Attributes {}.events_tf(world, History::default(), &Events::default());
88
89 if let Ok(attr_data) = world::get_mut(&mut world, attributes::ACTOR_NAME, "Attr data missing") {
90 trace_ok_err(attr_data.specifics.attributes_mut().map(|d| {
92 d.new_attr_name = old_attr_name;
93 d.new_attr_val = old_attr_val;
94 }));
95 }
96
97 world
98}
99
100pub(super) fn change_annos<T, DA, IA>(
101 world: &mut World,
102 f_change: impl FnOnce(&mut InstanceAnnotations<T>),
103) where
104 T: InstanceAnnotate,
105 DA: MetaDataAccess,
106 IA: InstanceAnnoAccess<T>,
107{
108 if let Some(annos) = IA::get_annos_mut(world) {
109 f_change(annos);
110 }
111 let track_changes_str = DA::get_track_changes_str(world);
112 if let Some(track_changes_str) = track_changes_str {
113 *world = insert_attribute(
114 mem::take(world),
115 track_changes_str,
116 AttrVal::Bool(true),
117 AttrVal::Bool(false),
118 None,
119 );
120 }
121}
122pub(super) fn check_trigger_redraw<DC>(mut world: World, name: &'static str) -> World
123where
124 DC: MetaDataAccess,
125{
126 let core_options = DC::get_core_options(&world).copied();
127 let is_redraw_triggered = core_options.map(|o| o.is_redraw_annos_triggered);
128 if is_redraw_triggered == Some(true) {
129 let visibility = vis_from_lfoption(
130 DC::get_label_info(&world),
131 core_options.map(|o| o.visible) == Some(true),
132 );
133 world.request_redraw_annotations(name, visibility);
134 let core_options_mut = DC::get_core_options_mut(&mut world);
135 if let Some(core_options_mut) = core_options_mut {
136 core_options_mut.is_redraw_annos_triggered = false;
137 }
138 }
139 world
140}
141
142pub(super) fn check_trigger_history_update<DC>(
143 mut world: World,
144 mut history: History,
145 name: &'static str,
146) -> (World, History)
147where
148 DC: MetaDataAccess,
149{
150 let core_options = DC::get_core_options_mut(&mut world).copied();
151 let is_history_update_triggered = core_options.map(|o| o.is_history_update_triggered);
152 if is_history_update_triggered == Some(true) {
153 let core_options_mut = DC::get_core_options_mut(&mut world);
154 if let Some(core_options_mut) = core_options_mut {
155 core_options_mut.is_history_update_triggered = false;
156 }
157 history.push(Record::new(world.clone(), name));
158 }
159 (world, history)
160}
161
162macro_rules! event_2_actionenum {
163 ($name:ident, $func:ident, $map_func:ident, $($key:ident),*) => {
164 #[derive(Debug, Clone, Copy)]
165 pub(super) enum $name {
166 None,
167 $($key,)*
168 }
169 pub(super) fn $map_func(event: &Events) -> $name {
170 if false {
171 $name::None
172 } $(else if event.$func($crate::KeyCode::$key) {
173 $name::$key
174 })*
175 else {
176 $name::None
177 }
178 }
179 };
180}
181
182event_2_actionenum!(
183 ReleasedKey,
184 released,
185 map_released_key,
186 A,
187 D,
188 E,
189 H,
190 C,
191 I,
192 T,
193 V,
194 L,
195 Key0,
196 Key1,
197 Key2,
198 Key3,
199 Key4,
200 Key5,
201 Key6,
202 Key7,
203 Key8,
204 Key9,
205 Delete,
206 Back,
207 Left,
208 Right,
209 Up,
210 Down
211);
212event_2_actionenum!(HeldKey, held, map_held_key, I, T);
213macro_rules! set_cat_current {
214 ($num:expr, $label_info:expr) => {
215 if $num < $label_info.cat_ids().len() + 1 {
216 if $label_info.cat_idx_current == $num - 1 {
217 $label_info.show_only_current = !$label_info.show_only_current;
218 } else {
219 $label_info.cat_idx_current = $num - 1;
220 $label_info.show_only_current = false;
221 }
222 true
223 } else {
224 false
225 }
226 };
227}
228
229fn replace_annotations_with_clipboard<T, DA, IA>(
230 mut world: World,
231 history: History,
232 actor: &'static str,
233 clipboard: Option<&ClipboardData<T>>,
234) -> (World, History)
235where
236 T: InstanceAnnotate,
237 DA: MetaDataAccess,
238 IA: InstanceAnnoAccess<T>,
239{
240 let annos = IA::get_annos_mut(&mut world);
241 if let Some(annos) = annos {
242 let all = (0..annos.elts().len()).collect::<Vec<_>>();
243 annos.remove_multiple(&all);
244 }
245 paste::<T, DA, IA>(world, history, actor, clipboard)
246}
247pub(super) fn check_autopaste<T, DA, IA>(
248 mut world: World,
249 mut history: History,
250 actor: &'static str,
251) -> (World, History)
252where
253 T: InstanceAnnotate,
254 DA: MetaDataAccess,
255 IA: InstanceAnnoAccess<T>,
256{
257 let clipboard_data = IA::get_clipboard(&world).cloned();
258 let auto_paste = DA::get_core_options_mut(&mut world).is_some_and(|o| o.auto_paste);
259 if world.data.meta_data.flags.is_loading_screen_active == Some(false) && auto_paste {
260 history.push(Record::new(world.clone(), actor));
261 replace_annotations_with_clipboard::<T, DA, IA>(
262 world,
263 history,
264 actor,
265 clipboard_data.as_ref(),
266 )
267 } else {
268 (world, history)
269 }
270}
271pub fn check_erase_mode<AC>(
272 released_key: ReleasedKey,
273 set_visible: impl Fn(&mut World),
274 mut world: World,
275) -> World
276where
277 AC: MetaDataAccess,
278{
279 if let (ReleasedKey::E, Some(core_options)) =
280 (released_key, AC::get_core_options_mut(&mut world))
281 {
282 if core_options.erase {
283 info!("stop erase via shortcut");
284 } else {
285 info!("start erase via shortcut");
286 }
287 core_options.visible = true;
288 core_options.erase = !core_options.erase;
289 set_visible(&mut world);
290 }
291 world
292}
293
294pub fn check_recolorboxes<AC>(mut world: World, actor: &'static str) -> World
295where
296 AC: MetaDataAccess,
297{
298 let is_colorchange_triggered =
299 AC::get_core_options_mut(&mut world).map(|o| o.is_colorchange_triggered);
300 if is_colorchange_triggered == Some(true) {
301 let core_options = AC::get_core_options_mut(&mut world);
302 if let Some(core_options) = core_options {
303 core_options.is_colorchange_triggered = false;
304 core_options.visible = true;
305 }
306 if let Some(label_info) = AC::get_label_info_mut(&mut world) {
307 label_info.new_random_colors();
308 }
309 let visibility = vis_from_lfoption(AC::get_label_info_mut(&mut world).map(|x| &*x), true);
310 world.request_redraw_annotations(actor, visibility);
311 }
312 world
313}
314pub(super) fn label_change_key(key: ReleasedKey, mut label_info: LabelInfo) -> (LabelInfo, bool) {
315 let label_change = match key {
316 ReleasedKey::Key1 => {
317 set_cat_current!(1, label_info)
318 }
319 ReleasedKey::Key2 => {
320 set_cat_current!(2, label_info)
321 }
322 ReleasedKey::Key3 => {
323 set_cat_current!(3, label_info)
324 }
325 ReleasedKey::Key4 => {
326 set_cat_current!(4, label_info)
327 }
328 ReleasedKey::Key5 => {
329 set_cat_current!(5, label_info)
330 }
331 ReleasedKey::Key6 => {
332 set_cat_current!(6, label_info)
333 }
334 ReleasedKey::Key7 => {
335 set_cat_current!(7, label_info)
336 }
337 ReleasedKey::Key8 => {
338 set_cat_current!(8, label_info)
339 }
340 ReleasedKey::Key9 => {
341 set_cat_current!(9, label_info)
342 }
343 _ => false,
344 };
345 (label_info, label_change)
346}
347pub(super) fn paste<T, DA, IA>(
348 mut world: World,
349 mut history: History,
350 actor: &'static str,
351 clipboard: Option<&ClipboardData<T>>,
352) -> (World, History)
353where
354 T: InstanceAnnotate,
355 DA: MetaDataAccess,
356 IA: InstanceAnnoAccess<T>,
357{
358 if let Some(clipboard) = clipboard {
359 let cb_bbs = clipboard.elts();
360 if !cb_bbs.is_empty() {
361 let shape_orig = ShapeI::from_im(world.data.im_background());
362 let paste_annos = |a: &mut InstanceAnnotations<T>| {
363 a.extend(
364 cb_bbs.iter().cloned(),
365 clipboard.cat_idxs().iter().copied(),
366 shape_orig,
367 );
368 };
369 change_annos::<T, DA, IA>(&mut world, paste_annos);
370 }
371 let options_mut = DA::get_core_options_mut(&mut world);
372 if let Some(options_mut) = options_mut {
373 options_mut.visible = true;
374 }
375 let visible = DA::get_core_options_mut(&mut world).map(|o| o.visible) == Some(true);
376 let vis = vis_from_lfoption(DA::get_label_info(&world), visible);
377 world.request_redraw_annotations(actor, vis);
378 history.push(Record::new(world.clone(), actor));
379 }
380
381 (world, history)
382}
383pub fn deselect_all<T, DA, IA>(mut world: World, actor: &'static str) -> World
384where
385 T: InstanceAnnotate,
386 DA: MetaDataAccess,
387 IA: InstanceAnnoAccess<T>,
388{
389 if let Some(a) = IA::get_annos_mut(&mut world) {
391 a.deselect_all();
392 };
393 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
394 world.request_redraw_annotations(actor, vis);
395 world
396}
397
398pub(super) fn on_selection_keys<T, DA, IA>(
399 mut world: World,
400 mut history: History,
401 key: ReleasedKey,
402 is_ctrl_held: bool,
403 actor: &'static str,
404) -> (World, History)
405where
406 T: InstanceAnnotate,
407 DA: MetaDataAccess,
408 IA: InstanceAnnoAccess<T>,
409{
410 match key {
411 ReleasedKey::A if is_ctrl_held => {
412 let current_active_idx = DA::get_label_info(&world).and_then(|li| {
414 if li.show_only_current {
415 Some(li.cat_idx_current)
416 } else {
417 None
418 }
419 });
420 let options = DA::get_core_options_mut(&mut world);
421 if options.map(|o| o.visible) == Some(true) {
422 if let (Some(current_active), Some(a)) =
423 (current_active_idx, IA::get_annos_mut(&mut world))
424 {
425 let relevant_indices = a
426 .cat_idxs()
427 .iter()
428 .enumerate()
429 .filter(|(_, cat_idx)| **cat_idx == current_active)
430 .map(|(i, _)| i)
431 .collect::<Vec<_>>();
432 a.select_multi(relevant_indices.into_iter());
433 } else if let Some(a) = IA::get_annos_mut(&mut world) {
434 a.select_all();
435 };
436 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
437 world.request_redraw_annotations(actor, vis);
438 }
439 }
440 ReleasedKey::D if is_ctrl_held => {
441 world = deselect_all::<_, DA, IA>(world, actor);
442 }
443 ReleasedKey::C if is_ctrl_held => {
444 let clipboard_data_new =
446 IA::get_annos(&world).map(|d| ClipboardData::from_annotations(d));
447 IA::set_clipboard(&mut world, clipboard_data_new);
448 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
449 world.request_redraw_annotations(actor, vis);
450 }
451 ReleasedKey::V if is_ctrl_held => {
452 let clipboard_data = IA::get_clipboard(&world).cloned();
453 (world, history) = paste::<_, DA, IA>(world, history, actor, clipboard_data.as_ref());
454 }
455 ReleasedKey::V if !is_ctrl_held => {
456 let clipboard_data = IA::get_clipboard(&world).cloned();
457 if let Some(options_mut) = DA::get_core_options_mut(&mut world) {
458 options_mut.auto_paste = !options_mut.auto_paste;
459 if options_mut.auto_paste {
460 (world, history) = replace_annotations_with_clipboard::<T, DA, IA>(
461 world,
462 history,
463 actor,
464 clipboard_data.as_ref(),
465 );
466 }
467 }
468 }
469 ReleasedKey::Delete | ReleasedKey::Back => {
470 let del_annos = |annos: &mut InstanceAnnotations<T>| {
472 if !annos.selected_mask().is_empty() {
473 annos.remove_selected();
474 }
475 };
476 change_annos::<T, DA, IA>(&mut world, del_annos);
477 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
478 world.request_redraw_annotations(actor, vis);
479 history.push(Record::new(world.clone(), actor));
480 }
481 _ => (),
482 }
483 (world, history)
484}
485
486pub trait Manipulate {
487 fn new() -> Self
488 where
489 Self: Sized;
490
491 fn on_activate(&mut self, world: World) -> World {
492 world
493 }
494 fn on_deactivate(&mut self, world: World) -> World {
495 world
496 }
497 fn on_filechange(&mut self, world: World, history: History) -> (World, History) {
498 (world, history)
499 }
500 fn on_always_active_zoom(&mut self, world: World, history: History) -> (World, History) {
501 (world, history)
502 }
503 fn has_been_used(&self, _: &Events) -> Option<bool> {
507 None
508 }
509 fn events_tf(&mut self, world: World, history: History, events: &Events) -> (World, History);
512
513 fn get_visibility(&self, _world: &World) -> Visibility {
514 Visibility::None
515 }
516}
517
518const N_HIST_ELTS: usize = 8;
519
520#[derive(Clone, Copy, Debug)]
521pub struct Mover {
522 mouse_pos_start: Option<PtF>,
523 mouse_pos_history: [Option<PtF>; N_HIST_ELTS],
524 idx_next_history_update: usize,
525}
526impl Mover {
527 pub fn new() -> Self {
528 Self {
529 mouse_pos_start: None,
530 mouse_pos_history: [None; N_HIST_ELTS],
531 idx_next_history_update: 0,
532 }
533 }
534 pub fn move_mouse_held<T, F: FnOnce(PtF, PtF) -> T>(
535 &mut self,
536 f_move: F,
537 mouse_pos: Option<PtF>,
538 ) -> Option<T> {
539 let res = if let (Some(mp_start), Some(mp)) = (self.mouse_pos_start, mouse_pos) {
540 if self.mouse_pos_history.contains(&mouse_pos) {
541 None
542 } else {
543 let mpo_from = Some(mp_start);
544 let mpo_to = Some(mp);
545 match (mpo_from, mpo_to) {
546 (Some(mp_from), Some(mp_to)) => Some(f_move(mp_from, mp_to)),
547 _ => None,
548 }
549 }
550 } else {
551 None
552 };
553 self.mouse_pos_history[self.idx_next_history_update % N_HIST_ELTS] = self.mouse_pos_start;
554 self.mouse_pos_start = mouse_pos;
555 self.idx_next_history_update = self.idx_next_history_update.wrapping_add(1);
556 res
557 }
558 pub fn move_mouse_pressed(&mut self, mouse_pos: Option<PtF>) {
559 if mouse_pos.is_some() {
560 self.mouse_pos_start = mouse_pos;
561 }
562 }
563}
564
565#[macro_export]
567macro_rules! make_tool_transform {
568 (
569 $self:expr,
570 $world:expr,
571 $history:expr,
572 $events:expr,
573 [$(($key_event:ident, $key_btn:expr, $method_name:ident)),*]
574 ) => {
575 if false {
576 ($world, $history)
577 }
578 $(else if $events.$key_event($key_btn) {
579 $self.$method_name($events, $world, $history)
580 })*
581 else {
582 ($world, $history)
583 }
584 };
585}