Improve UI
This commit is contained in:
parent
f440c25210
commit
296463319c
119
src/app.rs
119
src/app.rs
@ -1,18 +1,21 @@
|
||||
use std::{
|
||||
net::{SocketAddr, SocketAddrV4},
|
||||
sync::mpsc::{Receiver, Sender},
|
||||
sync::{mpsc::{Receiver, Sender}, Arc},
|
||||
vec, rc::Rc,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use async_ssh2_lite::{AsyncIoTcpStream, AsyncSession};
|
||||
use eframe::CreationContext;
|
||||
use egui::text::LayoutJob;
|
||||
use egui::{text::LayoutJob, Color32};
|
||||
use lazy_regex::regex_replace_all;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::ubus;
|
||||
|
||||
const ERROR_COLOR: Color32 = Color32::from_rgb(180, 20, 20);
|
||||
const SUCCESS_COLOR: Color32 = Color32::from_rgb(20, 150, 20);
|
||||
|
||||
pub enum AsyncEvent {
|
||||
Connect(Result<AsyncSession<AsyncIoTcpStream>>),
|
||||
Disconnect(Result<()>),
|
||||
@ -20,11 +23,18 @@ pub enum AsyncEvent {
|
||||
Call(Result<Value>)
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
pub struct AppSettings {
|
||||
address: String,
|
||||
port: u16,
|
||||
username: String,
|
||||
password: String,
|
||||
|
||||
show_object_ids: bool,
|
||||
connect_immidiately: bool
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
settings: AppSettings,
|
||||
session: Option<AsyncSession<AsyncIoTcpStream>>,
|
||||
|
||||
selected_object: Option<Rc<ubus::Object>>,
|
||||
@ -46,10 +56,14 @@ impl Default for App {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
Self {
|
||||
settings: AppSettings {
|
||||
address: "172.24.224.1".into(), //"192.168.1.1".to_owned(),
|
||||
port: 22,
|
||||
username: "root".to_owned(),
|
||||
password: "admin01".to_owned(),
|
||||
show_object_ids: false,
|
||||
connect_immidiately: true
|
||||
},
|
||||
session: None,
|
||||
|
||||
object_filter: "".into(),
|
||||
@ -101,8 +115,33 @@ fn remove_json_comments(text: &str) -> String {
|
||||
text.into()
|
||||
}
|
||||
|
||||
|
||||
fn json_layouter(ui: &egui::Ui, string: &str, wrap_width: f32) -> Arc<egui::Galley> {
|
||||
let mut layout_job = crate::syntax_highlighting::highlight(ui.ctx(), string, "json");
|
||||
layout_job.wrap.max_width = wrap_width;
|
||||
ui.fonts(|f| f.layout_job(layout_job))
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn init(&mut self, _cc: &CreationContext) {}
|
||||
pub fn init(&mut self, _cc: &CreationContext) {
|
||||
if self.settings.connect_immidiately {
|
||||
let username = &self.settings.username;
|
||||
let password = &self.settings.password;
|
||||
if username.is_empty() || password.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let address = self.settings.address.parse();
|
||||
if address.is_err() {
|
||||
return;
|
||||
}
|
||||
let address = address.unwrap();
|
||||
let port = self.settings.port;
|
||||
|
||||
let socket_addr = SocketAddrV4::new(address, port);
|
||||
self.start_connect(socket_addr, username.clone(), password.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_events(&mut self, _ctx: &egui::Context) {
|
||||
use AsyncEvent::*;
|
||||
@ -134,7 +173,6 @@ impl App {
|
||||
},
|
||||
|
||||
Call(result) => {
|
||||
dbg!(&result);
|
||||
self.response = Some(result)
|
||||
}
|
||||
}
|
||||
@ -176,17 +214,13 @@ impl App {
|
||||
}
|
||||
|
||||
fn start_call(&mut self, object: String, method: String, message: Option<Value>) {
|
||||
dbg!(self.session.is_some());
|
||||
if self.session.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
dbg!("call");
|
||||
|
||||
let tx = self.tx.clone();
|
||||
let session = self.session.clone().unwrap();
|
||||
tokio::spawn(async move {
|
||||
dbg!("exec");
|
||||
let result = ubus::call(&session, &object, &method, message.as_ref()).await;
|
||||
tx.send(AsyncEvent::Call(result)).expect("Failed to send event");
|
||||
});
|
||||
@ -229,11 +263,13 @@ impl App {
|
||||
text.append(&obj.name, 0.0, TextFormat {
|
||||
..TextFormat::default()
|
||||
});
|
||||
if self.settings.show_object_ids {
|
||||
text.append(&format!("@{:x}", obj.id), 10.0, TextFormat {
|
||||
color: style.noninteractive().fg_stroke.color,
|
||||
italics: true,
|
||||
..TextFormat::default()
|
||||
});
|
||||
}
|
||||
CollapsingHeader::new(text).show(ui, |ui| {
|
||||
for (name, method_params) in &shown_methods {
|
||||
if ui.selectable_label(false, name).clicked() {
|
||||
@ -254,16 +290,16 @@ impl App {
|
||||
|
||||
egui::ScrollArea::vertical()
|
||||
.show(ui, |ui| {
|
||||
ui.text_edit_singleline(&mut self.address);
|
||||
ui.text_edit_singleline(&mut self.settings.address);
|
||||
// TODO: ui.text_edit_singleline(&mut self.port);
|
||||
ui.text_edit_singleline(&mut self.username);
|
||||
ui.text_edit_singleline(&mut self.password);
|
||||
ui.text_edit_singleline(&mut self.settings.username);
|
||||
ui.text_edit_singleline(&mut self.settings.password);
|
||||
if self.is_connecting {
|
||||
ui.add_enabled(false, Button::new("Connecting..."));
|
||||
} else if self.session.is_none() {
|
||||
if ui.button("Connect").clicked() {
|
||||
let socket_addr = SocketAddrV4::new(self.address.parse().unwrap(), self.port);
|
||||
self.start_connect(socket_addr, self.username.clone(), self.password.clone());
|
||||
let socket_addr = SocketAddrV4::new(self.settings.address.parse().unwrap(), self.settings.port);
|
||||
self.start_connect(socket_addr, self.settings.username.clone(), self.settings.password.clone());
|
||||
}
|
||||
} else {
|
||||
if ui.button("Disconnect").clicked() {
|
||||
@ -292,7 +328,37 @@ impl App {
|
||||
return format!("{{\n{}\n}}", lines.join("\n"));
|
||||
}
|
||||
|
||||
fn display_response_textbox(ui: &mut egui::Ui, response: &Result<Value>) {
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
let mut text = match response {
|
||||
Ok(Value::Null) => "Success!".into(),
|
||||
Ok(response) => serde_json::to_string_pretty(response).unwrap(),
|
||||
Err(err) => format!("Error: {}", err)
|
||||
};
|
||||
|
||||
let textbox = egui::TextEdit::multiline(&mut text)
|
||||
.font(egui::TextStyle::Monospace) // for cursor height
|
||||
.code_editor()
|
||||
.desired_rows(4)
|
||||
.lock_focus(true)
|
||||
.desired_width(f32::INFINITY);
|
||||
|
||||
match response {
|
||||
Ok(Value::Null) => {
|
||||
ui.add(textbox.text_color(SUCCESS_COLOR));
|
||||
},
|
||||
Err(_) => {
|
||||
ui.add(textbox.text_color(ERROR_COLOR));
|
||||
},
|
||||
Ok(_) => {
|
||||
ui.add(textbox.layouter(&mut json_layouter));
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn show_central_panel(&mut self, ui: &mut egui::Ui) {
|
||||
ui.horizontal(|ui| {
|
||||
// Object dropdown
|
||||
{
|
||||
let object_name = self.selected_object.as_ref().map(|obj| obj.name.as_ref()).unwrap_or("");
|
||||
@ -366,35 +432,16 @@ impl App {
|
||||
// TODO: Block sending other requests
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
let mut layouter = |ui: &egui::Ui, string: &str, wrap_width: f32| {
|
||||
let mut layout_job = crate::syntax_highlighting::highlight(ui.ctx(), string, "json");
|
||||
layout_job.wrap.max_width = wrap_width;
|
||||
ui.fonts(|f| f.layout_job(layout_job))
|
||||
};
|
||||
|
||||
if let Some(response) = &self.response {
|
||||
egui::TopBottomPanel::bottom("bottom_panel")
|
||||
.resizable(true)
|
||||
.show_inside(ui, |ui| {
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
let mut text = match response {
|
||||
Ok(response) => serde_json::to_string_pretty(response).unwrap(),
|
||||
Err(err) => err.to_string()
|
||||
};
|
||||
ui.add_space(10.0);
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut text)
|
||||
.font(egui::TextStyle::Monospace) // for cursor height
|
||||
.code_editor()
|
||||
.desired_rows(10)
|
||||
.lock_focus(true)
|
||||
.desired_width(f32::INFINITY)
|
||||
.layouter(&mut layouter),
|
||||
);
|
||||
})
|
||||
App::display_response_textbox(ui, response);
|
||||
});
|
||||
}
|
||||
|
||||
@ -408,7 +455,7 @@ impl App {
|
||||
.desired_rows(10)
|
||||
.lock_focus(true)
|
||||
.desired_width(f32::INFINITY)
|
||||
.layouter(&mut layouter),
|
||||
.layouter(&mut json_layouter),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user