use gtk::{glib, prelude::*, Box, Entry, Label, ListBox}; use gtk::{Application, ApplicationWindow, Button}; use log::{error, info}; use serde::{Deserialize, Serialize}; use tokio::runtime::Handle; use tokio_tungstenite::tungstenite::Message; use std::sync::{atomic::AtomicBool, Arc}; use crate::config::load_config; // use crate::{joystick_loop, JoystickThreadUpdate}; use crate::coordinator::{ApplicationEvent, start_coordinator}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct AppState { pub ip: String, pub port: u32, } impl Default for AppState { fn default() -> Self { AppState { ip: "10.0.0.30".to_string(), port: 8765, } } } pub fn build_ui(app: &Application, runtime: Handle) { let initial_settings = load_config(); let main_box = ListBox::new(); let do_run: Arc = Arc::new(AtomicBool::new(true)); // let do_run2 = do_run.clone(); // let (tx, rx) = async_channel::bounded::(4); // let (tx2, rx2) = async_channel::bounded::(1); // let _ = std::thread::spawn(move || joystick_loop::joystick_websocket_loop(tx, do_run2, rx2)); // Main Event Channel let (to_mec, mec) = async_channel::unbounded::(); let (to_gui, gui_recv) = async_channel::bounded::(10); runtime.spawn(start_coordinator(mec, to_mec.clone(), to_gui, runtime.clone())); // let conn_status_label = Label::new(Some(&"No Connection".to_string())); let conn_status_label = Label::builder() .label("No Connection".to_string()) .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) .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 axis_label = Label::builder() .label("X: 0 Y: )") .justify(gtk::Justification::Center) .css_classes(vec!["JoystickCurrent"]) .build(); main_box.append(&conn_status_label); main_box.append(&content_box); main_box.append(&axis_label); main_box.append(&button2); // Connect to "clicked" signal of `button` button.connect_clicked(glib::clone!(@weak ip_entry, @weak port_entry, @strong to_mec => move |_button| { // Set the label to "Hello World!" after the button has been clicked on let ip_text = ip_entry.text(); let port_text = port_entry.text(); // &format!("ws://{}:{}", "localhost", "5000"), if ip_text.len() > 0 { if let Ok(val) = port_text.parse::() { 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 => async move { while let Ok(d) = gui_recv.recv().await { match d { ApplicationEvent::MoveEvent(msg) => { axis_label.set_text( format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str() ); } ApplicationEvent::SocketState(v) => { let label = { if v { ip_entry.set_sensitive(false); port_entry.set_sensitive(false); "Currently Connected" } else { ip_entry.set_sensitive(true); port_entry.set_sensitive(true); "Currently Disconnected" } }; button.set_label(label); conn_status_label.set_label(label); if v { conn_status_label.set_css_classes(&["YesConnection"]); button.set_css_classes(&["YesConnection"]); } else { conn_status_label.set_css_classes(&["NoConnection"]); button.set_css_classes(&["NoConnection"]); } } _ => { info!("Note, the gui_recv received an unhandled update"); } } } }), ); // Create a window let window = ApplicationWindow::builder() .application(app) .title("VCC Camera Controller") .child(&main_box) .build(); window.connect_close_request(move |_| { do_run.store(false, std::sync::atomic::Ordering::SeqCst); glib::Propagation::Proceed }); // Present window window.present(); }