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