2024-03-25 16:28:13 -07:00
|
|
|
use gtk::{glib, prelude::*, Box, Entry, Label, ListBox};
|
|
|
|
use gtk::{Application, ApplicationWindow, Button};
|
2024-04-06 10:49:19 -07:00
|
|
|
use log::error;
|
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;
|
2024-03-25 16:28:13 -07:00
|
|
|
|
2024-04-06 10:49:19 -07:00
|
|
|
use crate::config::{load_config, save_config};
|
2024-04-05 20:58:29 -07:00
|
|
|
// use crate::{joystick_loop, JoystickThreadUpdate};
|
2024-04-06 10:49:19 -07:00
|
|
|
use crate::coordinator::{start_coordinator, ApplicationEvent, MoveEvent};
|
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-06 10:49:19 -07:00
|
|
|
pub enum GuiUpdate {
|
|
|
|
SocketState(bool),
|
|
|
|
MoveEvent(MoveEvent),
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
2024-04-05 20:58:29 -07:00
|
|
|
// Main Event Channel
|
|
|
|
let (to_mec, mec) = async_channel::unbounded::<ApplicationEvent>();
|
2024-04-06 10:49:19 -07:00
|
|
|
let (to_gui, gui_recv) = async_channel::bounded::<GuiUpdate>(10);
|
2024-04-05 20:58:29 -07:00
|
|
|
|
2024-04-06 11:05:29 -07:00
|
|
|
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
|
|
|
let ip_text = ip_entry.text();
|
|
|
|
let port_text = port_entry.text();
|
|
|
|
|
2024-04-06 20:53:36 -07:00
|
|
|
if ip_text.len() == 0 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if let Ok(val) = port_text.parse::<u32>() {
|
|
|
|
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),
|
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| {
|
2024-04-13 19:13:34 -07:00
|
|
|
if let Err(e) = to_mec.try_send(ApplicationEvent::SocketMessage(Message::text("U45:L10"), 1)) {
|
2024-04-05 20:58:29 -07:00
|
|
|
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 {
|
2024-04-06 10:49:19 -07:00
|
|
|
GuiUpdate::MoveEvent(msg) => {
|
2024-04-05 20:58:29 -07:00
|
|
|
axis_label.set_text(
|
|
|
|
format!("X: {:>4} Y: {:>4}", msg.x, msg.y).as_str()
|
|
|
|
);
|
|
|
|
}
|
2024-04-06 10:49:19 -07:00
|
|
|
GuiUpdate::SocketState(v) => {
|
2024-04-05 20:58:29 -07:00
|
|
|
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"]);
|
|
|
|
}
|
|
|
|
}
|
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();
|
|
|
|
|
2024-04-06 11:05:29 -07:00
|
|
|
window.connect_close_request(move |_| glib::Propagation::Proceed);
|
2024-03-25 16:28:13 -07:00
|
|
|
|
|
|
|
// Present window
|
|
|
|
window.present();
|
|
|
|
}
|