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::{ImportExportTrigger, InstanceAnnotate, LabelInfo, vis_from_lfoption};
9use crate::util::Visibility;
10use crate::world::InstanceAnnoAccess;
11use crate::{InstanceLabelDisplay, ShapeI};
12use crate::{
13 events::Events,
14 history::History,
15 parameters::ParamVal,
16 world,
17 world::{MetaDataAccess, World},
18};
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 is_shift_held: bool,
459 actor: &'static str,
460) -> (World, History)
461where
462 T: InstanceAnnotate,
463 DA: MetaDataAccess,
464 IA: InstanceAnnoAccess<T>,
465{
466 match key {
467 ReleasedKey::A if is_ctrl_held => {
468 let current_active_idx = DA::get_label_info(&world).and_then(|li| {
470 if li.show_only_current {
471 Some(li.cat_idx_current)
472 } else {
473 None
474 }
475 });
476 let options = DA::get_core_options_mut(&mut world);
477 if options.map(|o| o.visible) == Some(true) {
478 let zoom_box = *world.zoom_box();
479 if let Some(a) = IA::get_annos_mut(&mut world) {
480 let relevant_idxs = a
481 .cat_idxs()
482 .iter()
483 .zip(a.elts().iter())
484 .enumerate()
485 .filter(|(_, (cat_idx, elt))| {
486 current_active_idx
487 .map(|caidx| caidx == **cat_idx)
488 .unwrap_or(true)
489 && (zoom_box
490 .map(|zb| zb.contains_bb(elt.enclosing_bb()))
491 .unwrap_or(true)
492 || !is_shift_held)
493 })
494 .map(|(idx, _)| idx)
495 .collect::<Vec<_>>();
496 a.select_multi(relevant_idxs.into_iter());
497 }
498
499 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
500 world.request_redraw_annotations(actor, vis);
501 }
502 }
503 ReleasedKey::D if is_ctrl_held => {
504 world = deselect_all::<_, DA, IA>(world, actor);
505 }
506 ReleasedKey::C if is_ctrl_held => {
507 let clipboard_data_new =
509 IA::get_annos(&world).map(|d| ClipboardData::from_annotations(d));
510 IA::set_clipboard(&mut world, clipboard_data_new);
511 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
512 world.request_redraw_annotations(actor, vis);
513 }
514 ReleasedKey::V if is_ctrl_held => {
515 let clipboard_data = IA::get_clipboard(&world).cloned();
516 (world, history) = paste::<_, DA, IA>(world, history, actor, clipboard_data.as_ref());
517 }
518 ReleasedKey::V if !is_ctrl_held => {
519 let clipboard_data = IA::get_clipboard(&world).cloned();
520 if let Some(options_mut) = DA::get_core_options_mut(&mut world) {
521 options_mut.auto_paste = !options_mut.auto_paste;
522 if options_mut.auto_paste {
523 (world, history) = replace_annotations_with_clipboard::<T, DA, IA>(
524 world,
525 history,
526 actor,
527 clipboard_data.as_ref(),
528 );
529 }
530 }
531 }
532 ReleasedKey::Delete | ReleasedKey::Back => {
533 let del_annos = |annos: &mut InstanceAnnotations<T>| {
535 if !annos.selected_mask().is_empty() {
536 annos.remove_selected();
537 }
538 };
539 change_annos::<T, DA, IA>(&mut world, del_annos);
540 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
541 world.request_redraw_annotations(actor, vis);
542 history.push(Record::new(world.clone(), actor));
543 }
544 _ => (),
545 }
546 (world, history)
547}
548
549pub trait Manipulate {
550 fn new() -> Self
551 where
552 Self: Sized;
553
554 fn on_activate(&mut self, world: World) -> World {
555 world
556 }
557 fn on_deactivate(&mut self, world: World) -> World {
558 world
559 }
560 fn on_filechange(&mut self, world: World, history: History) -> (World, History) {
561 (world, history)
562 }
563 fn on_always_active_zoom(&mut self, world: World, history: History) -> (World, History) {
564 (world, history)
565 }
566 fn has_been_used(&self, _: &Events) -> Option<bool> {
570 None
571 }
572 fn events_tf(&mut self, world: World, history: History, events: &Events) -> (World, History);
575
576 fn get_visibility(&self, _world: &World) -> Visibility {
577 Visibility::None
578 }
579}
580
581const N_HIST_ELTS: usize = 8;
582
583#[derive(Clone, Copy, Debug)]
584pub struct Mover {
585 mouse_pos_start: Option<PtF>,
586 mouse_pos_history: [Option<PtF>; N_HIST_ELTS],
587 idx_next_history_update: usize,
588}
589impl Mover {
590 pub fn new() -> Self {
591 Self {
592 mouse_pos_start: None,
593 mouse_pos_history: [None; N_HIST_ELTS],
594 idx_next_history_update: 0,
595 }
596 }
597 pub fn move_mouse_held<T, F: FnOnce(PtF, PtF) -> T>(
598 &mut self,
599 f_move: F,
600 mouse_pos: Option<PtF>,
601 ) -> Option<T> {
602 let res = if let (Some(mp_start), Some(mp)) = (self.mouse_pos_start, mouse_pos) {
603 if self.mouse_pos_history.contains(&mouse_pos) {
604 None
605 } else {
606 let mpo_from = Some(mp_start);
607 let mpo_to = Some(mp);
608 match (mpo_from, mpo_to) {
609 (Some(mp_from), Some(mp_to)) => Some(f_move(mp_from, mp_to)),
610 _ => None,
611 }
612 }
613 } else {
614 None
615 };
616 if let Some(mph) = self
617 .mouse_pos_history
618 .get_mut(self.idx_next_history_update % N_HIST_ELTS)
619 {
620 *mph = self.mouse_pos_start;
621 }
622 self.mouse_pos_start = mouse_pos;
623 self.idx_next_history_update = self.idx_next_history_update.wrapping_add(1);
624 res
625 }
626 pub fn move_mouse_pressed(&mut self, mouse_pos: Option<PtF>) {
627 if mouse_pos.is_some() {
628 self.mouse_pos_start = mouse_pos;
629 }
630 }
631}
632
633#[macro_export]
635macro_rules! make_tool_transform {
636 (
637 $self:expr,
638 $world:expr,
639 $history:expr,
640 $events:expr,
641 [$(($key_event:ident, $key_btn:expr, $method_name:ident)),*]
642 ) => {
643 if false {
644 ($world, $history)
645 }
646 $(else if $events.$key_event($key_btn) {
647 $self.$method_name($events, $world, $history)
648 })*
649 else {
650 ($world, $history)
651 }
652 };
653}