vcs-controller/src/ui_code.rs

171 lines
6 KiB
Rust
Raw Normal View History

2024-03-25 16:28:13 -07:00
use gtk::{glib, prelude::*, Box, Entry, Label, ListBox};
use gtk::{Application, ApplicationWindow, Button};
2024-04-05 20:58:29 -07:00
use log::{error, info};
2024-03-25 16:28:13 -07:00
use serde::{Deserialize, Serialize};
2024-04-05 20:58:29 -07:00
use tokio::runtime::Handle;
use tokio_tungstenite::tungstenite::Message;
use std::sync::{atomic::AtomicBool, Arc};
2024-03-25 16:28:13 -07:00
use crate::config::load_config;
2024-04-05 20:58:29 -07:00
// use crate::{joystick_loop, JoystickThreadUpdate};
use crate::coordinator::{ApplicationEvent, start_coordinator};
2024-03-25 16:28:13 -07:00
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AppState {
pub ip: String,
pub port: u32,
}
impl Default for AppState {
fn default() -> Self {
AppState {
2024-03-27 17:11:16 -07:00
ip: "10.0.0.30".to_string(),
2024-03-25 16:28:13 -07:00
port: 8765,
}
}
}
2024-04-05 20:58:29 -07:00
pub fn build_ui(app: &Application, runtime: Handle) {
2024-03-25 16:28:13 -07:00
let initial_settings = load_config();
let main_box = ListBox::new();
let do_run: Arc<AtomicBool> = Arc::new(AtomicBool::new(true));
2024-04-05 20:58:29 -07:00
// let do_run2 = do_run.clone();
// let (tx, rx) = async_channel::bounded::<JoystickThreadUpdate>(4);
// let (tx2, rx2) = async_channel::bounded::<SocketConnectionUpdate>(1);
// let _ = std::thread::spawn(move || joystick_loop::joystick_websocket_loop(tx, do_run2, rx2));
2024-03-25 16:28:13 -07:00
2024-04-05 20:58:29 -07:00
// Main Event Channel
let (to_mec, mec) = async_channel::unbounded::<ApplicationEvent>();
let (to_gui, gui_recv) = async_channel::bounded::<ApplicationEvent>(10);
runtime.spawn(start_coordinator(mec, to_mec.clone(), to_gui, runtime.clone()));
2024-03-25 16:28:13 -07:00
// 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();
2024-04-05 20:58:29 -07:00
let button2 = Button::builder().margin_top(12).build();
2024-03-25 16:28:13 -07:00
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);
2024-04-05 20:58:29 -07:00
main_box.append(&button2);
2024-03-25 16:28:13 -07:00
// Connect to "clicked" signal of `button`
2024-04-05 20:58:29 -07:00
button.connect_clicked(glib::clone!(@weak ip_entry, @weak port_entry, @strong to_mec => move |_button| {
2024-03-25 16:28:13 -07:00
// 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();
2024-04-05 20:58:29 -07:00
// &format!("ws://{}:{}", "localhost", "5000"),
2024-03-25 16:28:13 -07:00
if ip_text.len() > 0 {
if let Ok(val) = port_text.parse::<u32>() {
2024-04-05 20:58:29 -07:00
match to_mec.try_send(ApplicationEvent::StartSocket(
format!("ws://{}:{}", ip_text, val),
)) {
2024-03-25 16:28:13 -07:00
Ok(_) => { }
2024-04-05 20:58:29 -07:00
Err(async_channel::TrySendError::Closed(_)) => {panic!("Coordinator MEC is closed. Unrecoverable error.")}
Err(e) => {error!("There was an error sending to the MEC: {}", e)}
2024-03-25 16:28:13 -07:00
}
}
}
}));
2024-04-05 20:58:29 -07:00
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}");
}
}));
2024-03-25 16:28:13 -07:00
glib::spawn_future_local(
2024-04-05 20:58:29 -07:00
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");
2024-03-25 16:28:13 -07:00
}
}
}
}),
);
// 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();
}