mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-08 22:38:01 -06:00
Start working on gui
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
use crate::flowchart::FlowChart;
|
||||
|
||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)] // if we add new fields, give them default values when deserializing old state
|
||||
pub struct TemplateApp {
|
||||
tab: Tab,
|
||||
|
||||
#[serde(skip)]
|
||||
flowchart: FlowChart,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub enum Tab {
|
||||
Flowchart,
|
||||
Test,
|
||||
}
|
||||
|
||||
impl Default for TemplateApp {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tab: Tab::Flowchart,
|
||||
// Example stuff:
|
||||
// label: "Hello World!".to_owned(),
|
||||
// value: 2.7,
|
||||
flowchart: FlowChart::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
if let Some(storage) = cc.storage {
|
||||
eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default()
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for TemplateApp {
|
||||
/// Called by the framework to save state before shutdown.
|
||||
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||
eframe::set_value(storage, eframe::APP_KEY, self);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
// Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`.
|
||||
// For inspiration and more examples, go to https://emilk.github.io/egui
|
||||
|
||||
egui::TopBottomPanel::top("tab_panel").show(ctx, |ui| {
|
||||
// The top panel is often a good place for a menu bar:
|
||||
|
||||
egui::MenuBar::new()
|
||||
// .style(StyleModifier::new(|s| s.visuals))
|
||||
.ui(ui, |ui| {
|
||||
if ui
|
||||
.menu_button("Network", |ui| if ui.button("Quit").clicked() {})
|
||||
.response
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::Flowchart;
|
||||
};
|
||||
|
||||
if ui
|
||||
.menu_button("Test", |ui| if ui.button("Quit").clicked() {})
|
||||
.response
|
||||
.clicked()
|
||||
{
|
||||
self.tab = Tab::Test;
|
||||
};
|
||||
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
egui::widgets::global_theme_preference_switch(ui);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
egui::TopBottomPanel::bottom("tab_panel").show(ctx, |ui| {
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
ui.label(format!("UnShell UI {}", env!("CARGO_PKG_VERSION")));
|
||||
egui::warn_if_debug_build(ui);
|
||||
});
|
||||
});
|
||||
|
||||
// egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
// // The top panel is often a good place for a menu bar:
|
||||
|
||||
// egui::MenuBar::new().ui(ui, |ui| {
|
||||
// if ui
|
||||
// .menu_button("Network", |ui| if ui.button("Quit").clicked() {})
|
||||
// .response
|
||||
// .clicked()
|
||||
// {
|
||||
// self.tab = Tab::Flowchart;
|
||||
// };
|
||||
|
||||
// if ui
|
||||
// .menu_button("Test", |ui| if ui.button("Quit").clicked() {})
|
||||
// .response
|
||||
// .clicked()
|
||||
// {
|
||||
// self.tab = Tab::Test;
|
||||
// };
|
||||
// });
|
||||
// });
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
match self.tab {
|
||||
Tab::Flowchart => {
|
||||
self.flowchart.paint(ui);
|
||||
}
|
||||
Tab::Test => {
|
||||
// The central panel the region left after adding TopPanel's and SidePanel's
|
||||
ui.heading("eframe template");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Write something: ");
|
||||
// ui.text_edit_singleline(&mut self.label);
|
||||
});
|
||||
|
||||
// ui.add(egui::Slider::new(&mut self.value, 0.0..=10.0).text("value"));
|
||||
// if ui.button("Increment").clicked() {
|
||||
// self.value += 1.0;
|
||||
// }
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.add(egui::github_link_file!(
|
||||
"https://github.com/emilk/eframe_template/blob/main/",
|
||||
"Source code."
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
use egui::Ui;
|
||||
use egui::{Color32, Painter, Pos2, Rect, Stroke, UiBuilder, Vec2};
|
||||
|
||||
const TARGET_LINE_GAP: f32 = 80.;
|
||||
|
||||
const BG_STROKE: Stroke = Stroke {
|
||||
width: 0.3,
|
||||
color: Color32::GRAY,
|
||||
};
|
||||
|
||||
pub struct FlowChart {
|
||||
// frame: Frame,
|
||||
container: DraggableContainer,
|
||||
}
|
||||
|
||||
impl FlowChart {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
container: DraggableContainer::new(
|
||||
Pos2 { x: 100., y: 100. },
|
||||
Vec2 { x: 100., y: 100. },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_bg(&self, rect: &Rect, painter: &Painter) {
|
||||
let h_count = (rect.width() / TARGET_LINE_GAP).round() as usize;
|
||||
let h_spacing = rect.width() / h_count as f32;
|
||||
for n in 0..h_count {
|
||||
painter.vline(rect.min.x + n as f32 * h_spacing, rect.y_range(), BG_STROKE);
|
||||
}
|
||||
|
||||
let v_count = (rect.height() / TARGET_LINE_GAP).round() as usize;
|
||||
let v_spacing = rect.height() / v_count as f32;
|
||||
for n in 0..v_count {
|
||||
painter.hline(rect.x_range(), rect.min.y + n as f32 * v_spacing, BG_STROKE);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paint(&mut self, ui: &mut Ui) {
|
||||
self.paint_bg(&ui.clip_rect(), ui.painter());
|
||||
self.container.show(ui, |ui, rect| {
|
||||
ui.painter().rect(
|
||||
// ui.top
|
||||
*rect,
|
||||
0.,
|
||||
Color32::PURPLE,
|
||||
BG_STROKE,
|
||||
egui::StrokeKind::Middle,
|
||||
);
|
||||
ui.label("Tests");
|
||||
ui.button("Test");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DraggableContainer {
|
||||
pub pos: egui::Pos2,
|
||||
pub size: egui::Vec2,
|
||||
is_dragging: bool,
|
||||
drag_offset: egui::Vec2,
|
||||
}
|
||||
|
||||
impl DraggableContainer {
|
||||
pub fn new(pos: egui::Pos2, size: egui::Vec2) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
size,
|
||||
is_dragging: false,
|
||||
drag_offset: egui::Vec2::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show<R>(
|
||||
&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
add_contents: impl FnOnce(&mut egui::Ui, &Rect) -> R,
|
||||
) -> R {
|
||||
let rect = egui::Rect::from_min_size(self.pos, self.size);
|
||||
|
||||
// Handle dragging logic
|
||||
let response = ui.interact(rect, ui.id().with("drag_area"), egui::Sense::drag());
|
||||
|
||||
if response.drag_started() {
|
||||
self.is_dragging = true;
|
||||
if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() {
|
||||
self.drag_offset = self.pos - pointer_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if response.dragged() && self.is_dragging {
|
||||
if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() {
|
||||
self.pos = pointer_pos + self.drag_offset;
|
||||
}
|
||||
}
|
||||
|
||||
if response.drag_stopped() {
|
||||
self.is_dragging = false;
|
||||
}
|
||||
|
||||
// Create a child UI at the specified position
|
||||
// let mut child_ui = ui.child_ui(rect, egui::Layout::top_down(egui::Align::LEFT), None);
|
||||
|
||||
let mut child_ui = ui.new_child(UiBuilder::new().max_rect(rect));
|
||||
|
||||
// Add contents
|
||||
add_contents(&mut child_ui, &rect)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#![warn(clippy::all, rust_2018_idioms)]
|
||||
|
||||
mod app;
|
||||
pub use app::TemplateApp;
|
||||
|
||||
mod flowchart;
|
||||
@@ -0,0 +1,74 @@
|
||||
#![warn(clippy::all, rust_2018_idioms)]
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
use unshell_gui::TemplateApp;
|
||||
|
||||
// When compiling natively:
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() -> eframe::Result {
|
||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||
|
||||
let native_options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([400.0, 300.0])
|
||||
.with_min_inner_size([300.0, 220.0]),
|
||||
// .with_icon(
|
||||
// // NOTE: Adding an icon is optional
|
||||
// eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..])
|
||||
// .expect("Failed to load icon"),
|
||||
// ),
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
"eframe template",
|
||||
native_options,
|
||||
Box::new(|cc| Ok(Box::new(TemplateApp::new(cc)))),
|
||||
)
|
||||
}
|
||||
|
||||
// When compiling to web using trunk:
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
use eframe::wasm_bindgen::JsCast as _;
|
||||
|
||||
// Redirect `log` message to `console.log` and friends:
|
||||
eframe::WebLogger::init(log::LevelFilter::Debug).ok();
|
||||
|
||||
let web_options = eframe::WebOptions::default();
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async {
|
||||
let document = web_sys::window()
|
||||
.expect("No window")
|
||||
.document()
|
||||
.expect("No document");
|
||||
|
||||
let canvas = document
|
||||
.get_element_by_id("the_canvas_id")
|
||||
.expect("Failed to find the_canvas_id")
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
||||
.expect("the_canvas_id was not a HtmlCanvasElement");
|
||||
|
||||
let start_result = eframe::WebRunner::new()
|
||||
.start(
|
||||
canvas,
|
||||
web_options,
|
||||
Box::new(|cc| Ok(Box::new(TemplateApp::new(cc)))),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Remove the loading text and spinner:
|
||||
if let Some(loading_text) = document.get_element_by_id("loading_text") {
|
||||
match start_result {
|
||||
Ok(_) => {
|
||||
loading_text.remove();
|
||||
}
|
||||
Err(e) => {
|
||||
loading_text.set_inner_html(
|
||||
"<p> The app has crashed. See the developer console for details. </p>",
|
||||
);
|
||||
panic!("Failed to start eframe: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user