generated from lks/eframe_template_android
232 lines
8.4 KiB
Rust
232 lines
8.4 KiB
Rust
use eframe::{egui_glow, glow};
|
|
use egui::{mutex::Mutex, ScrollArea};
|
|
use std::sync::Arc;
|
|
|
|
use crate::backend::{self, Backends};
|
|
|
|
pub mod debug_plot;
|
|
use debug_plot::DebugPlots;
|
|
mod waterfall;
|
|
use waterfall::Waterfall;
|
|
mod fft;
|
|
use fft::Fft;
|
|
pub mod turbo_colormap;
|
|
|
|
const FFT_SIZE: usize = 1024;
|
|
|
|
pub struct TemplateApp {
|
|
plots: DebugPlots,
|
|
/// Behind an `Arc<Mutex<…>>` so we can pass it to [`egui::PaintCallback`] and paint later.
|
|
waterfall: Arc<Mutex<Waterfall>>,
|
|
fft: Fft,
|
|
backends: backend::Backends,
|
|
selected_backend: usize,
|
|
open_device: Option<Box<dyn backend::Device>>,
|
|
device_window_open: bool,
|
|
side_panel_open: bool,
|
|
}
|
|
|
|
impl TemplateApp {
|
|
/// Called once before the first frame.
|
|
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
|
// This is also where you can customize the look and feel of egui using
|
|
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
|
|
|
|
// Load previous app state (if any).
|
|
// Note that you must enable the `persistence` feature for this to work.
|
|
|
|
let plots = DebugPlots::new();
|
|
|
|
let (fft, rx) = Fft::new(FFT_SIZE, plots.get_sender()).unwrap();
|
|
|
|
let wf_size = fft.output_len;
|
|
let gl = cc
|
|
.gl
|
|
.as_ref()
|
|
.expect("Could not get gl context from glow backend");
|
|
|
|
Self {
|
|
plots,
|
|
waterfall: Arc::new(Mutex::new(Waterfall::new(gl, wf_size, wf_size, rx))),
|
|
fft,
|
|
backends: Backends::default(),
|
|
selected_backend: 0,
|
|
open_device: None,
|
|
device_window_open: true,
|
|
side_panel_open: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl eframe::App for TemplateApp {
|
|
/// Called by the frame work to save state before shutdown.
|
|
/// Currently does nothing
|
|
fn save(&mut self, _storage: &mut dyn eframe::Storage) {}
|
|
|
|
/// Called once on shutdown, after [`Self::save`].
|
|
fn on_exit(&mut self, gl: Option<&glow::Context>) {
|
|
if let Some(gl) = gl {
|
|
self.waterfall.lock().destroy(gl);
|
|
}
|
|
}
|
|
|
|
/// Called each time the UI needs repainting, which may be many times per second.
|
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
ctx.request_repaint();
|
|
self.plots.update_plots();
|
|
|
|
// Menu bar panel
|
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
|
egui::menu::bar(ui, |ui| {
|
|
// NOTE: no File->Quit on web pages!
|
|
let is_web = cfg!(target_arch = "wasm32");
|
|
ui.menu_button("File", |ui| {
|
|
if ui.button("Open Device").clicked() {
|
|
self.device_window_open = true;
|
|
}
|
|
if self.open_device.is_some() {
|
|
if ui.button("Close Device").clicked() {
|
|
if let Some(dev) = self.open_device.take() {
|
|
dev.close();
|
|
}
|
|
}
|
|
}
|
|
if !is_web {
|
|
if ui.button("Quit").clicked() {
|
|
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
|
}
|
|
}
|
|
});
|
|
ui.menu_button("View", |ui| {
|
|
ui.checkbox(&mut self.side_panel_open, "Side Panel");
|
|
});
|
|
|
|
self.plots.render_menu_buttons(ui);
|
|
ui.add_space(16.0);
|
|
|
|
egui::widgets::global_theme_preference_buttons(ui);
|
|
});
|
|
});
|
|
|
|
// Side panel
|
|
egui::SidePanel::right("Sid panel").show_animated(ctx, self.side_panel_open, |ui| {
|
|
if let Some(d) = &mut self.open_device {
|
|
d.show_settings(ui)
|
|
}
|
|
});
|
|
|
|
// Central panel
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|
egui::TopBottomPanel::top("Plot")
|
|
.resizable(true)
|
|
.show_inside(ui, |_ui| {
|
|
// TODO: Add plot
|
|
});
|
|
|
|
egui::CentralPanel::default().show_inside(ui, |ui| {
|
|
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
|
|
powered_by_egui_and_eframe(ui);
|
|
egui::warn_if_debug_build(ui);
|
|
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
|
let available_space = ui.available_size();
|
|
let (rect, response) =
|
|
ui.allocate_exact_size(available_space, egui::Sense::drag());
|
|
|
|
let _angle = response.drag_motion().x * 0.01;
|
|
|
|
// Clone locals so we can move them into the paint callback:
|
|
let waterfall = self.waterfall.clone();
|
|
|
|
let callback = egui::PaintCallback {
|
|
rect,
|
|
callback: std::sync::Arc::new(egui_glow::CallbackFn::new(
|
|
move |_info, painter| {
|
|
waterfall.lock().paint(painter.gl(), _angle);
|
|
},
|
|
)),
|
|
};
|
|
ui.painter().add(callback);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
// Update debug plot windows
|
|
self.plots.render_plot_windows(ctx);
|
|
|
|
// Update device selection window
|
|
let mut device_window = egui::Window::new("Select Device")
|
|
.default_width(600.0)
|
|
.default_height(400.0)
|
|
.vscroll(false)
|
|
.resizable(true)
|
|
.collapsible(false);
|
|
if self.open_device.is_some() {
|
|
device_window = device_window.open(&mut self.device_window_open);
|
|
} else {
|
|
device_window = device_window.anchor(egui::Align2::CENTER_CENTER, [0., 0.]);
|
|
}
|
|
let mut close_device_window = false;
|
|
device_window.show(ctx, |ui| {
|
|
egui::SidePanel::left("Select Driver")
|
|
.resizable(true)
|
|
.default_width(150.0)
|
|
.width_range(80.0..=200.0)
|
|
.show_inside(ui, |ui| {
|
|
ScrollArea::vertical().show(ui, |ui| {
|
|
ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
|
for (i, b) in self.backends.0.iter().enumerate() {
|
|
ui.selectable_value(
|
|
&mut self.selected_backend,
|
|
i,
|
|
b.display_text(),
|
|
);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
ui.vertical_centered(|ui| {
|
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
|
//if self._selected_backend < self._backends.0.len() {
|
|
if let Some(b) = self.backends.0.get_mut(self.selected_backend) {
|
|
//let mut b = &self._backends.0[self._selected_backend];
|
|
b.show_device_selection(ui);
|
|
if ui.add(egui::Button::new("Apply")).clicked() {
|
|
if let Some(dev) = self.open_device.take() {
|
|
dev.close()
|
|
};
|
|
if let Ok(device) =
|
|
b.build_device(self.fft.tx.clone(), self.plots.get_sender())
|
|
{
|
|
self.open_device = Some(device);
|
|
close_device_window = true;
|
|
}
|
|
}
|
|
} else {
|
|
ui.add(egui::Label::new("Select a Device Driver"));
|
|
}
|
|
});
|
|
});
|
|
});
|
|
if close_device_window {
|
|
self.device_window_open = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn powered_by_egui_and_eframe(ui: &mut egui::Ui) {
|
|
ui.horizontal(|ui| {
|
|
ui.spacing_mut().item_spacing.x = 0.0;
|
|
ui.label("Powered by ");
|
|
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
|
|
ui.label(", ");
|
|
ui.hyperlink_to(
|
|
"eframe",
|
|
"https://github.com/emilk/egui/tree/master/crates/eframe",
|
|
);
|
|
ui.label(" and ");
|
|
ui.hyperlink_to("glow", "https://github.com/grovesNL/glow");
|
|
ui.label(".");
|
|
});
|
|
}
|