diff --git a/src/ui/mod.rs b/src/ui/mod.rs index d2107e3..1c58ea8 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,3 +1,4 @@ +use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::time::Instant; @@ -15,6 +16,9 @@ use crate::config::{load_config, save_config}; use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent}; use crate::remote_sources::TrackerState; +mod socket_panel; +use socket_panel::SocketPanel; + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct AppState { pub ip: String, @@ -73,8 +77,8 @@ pub fn build_ui(app: &Application, runtime: Handle) { let window = ApplicationWindow::builder() .application(app) .title("VCC Camera Controller") - // .default_width(840) - // .default_height(480) + .default_width(840) + .default_height(480) .child(&main_box) .build(); @@ -104,31 +108,12 @@ pub fn build_ui(app: &Application, runtime: Handle) { .can_focus(true) .build(); - let content_box = Box::builder() - .orientation(gtk::Orientation::Vertical) - .spacing(10) - .margin_top(12) - .margin_start(24) - .margin_end(24) - .margin_bottom(12) + let tabpanel = gtk::Notebook::builder() .build(); - let ip_entry = Entry::builder() - .placeholder_text("IP Address") - .text(initial_settings.ip) - .can_focus(true) - .build(); - let port_entry = Entry::builder() - .placeholder_text("Port") - .text(initial_settings.port.to_string()) - .can_focus(true) - .build(); - let button = Button::builder().margin_top(12).build(); - let button2 = Button::builder().margin_top(12).build(); - - content_box.append(&ip_entry); - content_box.append(&port_entry); - content_box.append(&button); + let socket_panel = Arc::new(SocketPanel::new(&initial_settings)); + socket_panel.connect_button_callback(to_mec.clone()); + tabpanel.append_page(socket_panel.get_top_level(), Some(>k::Label::new(Some("Cam Connection")))); let axis_label = Label::builder() .label("X: 0 Y: )") @@ -136,16 +121,14 @@ pub fn build_ui(app: &Application, runtime: Handle) { .css_classes(vec!["JoystickCurrent"]) .build(); + left_box.append(&conn_status_label); - left_box.append(&content_box); + left_box.append(&tabpanel); left_box.append(&axis_label); - left_box.append(&button2); main_box.append(&left_box); let webcam_picture = gtk::Picture::builder() - // .height_request(270) - .width_request(640) .can_focus(false) .build(); @@ -171,40 +154,9 @@ pub fn build_ui(app: &Application, runtime: Handle) { overlay_box.set_child(Some(&webcam_picture)); overlay_box.add_overlay(&drawable); - // Connect to "clicked" signal of `button` - button.connect_clicked(glib::clone!(@weak ip_entry, @weak port_entry, @strong to_mec => move |_button| { - let ip_text = ip_entry.text(); - let port_text = port_entry.text(); - - if ip_text.len() == 0 { - return; - } - if let Ok(val) = port_text.parse::() { - if let Err(error) = save_config(&AppState { - ip: ip_text.to_string(), - port: val - }) { - error!("{error}"); - }; - - match to_mec.try_send(ApplicationEvent::StartSocket( - format!("ws://{}:{}", ip_text, val), - )) { - 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), - } - } - })); - - button2.connect_clicked(glib::clone!(@strong to_mec => move |_button| { - if let Err(e) = to_mec.try_send(ApplicationEvent::SocketMessage(Message::text("U45:L10"))) { - panic!("There was an error in connect clicked: {e}"); - } - })); glib::spawn_future_local( - glib::clone!(@weak axis_label, @weak button, @weak conn_status_label, @weak ip_entry, @weak port_entry, @strong gui_recv, @weak drawable => async move { + glib::clone!(@weak axis_label, @weak conn_status_label, @strong socket_panel, @strong gui_recv, @weak drawable => async move { while let Ok(d) = gui_recv.recv().await { drawable.queue_draw(); match d { @@ -216,24 +168,22 @@ pub fn build_ui(app: &Application, runtime: Handle) { GuiUpdate::SocketState(v) => { let label = { if v { - ip_entry.set_sensitive(false); - port_entry.set_sensitive(false); + socket_panel.set_sensitive(false); "Currently Connected" } else { - ip_entry.set_sensitive(true); - port_entry.set_sensitive(true); + socket_panel.set_sensitive(true); "Currently Disconnected" } }; - button.set_label(label); + socket_panel.button_label(label); conn_status_label.set_label(label); if v { conn_status_label.set_css_classes(&["YesConnection"]); - button.set_css_classes(&["YesConnection"]); + // button.set_css_classes(&["YesConnection"]); } else { conn_status_label.set_css_classes(&["NoConnection"]); - button.set_css_classes(&["NoConnection"]); + // button.set_css_classes(&["NoConnection"]); } } GuiUpdate::UpdatePaintable(sink) => { diff --git a/src/ui/socket_panel.rs b/src/ui/socket_panel.rs new file mode 100644 index 0000000..bc8b688 --- /dev/null +++ b/src/ui/socket_panel.rs @@ -0,0 +1,104 @@ +use async_channel::Sender; +use gtk::{glib::{self, GString}, prelude::{BoxExt, ButtonExt, EditableExt, WidgetExt}, Box, Button, Entry}; +use log::error; + +use crate::{config::save_config, coordinator::ApplicationEvent}; + +use super::AppState; + +pub struct SocketPanel { + top_level: Box, + + ip_entry: Entry, + port_entry: Entry, + connect_button: Button, +} + +impl SocketPanel { + pub fn new(initial_settings: &crate::ui::AppState) -> 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 ip_entry = Entry::builder() + .placeholder_text("IP Address") + .text(&initial_settings.ip) + .can_focus(true) + .build(); + let port_entry = Entry::builder() + .placeholder_text("Port") + .text(&initial_settings.port.to_string()) + .can_focus(true) + .build(); + let button = Button::builder().margin_top(12).build(); + + content_box.append(&ip_entry); + content_box.append(&port_entry); + content_box.append(&button); + + SocketPanel { + top_level: content_box, + ip_entry, + port_entry, + connect_button: button, + } + } + + pub fn get_top_level(&self) -> &Box { + &self.top_level + } + + pub fn set_sensitive(&self, is_sensitive: bool) { + self.ip_entry.set_sensitive(is_sensitive); + self.port_entry.set_sensitive(is_sensitive); + } + + pub fn button_label(&self, new_label: &str) { + self.connect_button.set_label(new_label); + } + + pub fn get_ip_text(&self) -> GString { + self.ip_entry.text() + } + + pub fn get_port_text(&self) -> GString { + self.port_entry.text() + } + + pub fn connect_button_callback(&self, to_mec: Sender) { + + let ip_entry = &self.ip_entry; + let port_entry = &self.port_entry; + + self.connect_button.connect_clicked(glib::clone!(@weak ip_entry, @weak port_entry, @strong to_mec => move |_button| { + let ip_text = ip_entry.text(); + let port_text = port_entry.text(); + + if ip_text.len() == 0 { + return; + } + if let Ok(val) = port_text.parse::() { + if let Err(error) = save_config(&AppState { + ip: ip_text.to_string(), + port: val + }) { + error!("{error}"); + }; + + match to_mec.try_send(ApplicationEvent::StartSocket( + format!("ws://{}:{}", ip_text, val), + )) { + 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), + } + } + })); + } +} \ No newline at end of file diff --git a/style.css b/style.css index a2e0f7a..90dca07 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,4 @@ + entry { font-size: 16pt; }