diff --git a/src/coordinator/remote_video_processor.rs b/src/coordinator/remote_video_processor.rs index 4d5a5ee..19d15f3 100644 --- a/src/coordinator/remote_video_processor.rs +++ b/src/coordinator/remote_video_processor.rs @@ -187,7 +187,6 @@ async fn listen_to_messages( Ok(None) => { info!("Recieved an empty message from the remote computer: Aborting"); break; - } Err(e) => { error!("Got an error on while recieving from remote computer: {e}"); diff --git a/src/ui/liveview_panel.rs b/src/ui/liveview_panel.rs index b404c1a..ef017b7 100644 --- a/src/ui/liveview_panel.rs +++ b/src/ui/liveview_panel.rs @@ -1,4 +1,7 @@ -use std::sync::{Arc, Mutex}; +use std::{ + cmp::Ordering, + sync::{Arc, Mutex}, +}; use gtk::{ gdk::Paintable, @@ -6,10 +9,10 @@ use gtk::{ AspectFrame, Box, DrawingArea, EventControllerMotion, GestureClick, Label, Overlay, Picture, }; -use tracing::info; - use crate::remote_sources::TrackerState; +use super::NormalizedBoxCoords; + pub struct LiveViewPanel { top_level: gtk::Box, @@ -59,7 +62,7 @@ impl LiveViewPanel { let move_handler = EventControllerMotion::builder() .propagation_limit(gtk::PropagationLimit::SameNative) .build(); - + let tracker_state_2 = tracker_state.clone(); let handler_picture = webcam_picture.clone(); @@ -110,20 +113,9 @@ impl LiveViewPanel { let y_coord = y_coord as f32 / y_size as f32; if let Ok(mut ts) = tracker_state.lock() { - let mut select: Option = None; - for nb in ts.identity_boxes.iter() { - if nb.x1 < x_coord as f32 - && nb.y1 < y_coord as f32 - && nb.x2 > x_coord as f32 - && nb.y2 > y_coord as f32 - { - select = Some(nb.id); - } - } - if let Some(new_id) = select { - ts.tracking_id = new_id; - } + ts.highlighted_id = calc_box_under_mouse(&ts.identity_boxes, x_coord, y_coord); } + } fn motion_callback( @@ -134,21 +126,13 @@ impl LiveViewPanel { ) { let x_size = overlay.size(gtk::Orientation::Horizontal); let y_size = overlay.size(gtk::Orientation::Vertical); - let x_coord = x_coord as f32 / x_size as f32; let y_coord = y_coord as f32 / y_size as f32; + if let Ok(mut ts) = tracker_state.lock() { - let mut highlighted_id: Option = None; - for nb in ts.identity_boxes.iter() { - if nb.x1 < x_coord as f32 - && nb.y1 < y_coord as f32 - && nb.x2 > x_coord as f32 - && nb.y2 > y_coord as f32 - { - highlighted_id = Some(nb.id); - } + if let Some(v) = calc_box_under_mouse(&ts.identity_boxes, x_coord, y_coord) { + ts.tracking_id = v; } - ts.highlighted_id = highlighted_id; } } @@ -164,3 +148,65 @@ impl LiveViewPanel { self.picture.set_paintable(Some(new_paintable)); } } + +fn calc_box_under_mouse(boxes: &Vec, x_coord: f32, y_coord: f32) -> Option { + let mut mouse_over: Vec = vec![]; + + for nb in boxes.iter() { + if nb.x1 < x_coord as f32 + && nb.y1 < y_coord as f32 + && nb.x2 > x_coord as f32 + && nb.y2 > y_coord as f32 + { + mouse_over.push(nb.clone()); + } + } + if mouse_over.len() == 1 { + // the length is one, why would the unwrap fail + let new_id = mouse_over.first().expect("Length one vec had no first"); + return Some(new_id.id); + } else if mouse_over.len() > 1 { + let mut x_coords = mouse_over + .iter() + .flat_map(|b| [b.x1, b.x2]) + .collect::>(); + x_coords.sort_by(|a, b| a.partial_cmp(b).unwrap()); + + let mid = x_coords.len() / 2; + let median_two_x = &x_coords[mid - 1..mid + 1]; + + let mut y_coords = mouse_over + .iter() + .flat_map(|b| [b.y1, b.y2]) + .collect::>(); + y_coords.sort_by(|a, b| a.partial_cmp(b).unwrap()); + let median_two_y = &y_coords[mid - 1..mid + 1]; + + let overlap_area = + (median_two_x[1] - median_two_x[0]) * (median_two_y[1] - median_two_y[0]); + + mouse_over.sort_by(|a, b| { + // a is valid for cutting in line if the + // overlap area is greater than 75% of a's area + let a_overlap = overlap_area >= 0.75 * a.area(); + let b_overlap = overlap_area >= 0.75 * b.area(); + + match (a_overlap, b_overlap) { + // if the areas are not valid for cutting in line or both are + // do the sorting based on the id, and let the hover affect + // force the user to decide + (true, true) | (false, false) => { + if a.id > b.id { + Ordering::Greater + } else { + Ordering::Less + } + } + (false, true) => Ordering::Less, + (true, false) => Ordering::Greater, + } + }); + } + + mouse_over.iter().map(|x| x.id).collect::>().first().copied() +} \ No newline at end of file diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 8db1ab8..198c6eb 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -30,7 +30,7 @@ pub enum GuiUpdate { UpdatePaintable(gstreamer::Element), } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct BoxCoords { pub id: u32, pub x1: u32, @@ -49,7 +49,7 @@ impl Display for BoxCoords { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct NormalizedBoxCoords { pub id: u32, pub x1: f32, @@ -68,6 +68,10 @@ impl NormalizedBoxCoords { y2: (self.y2 * height as f32) as u32, } } + + fn area(&self) -> f32 { + (self.x2 - self.x1) * (self.y2 - self.y1) + } } impl Display for NormalizedBoxCoords { @@ -101,7 +105,7 @@ pub fn on_activate(app: &Application) { }; let menubar = gio::Menu::new(); - menubar.append_submenu(Some("Connections"), &connections_menu); + menubar.append_submenu(Some("Settings"), &connections_menu); menubar }; @@ -128,10 +132,10 @@ pub fn build_ui(app: &Application, config: Arc>, runtime: Hand .build(); let rt = runtime.clone(); - let config_modal_config = config.clone(); + let connections_modal_config = config.clone(); let connections_activate = gio::ActionEntry::builder("connections") .activate(clone!(@weak window => move |app: >k::Application, _, _| { - let connections_modal = settings_modal::ConnectionsModal::new(&app, &window, &rt, &config_modal_config); + let connections_modal = settings_modal::ConnectionsModal::new(&app, &window, &rt, &connections_modal_config); connections_modal.window.present(); })) @@ -169,7 +173,6 @@ pub fn build_ui(app: &Application, config: Arc>, runtime: Hand let control_panel = ControlPanel::new(tracker_state.clone()); control_panel.connect_button_callbacks(to_mec.clone()); - // left_box.append(&conn_status_label); left_box.append(control_panel.get_top_level()); main_box.append(&left_box);