mirror of
https://git.alemi.dev/dashboard.git
synced 2024-11-22 23:44:55 +01:00
chore: modularized gui code
This commit is contained in:
parent
5d3f5478cd
commit
32d58e4019
6 changed files with 275 additions and 242 deletions
|
@ -62,23 +62,23 @@ impl ApplicationState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_panel(&self, name:&str) -> Result<(), FetchError> {
|
pub fn add_panel(&self, panel: &Panel) -> Result<(), FetchError> {
|
||||||
let panel = self.storage.lock().expect("Storage Mutex poisoned")
|
let verified_panel = self.storage.lock().expect("Storage Mutex poisoned")
|
||||||
.new_panel(
|
.new_panel(
|
||||||
name,
|
panel.name.as_str(),
|
||||||
100,
|
panel.view_size,
|
||||||
200,
|
panel.width,
|
||||||
280,
|
panel.height,
|
||||||
self.panels.read().expect("Panels RwLock poisoned").len() as i32 // todo can this be made more compact and without acquisition?
|
self.panels.read().expect("Panels RwLock poisoned").len() as i32 // todo can this be made more compact and without acquisition?
|
||||||
)?; // TODO make values customizable and useful
|
)?; // TODO make values customizable and useful
|
||||||
self.panels.write().expect("Panels RwLock poisoned").push(panel);
|
self.panels.write().expect("Panels RwLock poisoned").push(verified_panel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_source(&self, panel_id:i32, name:&str, url:&str, query_x:&str, query_y:&str, color:Color32, visible:bool) -> Result<(), FetchError> {
|
pub fn add_source(&self, source: &Source) -> Result<(), FetchError> {
|
||||||
let source = self.storage.lock().expect("Storage Mutex poisoned")
|
let verified_source = self.storage.lock().expect("Storage Mutex poisoned")
|
||||||
.new_source(panel_id, name, url, query_x, query_y, color, visible)?;
|
.new_source(source.panel_id, source.name.as_str(), source.url.as_str(), source.query_x.as_str(), source.query_y.as_str(), source.color, source.visible)?;
|
||||||
self.sources.write().expect("Sources RwLock poisoned").push(source);
|
self.sources.write().expect("Sources RwLock poisoned").push(verified_source);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,21 @@ pub struct Panel {
|
||||||
pub limit: bool,
|
pub limit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Panel {
|
||||||
|
fn default() -> Self {
|
||||||
|
Panel {
|
||||||
|
id: -1,
|
||||||
|
name: "".to_string(),
|
||||||
|
view_scroll: true,
|
||||||
|
view_size: 300,
|
||||||
|
timeserie: true,
|
||||||
|
width: 100,
|
||||||
|
height: 200,
|
||||||
|
limit: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Source {
|
pub struct Source {
|
||||||
pub(crate) id: i32,
|
pub(crate) id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -31,6 +46,24 @@ pub struct Source {
|
||||||
pub(crate) data: RwLock<Vec<Value>>,
|
pub(crate) data: RwLock<Vec<Value>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Source {
|
||||||
|
fn default() -> Self {
|
||||||
|
Source {
|
||||||
|
id: -1,
|
||||||
|
name: "".to_string(),
|
||||||
|
url: "".to_string(),
|
||||||
|
interval: 60,
|
||||||
|
color: Color32::TRANSPARENT,
|
||||||
|
visible: false,
|
||||||
|
last_fetch: RwLock::new(Utc::now()),
|
||||||
|
query_x: "".to_string(),
|
||||||
|
query_y: "".to_string(),
|
||||||
|
panel_id: -1,
|
||||||
|
data: RwLock::new(Vec::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
pub fn valid(&self) -> bool {
|
pub fn valid(&self) -> bool {
|
||||||
let last_fetch = self.last_fetch.read().expect("LastFetch RwLock poisoned");
|
let last_fetch = self.last_fetch.read().expect("LastFetch RwLock poisoned");
|
||||||
|
|
2
src/app/gui/mod.rs
Normal file
2
src/app/gui/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod source;
|
||||||
|
pub mod panel;
|
125
src/app/gui/panel.rs
Normal file
125
src/app/gui/panel.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use chrono::{Utc, Local};
|
||||||
|
use eframe::egui::{Ui, Layout, Align, plot::{Plot, Legend, Corner, Line, GridMark}, Slider, DragValue};
|
||||||
|
|
||||||
|
use crate::app::{data::source::{Panel, Source}, util::timestamp_to_str};
|
||||||
|
|
||||||
|
pub fn panel_edit_inline_ui(ui: &mut Ui, panel: &mut Panel) {
|
||||||
|
eframe::egui::TextEdit::singleline(&mut panel.name)
|
||||||
|
.hint_text("name")
|
||||||
|
.desired_width(50.0)
|
||||||
|
.show(ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn panel_title_ui(ui: &mut Ui, panel: &mut Panel) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.heading(panel.name.as_str());
|
||||||
|
ui.with_layout(Layout::right_to_left(), |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.toggle_value(&mut panel.view_scroll, " • ");
|
||||||
|
ui.separator();
|
||||||
|
ui.label("m");
|
||||||
|
ui.add(
|
||||||
|
DragValue::new(&mut panel.view_size)
|
||||||
|
.speed(10)
|
||||||
|
.clamp_range(0..=2147483647i32),
|
||||||
|
);
|
||||||
|
ui.checkbox(&mut panel.limit, "limit");
|
||||||
|
ui.separator();
|
||||||
|
ui.checkbox(&mut panel.timeserie, "timeserie");
|
||||||
|
ui.separator();
|
||||||
|
ui.add(
|
||||||
|
Slider::new(&mut panel.height, 0..=500).text("height"),
|
||||||
|
);
|
||||||
|
ui.separator();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn panel_body_ui(ui: &mut Ui, panel: &mut Panel, sources: &Vec<Source>) {
|
||||||
|
let mut p = Plot::new(format!("plot-{}", panel.name))
|
||||||
|
.height(panel.height as f32)
|
||||||
|
.allow_scroll(false)
|
||||||
|
.legend(Legend::default().position(Corner::LeftTop));
|
||||||
|
|
||||||
|
if panel.view_scroll {
|
||||||
|
p = p
|
||||||
|
.include_x(Utc::now().timestamp() as f64);
|
||||||
|
if panel.limit {
|
||||||
|
p = p
|
||||||
|
.set_margin_fraction(eframe::emath::Vec2{x:0.0, y:0.1})
|
||||||
|
.include_x((Utc::now().timestamp() + ( panel.view_size as i64 * 3)) as f64);
|
||||||
|
}
|
||||||
|
if panel.limit {
|
||||||
|
p = p.include_x(
|
||||||
|
(Utc::now().timestamp() - (panel.view_size as i64 * 60))
|
||||||
|
as f64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if panel.timeserie {
|
||||||
|
p = p
|
||||||
|
.x_axis_formatter(|x, _range| {
|
||||||
|
timestamp_to_str(x as i64, true, false)
|
||||||
|
})
|
||||||
|
.label_formatter(|name, value| {
|
||||||
|
if !name.is_empty() {
|
||||||
|
return format!(
|
||||||
|
"{}\nx = {}\ny = {:.1}",
|
||||||
|
name,
|
||||||
|
timestamp_to_str(value.x as i64, false, true),
|
||||||
|
value.y
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return format!(
|
||||||
|
"x = {}\ny = {:.1}",
|
||||||
|
timestamp_to_str(value.x as i64, false, true),
|
||||||
|
value.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.x_grid_spacer(|grid| {
|
||||||
|
let offset = Local::now().offset().local_minus_utc() as i64;
|
||||||
|
let (start, end) = grid.bounds;
|
||||||
|
let mut counter = (start as i64) - ((start as i64) % 3600);
|
||||||
|
let mut out: Vec<GridMark> = Vec::new();
|
||||||
|
loop {
|
||||||
|
counter += 3600;
|
||||||
|
if counter > end as i64 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (counter + offset) % 86400 == 0 {
|
||||||
|
out.push(GridMark {
|
||||||
|
value: counter as f64,
|
||||||
|
step_size: 86400 as f64,
|
||||||
|
})
|
||||||
|
} else if counter % 3600 == 0 {
|
||||||
|
out.push(GridMark {
|
||||||
|
value: counter as f64,
|
||||||
|
step_size: 3600 as f64,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
p.show(ui, |plot_ui| {
|
||||||
|
for source in &*sources {
|
||||||
|
if source.visible && source.panel_id == panel.id {
|
||||||
|
let line = if panel.limit {
|
||||||
|
Line::new(source.values_filter(
|
||||||
|
(Utc::now().timestamp()
|
||||||
|
- (panel.view_size as i64 * 60)) as f64,
|
||||||
|
))
|
||||||
|
.name(source.name.as_str())
|
||||||
|
} else {
|
||||||
|
Line::new(source.values()).name(source.name.as_str())
|
||||||
|
};
|
||||||
|
plot_ui.line(line.color(source.color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
79
src/app/gui/source.rs
Normal file
79
src/app/gui/source.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
use eframe::egui;
|
||||||
|
use eframe::egui::Ui;
|
||||||
|
|
||||||
|
use crate::app::data::source::{Source, Panel};
|
||||||
|
|
||||||
|
pub fn source_edit_inline_ui(ui: &mut Ui, source: &mut Source, panels: &Vec<Panel>) {
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.name)
|
||||||
|
.hint_text("name")
|
||||||
|
.desired_width(50.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.url)
|
||||||
|
.hint_text("url")
|
||||||
|
.desired_width(160.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.query_x)
|
||||||
|
.hint_text("x")
|
||||||
|
.desired_width(30.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.query_y)
|
||||||
|
.hint_text("y")
|
||||||
|
.desired_width(30.0)
|
||||||
|
.show(ui);
|
||||||
|
egui::ComboBox::from_id_source("panel")
|
||||||
|
.selected_text(format!("panel [{}]", source.panel_id))
|
||||||
|
.width(70.0)
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for p in panels {
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut source.panel_id,
|
||||||
|
p.id,
|
||||||
|
p.name.as_str(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.checkbox(&mut source.visible, "visible");
|
||||||
|
ui.add(egui::Slider::new(&mut source.interval, 1..=60));
|
||||||
|
ui.color_edit_button_srgba(&mut source.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn source_ui(ui: &mut Ui, source: &mut Source, panels: &Vec<Panel>) {
|
||||||
|
ui.group(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.checkbox(&mut source.visible, "");
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.name)
|
||||||
|
.hint_text("name")
|
||||||
|
.desired_width(80.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.url)
|
||||||
|
.hint_text("url")
|
||||||
|
.desired_width(300.0)
|
||||||
|
.show(ui);
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add(egui::Slider::new(&mut source.interval, 1..=60));
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.query_x)
|
||||||
|
.hint_text("x")
|
||||||
|
.desired_width(50.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.query_y)
|
||||||
|
.hint_text("y")
|
||||||
|
.desired_width(50.0)
|
||||||
|
.show(ui);
|
||||||
|
egui::ComboBox::from_id_source(format!("panel-{}", source.id))
|
||||||
|
.selected_text(format!("panel [{}]", source.panel_id))
|
||||||
|
.width(70.0)
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for p in panels {
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut source.panel_id,
|
||||||
|
p.id,
|
||||||
|
p.name.as_str(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.color_edit_button_srgba(&mut source.color);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
252
src/app/mod.rs
252
src/app/mod.rs
|
@ -1,4 +1,5 @@
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
pub mod gui;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod worker;
|
pub mod worker;
|
||||||
|
|
||||||
|
@ -13,40 +14,16 @@ use std::sync::Arc;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use self::data::ApplicationState;
|
use self::data::ApplicationState;
|
||||||
|
use self::data::source::{Panel,Source};
|
||||||
|
use self::gui::panel::{panel_edit_inline_ui, panel_title_ui, panel_body_ui};
|
||||||
|
use self::gui::source::{source_ui, source_edit_inline_ui};
|
||||||
use self::util::{human_size, timestamp_to_str};
|
use self::util::{human_size, timestamp_to_str};
|
||||||
use self::worker::native_save;
|
use self::worker::native_save;
|
||||||
|
|
||||||
struct InputBuffer {
|
|
||||||
panel_name: String,
|
|
||||||
name: String,
|
|
||||||
url: String,
|
|
||||||
interval: i32,
|
|
||||||
query_x: String,
|
|
||||||
query_y: String,
|
|
||||||
color: Color32,
|
|
||||||
visible: bool,
|
|
||||||
panel_id: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for InputBuffer {
|
|
||||||
fn default() -> Self {
|
|
||||||
InputBuffer {
|
|
||||||
panel_name: "".to_string(),
|
|
||||||
name: "".to_string(),
|
|
||||||
url: "".to_string(),
|
|
||||||
interval: 60,
|
|
||||||
query_x: "".to_string(),
|
|
||||||
query_y: "".to_string(),
|
|
||||||
color: Color32::TRANSPARENT,
|
|
||||||
visible: true,
|
|
||||||
panel_id: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
data: Arc<ApplicationState>,
|
data: Arc<ApplicationState>,
|
||||||
input: InputBuffer,
|
input_source: Source,
|
||||||
|
input_panel: Panel,
|
||||||
edit: bool,
|
edit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +31,8 @@ impl App {
|
||||||
pub fn new(_cc: &eframe::CreationContext, data: Arc<ApplicationState>) -> Self {
|
pub fn new(_cc: &eframe::CreationContext, data: Arc<ApplicationState>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
input: InputBuffer::default(),
|
input_panel: Panel::default(),
|
||||||
|
input_source: Source::default(),
|
||||||
edit: false,
|
edit: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,59 +53,17 @@ impl eframe::App for App {
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.label("+ panel");
|
ui.label("+ panel");
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.panel_name)
|
panel_edit_inline_ui(ui, &mut self.input_panel);
|
||||||
.hint_text("name")
|
|
||||||
.desired_width(50.0)
|
|
||||||
.show(ui);
|
|
||||||
if ui.button("add").clicked() {
|
if ui.button("add").clicked() {
|
||||||
if let Err(e) = self.data.add_panel(self.input.panel_name.as_str()) {
|
if let Err(e) = self.data.add_panel(&self.input_panel) {
|
||||||
error!(target: "ui", "Failed to add panel: {:?}", e);
|
error!(target: "ui", "Failed to add panel: {:?}", e);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.label("+ source");
|
ui.label("+ source");
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.name)
|
source_edit_inline_ui(ui, &mut self.input_source, &self.data.panels.read().expect("Panels RwLock poisoned"));
|
||||||
.hint_text("name")
|
|
||||||
.desired_width(50.0)
|
|
||||||
.show(ui);
|
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.url)
|
|
||||||
.hint_text("url")
|
|
||||||
.desired_width(160.0)
|
|
||||||
.show(ui);
|
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.query_x)
|
|
||||||
.hint_text("x")
|
|
||||||
.desired_width(30.0)
|
|
||||||
.show(ui);
|
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.query_y)
|
|
||||||
.hint_text("y")
|
|
||||||
.desired_width(30.0)
|
|
||||||
.show(ui);
|
|
||||||
egui::ComboBox::from_id_source("panel")
|
|
||||||
.selected_text(format!("panel [{}]", self.input.panel_id))
|
|
||||||
.width(70.0)
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
let pnls = self.data.panels.write().expect("Panels RwLock poisoned");
|
|
||||||
for p in &*pnls {
|
|
||||||
ui.selectable_value(
|
|
||||||
&mut self.input.panel_id,
|
|
||||||
p.id,
|
|
||||||
p.name.as_str(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.checkbox(&mut self.input.visible, "visible");
|
|
||||||
ui.add(egui::Slider::new(&mut self.input.interval, 1..=60));
|
|
||||||
ui.color_edit_button_srgba(&mut self.input.color);
|
|
||||||
if ui.button("add").clicked() {
|
if ui.button("add").clicked() {
|
||||||
if let Err(e) = self.data.add_source(
|
if let Err(e) = self.data.add_source(&self.input_source) {
|
||||||
self.input.panel_id,
|
|
||||||
self.input.name.as_str(),
|
|
||||||
self.input.url.as_str(),
|
|
||||||
self.input.query_x.as_str(),
|
|
||||||
self.input.query_y.as_str(),
|
|
||||||
self.input.color,
|
|
||||||
self.input.visible,
|
|
||||||
) {
|
|
||||||
error!(target: "ui", "Error adding souce : {:?}", e);
|
error!(target: "ui", "Error adding souce : {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,51 +126,12 @@ impl eframe::App for App {
|
||||||
if self.edit {
|
if self.edit {
|
||||||
egui::SidePanel::left("sources-bar").show(ctx, |ui| {
|
egui::SidePanel::left("sources-bar").show(ctx, |ui| {
|
||||||
let mut sources = self.data.sources.write().expect("Sources RwLock poisoned");
|
let mut sources = self.data.sources.write().expect("Sources RwLock poisoned");
|
||||||
|
let panels = self.data.panels.read().expect("Panels RwLock poisoned");
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
for source in &mut *sources {
|
for source in &mut *sources {
|
||||||
ui.group(|ui| {
|
source_ui(ui, source, &panels);
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.checkbox(&mut source.visible, "");
|
|
||||||
eframe::egui::TextEdit::singleline(&mut source.name)
|
|
||||||
.hint_text("name")
|
|
||||||
.desired_width(80.0)
|
|
||||||
.show(ui);
|
|
||||||
eframe::egui::TextEdit::singleline(&mut source.url)
|
|
||||||
.hint_text("url")
|
|
||||||
.desired_width(300.0)
|
|
||||||
.show(ui);
|
|
||||||
});
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.add(egui::Slider::new(&mut source.interval, 1..=60));
|
|
||||||
eframe::egui::TextEdit::singleline(&mut source.query_x)
|
|
||||||
.hint_text("x")
|
|
||||||
.desired_width(50.0)
|
|
||||||
.show(ui);
|
|
||||||
eframe::egui::TextEdit::singleline(&mut source.query_y)
|
|
||||||
.hint_text("y")
|
|
||||||
.desired_width(50.0)
|
|
||||||
.show(ui);
|
|
||||||
egui::ComboBox::from_id_source(format!("panel-{}", source.id))
|
|
||||||
.selected_text(format!("panel [{}]", source.panel_id))
|
|
||||||
.width(70.0)
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
let pnls = self
|
|
||||||
.data
|
|
||||||
.panels
|
|
||||||
.read()
|
|
||||||
.expect("Panels RwLock poisoned");
|
|
||||||
for p in &*pnls {
|
|
||||||
ui.selectable_value(
|
|
||||||
&mut source.panel_id,
|
|
||||||
p.id,
|
|
||||||
p.name.as_str(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.color_edit_button_srgba(&mut source.color);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// TODO make this not necessary
|
||||||
ui.collapsing("extra space", |ui| {
|
ui.collapsing("extra space", |ui| {
|
||||||
ui.add_space(300.0);
|
ui.add_space(300.0);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
@ -246,6 +143,7 @@ impl eframe::App for App {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
let mut panels = self.data.panels.write().expect("Panels RwLock poisoned"); // TODO only lock as write when editing
|
let mut panels = self.data.panels.write().expect("Panels RwLock poisoned"); // TODO only lock as write when editing
|
||||||
|
let panels_count = panels.len();
|
||||||
let sources = self.data.sources.read().expect("Sources RwLock poisoned"); // TODO only lock as write when editing
|
let sources = self.data.sources.read().expect("Sources RwLock poisoned"); // TODO only lock as write when editing
|
||||||
for (index, panel) in panels.iter_mut().enumerate() {
|
for (index, panel) in panels.iter_mut().enumerate() {
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
|
@ -257,125 +155,21 @@ impl eframe::App for App {
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.show_header(ui, |ui| {
|
.show_header(ui, |ui| {
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if self.edit {
|
if self.edit {
|
||||||
if ui.small_button(" ^ ").clicked() {
|
if ui.small_button(" + ").clicked() {
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
to_swap.push(index); // TODO kinda jank but is there a better way?
|
to_swap.push(index); // TODO kinda jank but is there a better way?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.separator();
|
if ui.small_button(" - ").clicked() {
|
||||||
|
if index < panels_count - 1 {
|
||||||
|
to_swap.push(index + 1); // TODO kinda jank but is there a better way?
|
||||||
}
|
}
|
||||||
ui.heading(panel.name.as_str());
|
|
||||||
if self.edit {
|
|
||||||
ui.separator();
|
|
||||||
ui.add(
|
|
||||||
egui::Slider::new(&mut panel.height, 0..=500).text("height"),
|
|
||||||
);
|
|
||||||
ui.separator();
|
|
||||||
ui.checkbox(&mut panel.timeserie, "timeserie");
|
|
||||||
}
|
}
|
||||||
ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| {
|
}
|
||||||
ui.horizontal(|ui| {
|
panel_title_ui(ui, panel);
|
||||||
ui.toggle_value(&mut panel.view_scroll, " • ");
|
|
||||||
ui.separator();
|
|
||||||
ui.label("m");
|
|
||||||
ui.add(
|
|
||||||
egui::DragValue::new(&mut panel.view_size)
|
|
||||||
.speed(10)
|
|
||||||
.clamp_range(0..=2147483647i32),
|
|
||||||
);
|
|
||||||
ui.checkbox(&mut panel.limit, "limit");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.body(|ui| {
|
.body(|ui| panel_body_ui(ui, panel, &sources));
|
||||||
let mut p = Plot::new(format!("plot-{}", panel.name))
|
|
||||||
.height(panel.height as f32)
|
|
||||||
.allow_scroll(false)
|
|
||||||
.legend(egui::plot::Legend::default().position(egui::plot::Corner::LeftTop));
|
|
||||||
|
|
||||||
if panel.view_scroll {
|
|
||||||
p = p
|
|
||||||
.include_x(Utc::now().timestamp() as f64);
|
|
||||||
if panel.limit {
|
|
||||||
p = p
|
|
||||||
.set_margin_fraction(eframe::emath::Vec2{x:0.0, y:0.1})
|
|
||||||
.include_x((Utc::now().timestamp() + ( panel.view_size as i64 * 3)) as f64);
|
|
||||||
}
|
|
||||||
if panel.limit {
|
|
||||||
p = p.include_x(
|
|
||||||
(Utc::now().timestamp() - (panel.view_size as i64 * 60))
|
|
||||||
as f64,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if panel.timeserie {
|
|
||||||
p = p
|
|
||||||
.x_axis_formatter(|x, _range| {
|
|
||||||
timestamp_to_str(x as i64, true, false)
|
|
||||||
})
|
|
||||||
.label_formatter(|name, value| {
|
|
||||||
if !name.is_empty() {
|
|
||||||
return format!(
|
|
||||||
"{}\nx = {}\ny = {:.1}",
|
|
||||||
name,
|
|
||||||
timestamp_to_str(value.x as i64, false, true),
|
|
||||||
value.y
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return format!(
|
|
||||||
"x = {}\ny = {:.1}",
|
|
||||||
timestamp_to_str(value.x as i64, false, true),
|
|
||||||
value.y
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.x_grid_spacer(|grid| {
|
|
||||||
let offset = Local::now().offset().local_minus_utc() as i64;
|
|
||||||
let (start, end) = grid.bounds;
|
|
||||||
let mut counter = (start as i64) - ((start as i64) % 3600);
|
|
||||||
let mut out: Vec<GridMark> = Vec::new();
|
|
||||||
loop {
|
|
||||||
counter += 3600;
|
|
||||||
if counter > end as i64 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (counter + offset) % 86400 == 0 {
|
|
||||||
out.push(GridMark {
|
|
||||||
value: counter as f64,
|
|
||||||
step_size: 86400 as f64,
|
|
||||||
})
|
|
||||||
} else if counter % 3600 == 0 {
|
|
||||||
out.push(GridMark {
|
|
||||||
value: counter as f64,
|
|
||||||
step_size: 3600 as f64,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
p.show(ui, |plot_ui| {
|
|
||||||
for source in &*sources {
|
|
||||||
if source.visible && source.panel_id == panel.id {
|
|
||||||
let line = if panel.limit {
|
|
||||||
Line::new(source.values_filter(
|
|
||||||
(Utc::now().timestamp()
|
|
||||||
- (panel.view_size as i64 * 60)) as f64,
|
|
||||||
))
|
|
||||||
.name(source.name.as_str())
|
|
||||||
} else {
|
|
||||||
Line::new(source.values()).name(source.name.as_str())
|
|
||||||
};
|
|
||||||
plot_ui.line(line.color(source.color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue