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::{
12 events::Events,
13 history::History,
14 world,
15 world::{MetaDataAccess, World},
16};
17use crate::{InstanceLabelDisplay, ShapeI};
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 ild = DA::get_core_options(&world)
362 .map(|o| o.instance_label_display)
363 .unwrap_or_default();
364 let shape_orig = ShapeI::from_im(world.data.im_background());
365 let paste_annos = |a: &mut InstanceAnnotations<T>| {
366 a.extend(
367 cb_bbs.iter().cloned(),
368 clipboard.cat_idxs().iter().copied(),
369 shape_orig,
370 ild,
371 );
372 };
373 change_annos::<T, DA, IA>(&mut world, paste_annos);
374 }
375 let options_mut = DA::get_core_options_mut(&mut world);
376 if let Some(options_mut) = options_mut {
377 options_mut.visible = true;
378 }
379 let visible = DA::get_core_options_mut(&mut world).map(|o| o.visible) == Some(true);
380 let vis = vis_from_lfoption(DA::get_label_info(&world), visible);
381 world.request_redraw_annotations(actor, vis);
382 history.push(Record::new(world.clone(), actor));
383 }
384
385 (world, history)
386}
387pub fn deselect_all<T, DA, IA>(mut world: World, actor: &'static str) -> World
388where
389 T: InstanceAnnotate,
390 DA: MetaDataAccess,
391 IA: InstanceAnnoAccess<T>,
392{
393 if let Some(a) = IA::get_annos_mut(&mut world) {
395 a.deselect_all();
396 };
397 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
398 world.request_redraw_annotations(actor, vis);
399 world
400}
401
402pub(super) fn instance_label_display_sort<T, DA, IA>(
403 mut world: World,
404 instance_label_display: InstanceLabelDisplay,
405 actor: &'static str,
406) -> World
407where
408 T: InstanceAnnotate,
409 DA: MetaDataAccess,
410 IA: InstanceAnnoAccess<T>,
411{
412 let annos = IA::get_annos_mut(&mut world);
413 if let Some(annos) = annos {
414 *annos = instance_label_display.sort(mem::take(annos));
415 }
416
417 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
418 world.request_redraw_annotations(actor, vis);
419
420 world
421}
422pub(super) fn check_instance_label_display_change<T, DA, IA>(
423 mut world: World,
424 key: ReleasedKey,
425 actor: &'static str,
426) -> World
427where
428 T: InstanceAnnotate,
429 DA: MetaDataAccess,
430 IA: InstanceAnnoAccess<T>,
431{
432 if let ReleasedKey::L = key {
433 let options = DA::get_core_options_mut(&mut world);
435 if let Some(options) = options {
436 options.instance_label_display = options.instance_label_display.next();
437 tracing::info!(
438 "instance label display changed to {}",
439 options.instance_label_display
440 );
441 }
442
443 let ild = DA::get_core_options(&world)
445 .map(|o| o.instance_label_display)
446 .unwrap_or_default();
447 world = instance_label_display_sort::<_, DA, IA>(world, ild, actor);
448 }
449 world
450}
451
452pub(super) fn on_selection_keys<T, DA, IA>(
453 mut world: World,
454 mut history: History,
455 key: ReleasedKey,
456 is_ctrl_held: bool,
457 actor: &'static str,
458) -> (World, History)
459where
460 T: InstanceAnnotate,
461 DA: MetaDataAccess,
462 IA: InstanceAnnoAccess<T>,
463{
464 match key {
465 ReleasedKey::A if is_ctrl_held => {
466 let current_active_idx = DA::get_label_info(&world).and_then(|li| {
468 if li.show_only_current {
469 Some(li.cat_idx_current)
470 } else {
471 None
472 }
473 });
474 let options = DA::get_core_options_mut(&mut world);
475 if options.map(|o| o.visible) == Some(true) {
476 if let (Some(current_active), Some(a)) =
477 (current_active_idx, IA::get_annos_mut(&mut world))
478 {
479 let relevant_indices = a
480 .cat_idxs()
481 .iter()
482 .enumerate()
483 .filter(|(_, cat_idx)| **cat_idx == current_active)
484 .map(|(i, _)| i)
485 .collect::<Vec<_>>();
486 a.select_multi(relevant_indices.into_iter());
487 } else if let Some(a) = IA::get_annos_mut(&mut world) {
488 a.select_all();
489 };
490 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
491 world.request_redraw_annotations(actor, vis);
492 }
493 }
494 ReleasedKey::D if is_ctrl_held => {
495 world = deselect_all::<_, DA, IA>(world, actor);
496 }
497 ReleasedKey::C if is_ctrl_held => {
498 let clipboard_data_new =
500 IA::get_annos(&world).map(|d| ClipboardData::from_annotations(d));
501 IA::set_clipboard(&mut world, clipboard_data_new);
502 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
503 world.request_redraw_annotations(actor, vis);
504 }
505 ReleasedKey::V if is_ctrl_held => {
506 let clipboard_data = IA::get_clipboard(&world).cloned();
507 (world, history) = paste::<_, DA, IA>(world, history, actor, clipboard_data.as_ref());
508 }
509 ReleasedKey::V if !is_ctrl_held => {
510 let clipboard_data = IA::get_clipboard(&world).cloned();
511 if let Some(options_mut) = DA::get_core_options_mut(&mut world) {
512 options_mut.auto_paste = !options_mut.auto_paste;
513 if options_mut.auto_paste {
514 (world, history) = replace_annotations_with_clipboard::<T, DA, IA>(
515 world,
516 history,
517 actor,
518 clipboard_data.as_ref(),
519 );
520 }
521 }
522 }
523 ReleasedKey::Delete | ReleasedKey::Back => {
524 let del_annos = |annos: &mut InstanceAnnotations<T>| {
526 if !annos.selected_mask().is_empty() {
527 annos.remove_selected();
528 }
529 };
530 change_annos::<T, DA, IA>(&mut world, del_annos);
531 let vis = vis_from_lfoption(DA::get_label_info(&world), true);
532 world.request_redraw_annotations(actor, vis);
533 history.push(Record::new(world.clone(), actor));
534 }
535 _ => (),
536 }
537 (world, history)
538}
539
540pub trait Manipulate {
541 fn new() -> Self
542 where
543 Self: Sized;
544
545 fn on_activate(&mut self, world: World) -> World {
546 world
547 }
548 fn on_deactivate(&mut self, world: World) -> World {
549 world
550 }
551 fn on_filechange(&mut self, world: World, history: History) -> (World, History) {
552 (world, history)
553 }
554 fn on_always_active_zoom(&mut self, world: World, history: History) -> (World, History) {
555 (world, history)
556 }
557 fn has_been_used(&self, _: &Events) -> Option<bool> {
561 None
562 }
563 fn events_tf(&mut self, world: World, history: History, events: &Events) -> (World, History);
566
567 fn get_visibility(&self, _world: &World) -> Visibility {
568 Visibility::None
569 }
570}
571
572const N_HIST_ELTS: usize = 8;
573
574#[derive(Clone, Copy, Debug)]
575pub struct Mover {
576 mouse_pos_start: Option<PtF>,
577 mouse_pos_history: [Option<PtF>; N_HIST_ELTS],
578 idx_next_history_update: usize,
579}
580impl Mover {
581 pub fn new() -> Self {
582 Self {
583 mouse_pos_start: None,
584 mouse_pos_history: [None; N_HIST_ELTS],
585 idx_next_history_update: 0,
586 }
587 }
588 pub fn move_mouse_held<T, F: FnOnce(PtF, PtF) -> T>(
589 &mut self,
590 f_move: F,
591 mouse_pos: Option<PtF>,
592 ) -> Option<T> {
593 let res = if let (Some(mp_start), Some(mp)) = (self.mouse_pos_start, mouse_pos) {
594 if self.mouse_pos_history.contains(&mouse_pos) {
595 None
596 } else {
597 let mpo_from = Some(mp_start);
598 let mpo_to = Some(mp);
599 match (mpo_from, mpo_to) {
600 (Some(mp_from), Some(mp_to)) => Some(f_move(mp_from, mp_to)),
601 _ => None,
602 }
603 }
604 } else {
605 None
606 };
607 self.mouse_pos_history[self.idx_next_history_update % N_HIST_ELTS] = self.mouse_pos_start;
608 self.mouse_pos_start = mouse_pos;
609 self.idx_next_history_update = self.idx_next_history_update.wrapping_add(1);
610 res
611 }
612 pub fn move_mouse_pressed(&mut self, mouse_pos: Option<PtF>) {
613 if mouse_pos.is_some() {
614 self.mouse_pos_start = mouse_pos;
615 }
616 }
617}
618
619#[macro_export]
621macro_rules! make_tool_transform {
622 (
623 $self:expr,
624 $world:expr,
625 $history:expr,
626 $events:expr,
627 [$(($key_event:ident, $key_btn:expr, $method_name:ident)),*]
628 ) => {
629 if false {
630 ($world, $history)
631 }
632 $(else if $events.$key_event($key_btn) {
633 $self.$method_name($events, $world, $history)
634 })*
635 else {
636 ($world, $history)
637 }
638 };
639}