diff --git a/src/coordinator/mod.rs b/src/coordinator/mod.rs index 0f09358..2e69161 100644 --- a/src/coordinator/mod.rs +++ b/src/coordinator/mod.rs @@ -135,6 +135,10 @@ impl<'a> CoordState<'a> { debug!("Starting socket"); self.sck_alive_recvr.store(true, Ordering::SeqCst); + if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnecting).await { + error!("Cannot send message to gui thread: {e}"); + } + let conn_string: String = { let read_settings = self.settings.read().await; @@ -156,9 +160,16 @@ impl<'a> CoordState<'a> { inbound, )); self.sck_outbound = Some(outbound); + + if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnected).await { + error!("Cannot send message to gui thread: {e}"); + } } Err(_) => { error!("Couldn't connect to URL!"); + if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await { + error!("Cannot send message to gui thread: {e}"); + } } } } @@ -169,6 +180,10 @@ impl<'a> CoordState<'a> { error!("Couldnt' close socket during shutdown: {e}"); } } + + if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await { + error!("Cannot send message to gui thread: {e}"); + } self.sck_alive_recvr.store(false, Ordering::SeqCst); } @@ -230,10 +245,10 @@ impl<'a> CoordState<'a> { if !self.sck_alive_recvr.load(Ordering::SeqCst) || self.sck_outbound.is_none() { self.socket_close().await; - if let Err(e) = self.to_gui.send(GuiUpdate::SocketState(false)).await { + if let Err(e) = self.to_gui.send(GuiUpdate::SocketDisconnected).await { error!("Cannot send message to gui thread: {e}"); } - } else if let Err(e) = self.to_gui.send(GuiUpdate::SocketState(true)).await { + } else if let Err(e) = self.to_gui.send(GuiUpdate::SocketConnected).await { error!("Cannot send message to gui thread: {e}"); self.close().await; } @@ -296,7 +311,7 @@ pub async fn start_coordinator( state.socket_start().await; } ApplicationEvent::SocketMessage(socket_message) => { - if let Err(e) = state.to_gui.send(GuiUpdate::SocketState(true)).await { + if let Err(e) = state.to_gui.send(GuiUpdate::SocketConnected).await { error!("Could not send to gui thread! Closing coordinator: {e}"); state.close().await; break; diff --git a/src/main.rs b/src/main.rs index 2880514..80e9dc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use gtk::{glib, Application}; use std::{env, sync::Arc}; use tokio::{runtime, sync::RwLock}; use tracing::{self, info, Level}; +#[cfg(not(debug_assertions))] use tracing_appender; use tracing_subscriber; @@ -20,9 +21,20 @@ fn main() -> glib::ExitCode { // set the environment var to make gtk use window's default action bar env::set_var("gtk_csd", "0"); - let file_appender = tracing_appender::rolling::daily(".", "joystick-log"); - let (non_blocking, _gaurd) = tracing_appender::non_blocking(file_appender); - tracing_subscriber::fmt().with_writer(non_blocking).init(); + #[cfg(not(debug_assertions))] + { + let file_appender = tracing_appender::rolling::daily(".", "joystick-log"); + let (non_blocking, _gaurd) = tracing_appender::non_blocking(file_appender); + tracing_subscriber::fmt().with_writer(non_blocking).init(); + } + #[cfg(debug_assertions)] + { + tracing_subscriber::fmt() + // .compact() + .pretty() + .with_max_level(tracing::Level::TRACE) + .init(); + } let span = tracing::span!(Level::TRACE, "main"); let _enter = span.enter(); diff --git a/src/ui/tracker_panel.rs b/src/ui/control_panel.rs similarity index 52% rename from src/ui/tracker_panel.rs rename to src/ui/control_panel.rs index e83dde0..a6dde7d 100644 --- a/src/ui/tracker_panel.rs +++ b/src/ui/control_panel.rs @@ -1,16 +1,14 @@ + use std::sync::{Arc, Mutex}; use async_channel::Sender; use gtk::{ - glib::object::CastNone, - prelude::{ - BoxExt, ButtonExt, Cast, GObjectPropertyExpressionExt, ListItemExt, ListModelExt, + glib::{self, object::CastNone}, prelude::{ + BoxExt, ButtonExt, Cast, GObjectPropertyExpressionExt, ListItemExt, ToggleButtonExt, - }, - Box, Label, ListItem, ListView, ScrolledWindow, SignalListItemFactory, SingleSelection, - StringList, StringObject, ToggleButton, Widget, + }, Box, Button, Expander, Label, ListItem, ListView, ScrolledWindow, SignalListItemFactory, SingleSelection, StringList, StringObject, ToggleButton, Widget }; -use tracing::{error, instrument}; +use tracing::{error, span, Level, event}; #[cfg(feature = "tracker-state-debug")] use tracing::debug; @@ -18,19 +16,25 @@ use tracing::debug; use crate::{coordinator::ApplicationEvent, remote_sources::TrackerState}; #[derive(Debug)] -pub struct TrackerPanel { +pub struct ControlPanel { top_level: Box, - enable_disable: ToggleButton, + pub connection_buttons: ExpanderMenu, pub current_id: Label, - scrolled_window: ScrolledWindow, pub items: StringList, - list_view: ListView, } -impl TrackerPanel { - pub fn new(tracker_state: Arc>) -> TrackerPanel { +#[derive(Debug)] +pub struct ExpanderMenu { + pub top_level: Expander, + + pub camera_connection: Button, + pub tracker_enable_toggle: ToggleButton, +} + +impl ControlPanel { + pub fn new(tracker_state: Arc>) -> ControlPanel { let factory = SignalListItemFactory::new(); factory.connect_setup(move |_, list_item| { let list_item = list_item @@ -89,10 +93,7 @@ impl TrackerPanel { .margin_bottom(12) .build(); - let enable_disable = ToggleButton::builder() - .label("Enable Automatic Tracking") - .active(false) - .build(); + let expander = ExpanderMenu::new(); let current_id = Label::builder() .label("Not Tracking") @@ -101,19 +102,17 @@ impl TrackerPanel { .css_classes(["current-id"]) .build(); - top_level.append(&enable_disable); + top_level.append(&expander.top_level); top_level.append(¤t_id); top_level.append(&scrolled_window); - TrackerPanel { + ControlPanel { top_level, - enable_disable, + connection_buttons: expander, current_id, - scrolled_window, items, - list_view, } } @@ -121,14 +120,63 @@ impl TrackerPanel { &self.top_level } - #[instrument] - pub fn connect_button_callback(&self, to_mec: Sender) { - self.enable_disable.connect_clicked(move |button| { + pub fn connect_button_callbacks(&self, to_mec: Sender) { + self.connection_buttons.tracker_enable_toggle.connect_clicked(glib::clone!(@strong to_mec => move |button| { + let span = span!(Level::TRACE, "tracker_enable_toggle callback"); + let _enter = span.enter(); if let Err(e) = to_mec.send_blocking(ApplicationEvent::EnableAutomatic(button.is_active())) { - error!("Could not send message to the MEC: {e}"); + event!(Level::ERROR, error = ?e, "Could not send message to the MEC"); } - }); + })); + + self.connection_buttons.camera_connection.connect_clicked(glib::clone!(@strong to_mec => move |_button| { + let span = span!(Level::TRACE, "camera_connection callback"); + let _enter = span.enter(); + match to_mec.try_send(ApplicationEvent::StartCameraSocket) { + Ok(_) => {}, + Err(async_channel::TrySendError::Closed(_)) => panic!("Coordinator MEC is closed. Unrecoverable error."), + Err(e) => event!(Level::ERROR, error = ?e, message = "There was an error sending to the MEC"), + } + })); + } +} + +impl ExpanderMenu { + pub fn new() -> Self { + let content_box = Box::builder() + .orientation(gtk::Orientation::Vertical) + .spacing(10) + .margin_top(12) + .margin_start(24) + .margin_end(24) + .margin_bottom(12) + .build(); + let expander = Expander::builder() + .child(&content_box) + .expanded(true) + .label("Connections") + .build(); + + let camera_connection = Button::builder() + .label("Connect to Camera") + .margin_top(12) + .build(); + let tracker_enable_toggle = ToggleButton::builder() + .label("Connect to Tracker Computer") + .active(false) + .margin_top(12) + .build(); + + content_box.append(&camera_connection); + content_box.append(&tracker_enable_toggle); + + ExpanderMenu { + top_level: expander, + + camera_connection, + tracker_enable_toggle, + } } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 91bca06..e1c5d2f 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -16,12 +16,9 @@ use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent}; use crate::remote_sources::TrackerState; mod settings_modal; -mod socket_panel; -mod tracker_panel; mod control_panel; -use socket_panel::SocketPanel; -use tracker_panel::TrackerPanel; +use control_panel::ControlPanel; pub enum GuiUpdate { SocketDisconnected, @@ -166,21 +163,8 @@ pub fn build_ui(app: &Application, config: Arc>, runtime: Hand .can_focus(true) .build(); - let tabpanel = gtk::Notebook::builder().enable_popup(true).build(); - - let socket_panel = Arc::new(SocketPanel::new()); - socket_panel.connect_button_callback(to_mec.clone()); - tabpanel.append_page( - socket_panel.get_top_level(), - Some(>k::Label::new(Some("Cam Connection"))), - ); - - let tracker_panel = TrackerPanel::new(tracker_state.clone()); - tracker_panel.connect_button_callback(to_mec.clone()); - tabpanel.append_page( - tracker_panel.get_top_level(), - Some(&Label::new(Some("Auto Settings"))), - ); + let control_panel = Arc::new(ControlPanel::new(tracker_state.clone())); + control_panel.connect_button_callbacks(to_mec.clone()); let axis_label = Label::builder() .label("X: 0 Y: )") @@ -189,7 +173,7 @@ pub fn build_ui(app: &Application, config: Arc>, runtime: Hand .build(); left_box.append(&conn_status_label); - left_box.append(&tabpanel); + left_box.append(control_panel.get_top_level()); left_box.append(&axis_label); main_box.append(&left_box); @@ -214,8 +198,8 @@ pub fn build_ui(app: &Application, config: Arc>, runtime: Hand overlay_box.set_child(Some(&webcam_picture)); overlay_box.add_overlay(&drawable); - let items = tracker_panel.items.clone(); - let id_label = tracker_panel.current_id.clone(); + let items = control_panel.items.clone(); + let id_label = control_panel.current_id.clone(); glib::timeout_add_seconds_local( 1, @@ -251,7 +235,7 @@ pub fn build_ui(app: &Application, config: Arc>, runtime: Hand ); glib::spawn_future_local( - glib::clone!(@weak axis_label, @weak conn_status_label, @weak tabpanel, @strong socket_panel, @strong gui_recv, @weak drawable => async move { + glib::clone!(@weak axis_label, @weak conn_status_label, @weak control_panel, @strong gui_recv, @weak drawable => async move { while let Ok(d) = gui_recv.recv().await { drawable.queue_draw(); match d { @@ -261,31 +245,24 @@ pub fn build_ui(app: &Application, config: Arc>, runtime: Hand ); } GuiUpdate::SocketConnected => { - tabpanel.set_show_tabs(true); - - socket_panel.button_enable(true); - socket_panel.button_label("Press to Disconnect"); + control_panel.connection_buttons.camera_connection.set_sensitive(true); + control_panel.connection_buttons.camera_connection.set_label("Press to Disconnect"); conn_status_label.set_label("Connected"); conn_status_label.set_css_classes(&["YesConnection"]); }, GuiUpdate::SocketConnecting => { - tabpanel.set_show_tabs(true); - - socket_panel.button_enable(true); - socket_panel.button_label("Press to Cancel"); + control_panel.connection_buttons.camera_connection.set_sensitive(true); + control_panel.connection_buttons.camera_connection.set_label("Press to Cancel"); conn_status_label.set_label("Connected"); conn_status_label.set_css_classes(&["LoadingConnection"]); }, GuiUpdate::SocketDisconnected => { - tabpanel.set_page(0); - tabpanel.set_show_tabs(false); - - socket_panel.button_enable(false); - socket_panel.button_label("Press to Connect to Camera"); + control_panel.connection_buttons.camera_connection.set_sensitive(false); + control_panel.connection_buttons.camera_connection.set_label("Press to Connect to Camera"); conn_status_label.set_label("Not Connected to Camera"); conn_status_label.set_css_classes(&["NoConnection"]); diff --git a/src/ui/settings_modal.rs b/src/ui/settings_modal.rs index 2ee6dc2..3dfe11d 100644 --- a/src/ui/settings_modal.rs +++ b/src/ui/settings_modal.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use gtk::glib::{self, clone}; use gtk::{ - prelude::{BoxExt, ButtonExt, EditableExt}, + prelude::{BoxExt, ButtonExt, EditableExt, GtkWindowExt}, Application, ApplicationWindow, Box, Button, Entry, Label, Window, }; use log::{error, info}; @@ -121,7 +121,7 @@ impl ConnectionsModal { let new_ref = app_config.clone(); quit_button.connect_clicked(clone!( - @strong rt, + @strong rt, @weak window, @weak camera_ip_entry, @weak camera_port_entry, @weak tracker_ip_entry, @weak tracker_port_entry, @weak tracker_refresh_millis => move |_| { @@ -150,6 +150,8 @@ impl ConnectionsModal { } // FBI!!! OPEN UP!!!! } + + window.close(); info!("Please nicholas, add a non-crashing parse"); })); diff --git a/src/ui/socket_panel.rs b/src/ui/socket_panel.rs deleted file mode 100644 index 3b04667..0000000 --- a/src/ui/socket_panel.rs +++ /dev/null @@ -1,57 +0,0 @@ -use async_channel::Sender; -use gtk::{ - glib, prelude::{BoxExt, ButtonExt, WidgetExt}, Box, Button -}; -use log::error; - -use crate::coordinator::ApplicationEvent; - -pub struct SocketPanel { - top_level: Box, - - connect_button: Button, -} - -impl SocketPanel { - pub fn new() -> SocketPanel { - let content_box = Box::builder() - .orientation(gtk::Orientation::Vertical) - .spacing(10) - .margin_top(12) - .margin_start(24) - .margin_end(24) - .margin_bottom(12) - .build(); - - let button = Button::builder().margin_top(12).build(); - - content_box.append(&button); - - SocketPanel { - top_level: content_box, - connect_button: button, - } - } - - pub fn get_top_level(&self) -> &Box { - &self.top_level - } - - pub fn button_label(&self, new_label: &str) { - self.connect_button.set_label(new_label); - } - - pub fn button_enable(&self, new_state: bool) { - self.connect_button.set_sensitive(new_state); - } - - pub fn connect_button_callback(&self, to_mec: Sender) { - self.connect_button.connect_clicked(glib::clone!(@strong to_mec => move |_button| { - match to_mec.try_send(ApplicationEvent::StartCameraSocket) { - Ok(_) => {}, - Err(async_channel::TrySendError::Closed(_)) => panic!("Coordinator MEC is closed. Unrecoverable error."), - Err(e) => error!("There was an error sending to the MEC: {}", e), - } - })); - } -} diff --git a/style.css b/style.css index e3778ab..631260d 100644 --- a/style.css +++ b/style.css @@ -19,6 +19,11 @@ entry { font-size: 16pt; } +label.LoadingConnection { + background-color: goldenrod; + color: black; +} + label.NoConnection { background-color: brown; color: whitesmoke;