user interface improvements
This commit is contained in:
parent
782d567302
commit
8ed749ef72
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -659,7 +659,7 @@ checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "eframe"
|
name = "eframe"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/Titaniumtown/egui.git#ea25cc1a991610099ada8fe02f1c11ba708f77fd"
|
source = "git+https://github.com/Titaniumtown/egui.git#b5adf3b7ec16255873e7a610573a645719d02e89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"egui",
|
"egui",
|
||||||
@ -679,7 +679,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "egui"
|
name = "egui"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
source = "git+https://github.com/Titaniumtown/egui.git#ea25cc1a991610099ada8fe02f1c11ba708f77fd"
|
source = "git+https://github.com/Titaniumtown/egui.git#b5adf3b7ec16255873e7a610573a645719d02e89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"epaint",
|
"epaint",
|
||||||
@ -691,7 +691,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-winit"
|
name = "egui-winit"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/Titaniumtown/egui.git#ea25cc1a991610099ada8fe02f1c11ba708f77fd"
|
source = "git+https://github.com/Titaniumtown/egui.git#b5adf3b7ec16255873e7a610573a645719d02e89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arboard",
|
"arboard",
|
||||||
"egui",
|
"egui",
|
||||||
@ -705,7 +705,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_glow"
|
name = "egui_glow"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
source = "git+https://github.com/Titaniumtown/egui.git#ea25cc1a991610099ada8fe02f1c11ba708f77fd"
|
source = "git+https://github.com/Titaniumtown/egui.git#b5adf3b7ec16255873e7a610573a645719d02e89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"egui",
|
"egui",
|
||||||
@ -725,7 +725,7 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "emath"
|
name = "emath"
|
||||||
version = "0.18.0"
|
version = "0.18.0"
|
||||||
source = "git+https://github.com/Titaniumtown/egui.git#ea25cc1a991610099ada8fe02f1c11ba708f77fd"
|
source = "git+https://github.com/Titaniumtown/egui.git#b5adf3b7ec16255873e7a610573a645719d02e89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"serde",
|
"serde",
|
||||||
@ -734,7 +734,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint"
|
name = "epaint"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
source = "git+https://github.com/Titaniumtown/egui.git#ea25cc1a991610099ada8fe02f1c11ba708f77fd"
|
source = "git+https://github.com/Titaniumtown/egui.git#b5adf3b7ec16255873e7a610573a645719d02e89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ab_glyph",
|
"ab_glyph",
|
||||||
"ahash",
|
"ahash",
|
||||||
|
|||||||
@ -10,8 +10,7 @@
|
|||||||
"- The 'Panel' button toggles if the side bar should be shown or not. This can also be accomplished by pressing the 'h' key.",
|
"- The 'Panel' button toggles if the side bar should be shown or not. This can also be accomplished by pressing the 'h' key.",
|
||||||
"- The 'Add Function' button adds a new function to be graphed. You can then configure that function in the side panel.",
|
"- The 'Add Function' button adds a new function to be graphed. You can then configure that function in the side panel.",
|
||||||
"- The 'Help' button opens and closes this window!",
|
"- The 'Help' button opens and closes this window!",
|
||||||
"- The 'Info' button provides information on the build currently running.",
|
"- The 'Info' button provides information on the build currently running."
|
||||||
"- The Sun/Moon button toggles Dark and Light mode."
|
|
||||||
],
|
],
|
||||||
"help_function": [
|
"help_function": [
|
||||||
"(From Left to Right)",
|
"(From Left to Right)",
|
||||||
@ -25,6 +24,7 @@
|
|||||||
],
|
],
|
||||||
"welcome": [
|
"welcome": [
|
||||||
"Welcome to the (Yet-to-be-named) Graphing Software!",
|
"Welcome to the (Yet-to-be-named) Graphing Software!",
|
||||||
|
"",
|
||||||
"This project aims to provide an intuitive experience graphing mathematical functions with features such as Integration, Differentiation, Extrema, Roots, and much more! (see the Help Window for more details)"
|
"This project aims to provide an intuitive experience graphing mathematical functions with features such as Integration, Differentiation, Extrema, Roots, and much more! (see the Help Window for more details)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ mod function_entry;
|
|||||||
mod function_manager;
|
mod function_manager;
|
||||||
mod math_app;
|
mod math_app;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
mod style;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
|
|||||||
@ -22,6 +22,7 @@ mod function_entry;
|
|||||||
mod function_manager;
|
mod function_manager;
|
||||||
mod math_app;
|
mod math_app;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
mod style;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
|
|
||||||
// For running the program natively! (Because why not?)
|
// For running the program natively! (Because why not?)
|
||||||
|
|||||||
190
src/math_app.rs
190
src/math_app.rs
@ -6,7 +6,7 @@ use crate::misc::{dyn_mut_iter, option_vec_printer};
|
|||||||
use eframe::App;
|
use eframe::App;
|
||||||
use egui::{
|
use egui::{
|
||||||
plot::Plot, style::Margin, Button, CentralPanel, ComboBox, Context, Frame, Key, Layout,
|
plot::Plot, style::Margin, Button, CentralPanel, ComboBox, Context, Frame, Key, Layout,
|
||||||
SidePanel, Slider, TopBottomPanel, Vec2, Visuals, Window,
|
SidePanel, Slider, TopBottomPanel, Vec2, Window,
|
||||||
};
|
};
|
||||||
use emath::{Align, Align2};
|
use emath::{Align, Align2};
|
||||||
use epaint::Rounding;
|
use epaint::Rounding;
|
||||||
@ -93,9 +93,6 @@ pub struct MathApp {
|
|||||||
/// Contains the list of Areas calculated (the vector of f64) and time it took for the last frame (the Duration). Stored in a Tuple.
|
/// Contains the list of Areas calculated (the vector of f64) and time it took for the last frame (the Duration). Stored in a Tuple.
|
||||||
last_info: (Option<String>, Option<String>),
|
last_info: (Option<String>, Option<String>),
|
||||||
|
|
||||||
/// Whether or not dark mode is enabled
|
|
||||||
dark_mode: bool,
|
|
||||||
|
|
||||||
/// Stores opened windows/elements for later reference
|
/// Stores opened windows/elements for later reference
|
||||||
opened: Opened,
|
opened: Opened,
|
||||||
|
|
||||||
@ -242,7 +239,12 @@ impl MathApp {
|
|||||||
cc.egui_ctx.set_fonts(data.fonts);
|
cc.egui_ctx.set_fonts(data.fonts);
|
||||||
|
|
||||||
// Set dark mode by default
|
// Set dark mode by default
|
||||||
cc.egui_ctx.set_visuals(Visuals::dark());
|
cc.egui_ctx.set_visuals(crate::style::STYLE);
|
||||||
|
|
||||||
|
// Set spacing
|
||||||
|
let mut style: egui::Style = (*cc.egui_ctx.style()).clone();
|
||||||
|
style.spacing = crate::style::SPACING;
|
||||||
|
cc.egui_ctx.set_style(style);
|
||||||
|
|
||||||
tracing::info!("Initialized! Took: {:?}", start.elapsed());
|
tracing::info!("Initialized! Took: {:?}", start.elapsed());
|
||||||
|
|
||||||
@ -254,7 +256,6 @@ impl MathApp {
|
|||||||
functions: FunctionManager::default(),
|
functions: FunctionManager::default(),
|
||||||
|
|
||||||
last_info: (None, None),
|
last_info: (None, None),
|
||||||
dark_mode: true, // dark mode is default and is previously set
|
|
||||||
text: data.text,
|
text: data.text,
|
||||||
opened: Opened::default(),
|
opened: Opened::default(),
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
@ -440,25 +441,6 @@ impl App for MathApp {
|
|||||||
.clicked(),
|
.clicked(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Toggles dark/light mode
|
|
||||||
if ui
|
|
||||||
.add(Button::new(match self.dark_mode {
|
|
||||||
true => "🌞",
|
|
||||||
false => "🌙",
|
|
||||||
}))
|
|
||||||
.on_hover_text(match self.dark_mode {
|
|
||||||
true => "Turn the Lights on!",
|
|
||||||
false => "Turn the Lights off.",
|
|
||||||
})
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
ctx.set_visuals(match self.dark_mode {
|
|
||||||
true => Visuals::light(),
|
|
||||||
false => Visuals::dark(),
|
|
||||||
});
|
|
||||||
self.dark_mode.bitxor_assign(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display Area and time of last frame
|
// Display Area and time of last frame
|
||||||
if let Some(ref area) = self.last_info.0 {
|
if let Some(ref area) = self.last_info.0 {
|
||||||
ui.label(area);
|
ui.label(area);
|
||||||
@ -495,14 +477,23 @@ impl App for MathApp {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Welcome window
|
// Welcome window
|
||||||
Window::new("Welcome!")
|
if self.opened.welcome {
|
||||||
.open(&mut self.opened.welcome)
|
let welcome_response = Window::new("Welcome!")
|
||||||
.anchor(Align2::CENTER_CENTER, Vec2::ZERO)
|
.open(&mut self.opened.welcome)
|
||||||
.resizable(false)
|
.anchor(Align2::CENTER_CENTER, Vec2::ZERO)
|
||||||
.collapsible(false)
|
.resizable(false)
|
||||||
.show(ctx, |ui| {
|
.collapsible(false)
|
||||||
ui.label(self.text.welcome.clone());
|
.show(ctx, |ui| {
|
||||||
});
|
ui.label(self.text.welcome.clone());
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(response) = welcome_response {
|
||||||
|
// if user clicks off welcome window, close it
|
||||||
|
if response.response.clicked_elsewhere() {
|
||||||
|
self.opened.welcome = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Window with information about the build and current commit
|
// Window with information about the build and current commit
|
||||||
Window::new("Info")
|
Window::new("Info")
|
||||||
@ -523,78 +514,75 @@ impl App for MathApp {
|
|||||||
self.side_panel(ctx);
|
self.side_panel(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Central panel which contains the central plot (or an error created when
|
const EMPTY_FRAME: Frame = Frame {
|
||||||
// parsing)
|
inner_margin: Margin::symmetric(0.0, 0.0),
|
||||||
CentralPanel::default()
|
rounding: Rounding::none(),
|
||||||
.frame(Frame {
|
fill: crate::style::STYLE.window_fill(),
|
||||||
inner_margin: Margin::symmetric(0.0, 0.0),
|
..Frame::none()
|
||||||
rounding: Rounding::none(),
|
};
|
||||||
fill: ctx.style().visuals.window_fill(),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
// Display an error if it exists
|
|
||||||
let errors_formatted: String = self
|
|
||||||
.functions
|
|
||||||
.get_entries()
|
|
||||||
.iter()
|
|
||||||
.map(|(_, func)| func.get_test_result())
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(_, error)| error.is_some())
|
|
||||||
.map(|(i, error)| {
|
|
||||||
// use unwrap_unchecked as None Errors are already filtered out
|
|
||||||
unsafe {
|
|
||||||
format!("(Function #{}) {}\n", i, error.as_ref().unwrap_unchecked())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<String>();
|
|
||||||
|
|
||||||
if !errors_formatted.is_empty() {
|
// Central panel which contains the central plot (or an error created when parsing)
|
||||||
ui.centered_and_justified(|ui| {
|
CentralPanel::default().frame(EMPTY_FRAME).show(ctx, |ui| {
|
||||||
ui.heading(errors_formatted);
|
// Display an error if it exists
|
||||||
|
let errors_formatted: String = self
|
||||||
|
.functions
|
||||||
|
.get_entries()
|
||||||
|
.iter()
|
||||||
|
.map(|(_, func)| func.get_test_result())
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, error)| error.is_some())
|
||||||
|
.map(|(i, error)| {
|
||||||
|
// use unwrap_unchecked as None Errors are already filtered out
|
||||||
|
unsafe { format!("(Function #{}) {}\n", i, error.as_ref().unwrap_unchecked()) }
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
if !errors_formatted.is_empty() {
|
||||||
|
ui.centered_and_justified(|ui| {
|
||||||
|
ui.heading(errors_formatted);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let available_width: usize = (ui.available_width() as usize) + 1; // Used in later logic
|
||||||
|
let width_changed = available_width != self.settings.plot_width;
|
||||||
|
self.settings.plot_width = available_width;
|
||||||
|
|
||||||
|
// Create and setup plot
|
||||||
|
Plot::new("plot")
|
||||||
|
.set_margin_fraction(Vec2::ZERO)
|
||||||
|
.data_aspect(1.0)
|
||||||
|
.include_y(0)
|
||||||
|
.show(ui, |plot_ui| {
|
||||||
|
let bounds = plot_ui.plot_bounds();
|
||||||
|
let min_x: f64 = bounds.min()[0];
|
||||||
|
let max_x: f64 = bounds.max()[0];
|
||||||
|
let min_max_changed =
|
||||||
|
(min_x != self.settings.min_x) | (max_x != self.settings.max_x);
|
||||||
|
self.settings.min_x = min_x;
|
||||||
|
self.settings.max_x = max_x;
|
||||||
|
|
||||||
|
dyn_mut_iter(self.functions.get_entries_mut()).for_each(|(_, function)| {
|
||||||
|
function.calculate(width_changed, min_max_changed, &self.settings)
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let available_width: usize = (ui.available_width() as usize) + 1; // Used in later logic
|
let area: Vec<Option<f64>> = self
|
||||||
let width_changed = available_width != self.settings.plot_width;
|
.functions
|
||||||
self.settings.plot_width = available_width;
|
.get_entries()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, (_, function))| {
|
||||||
|
function.display(plot_ui, &self.settings, COLORS[i])
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Create and setup plot
|
self.last_info.0 = if area.iter().any(|e| e.is_some()) {
|
||||||
Plot::new("plot")
|
Some(format!("Area: {}", option_vec_printer(area.as_slice())))
|
||||||
.set_margin_fraction(Vec2::ZERO)
|
} else {
|
||||||
.data_aspect(1.0)
|
None
|
||||||
.include_y(0)
|
};
|
||||||
.show(ui, |plot_ui| {
|
});
|
||||||
let bounds = plot_ui.plot_bounds();
|
});
|
||||||
let min_x: f64 = bounds.min()[0];
|
|
||||||
let max_x: f64 = bounds.max()[0];
|
|
||||||
let min_max_changed =
|
|
||||||
(min_x != self.settings.min_x) | (max_x != self.settings.max_x);
|
|
||||||
self.settings.min_x = min_x;
|
|
||||||
self.settings.max_x = max_x;
|
|
||||||
|
|
||||||
dyn_mut_iter(self.functions.get_entries_mut()).for_each(|(_, function)| {
|
|
||||||
function.calculate(width_changed, min_max_changed, &self.settings)
|
|
||||||
});
|
|
||||||
|
|
||||||
let area: Vec<Option<f64>> = self
|
|
||||||
.functions
|
|
||||||
.get_entries()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, (_, function))| {
|
|
||||||
function.display(plot_ui, &self.settings, COLORS[i])
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.last_info.0 = if area.iter().any(|e| e.is_some()) {
|
|
||||||
Some(format!("Area: {}", option_vec_printer(area.as_slice())))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Calculate and store the last time it took to draw the frame
|
// Calculate and store the last time it took to draw the frame
|
||||||
self.last_info.1 = start.map(|a| format!("Took: {:?}", a.elapsed()));
|
self.last_info.1 = start.map(|a| format!("Took: {:?}", a.elapsed()));
|
||||||
|
|||||||
91
src/style.rs
Normal file
91
src/style.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
use egui::{
|
||||||
|
style::{Margin, Selection, Spacing, WidgetVisuals, Widgets},
|
||||||
|
Visuals,
|
||||||
|
};
|
||||||
|
use emath::vec2;
|
||||||
|
use epaint::{Color32, Rounding, Shadow, Stroke};
|
||||||
|
|
||||||
|
const fn widgets_dark() -> Widgets {
|
||||||
|
Widgets {
|
||||||
|
noninteractive: WidgetVisuals {
|
||||||
|
bg_fill: Color32::from_gray(27), // window background
|
||||||
|
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), // separators, indentation lines, windows outlines
|
||||||
|
fg_stroke: Stroke::new(1.0, Color32::from_gray(140)), // normal text color
|
||||||
|
rounding: Rounding::same(2.0),
|
||||||
|
expansion: 0.0,
|
||||||
|
},
|
||||||
|
inactive: WidgetVisuals {
|
||||||
|
bg_fill: Color32::from_gray(60), // button background
|
||||||
|
bg_stroke: Stroke::default(),
|
||||||
|
fg_stroke: Stroke::new(1.0, Color32::from_gray(180)), // button text
|
||||||
|
rounding: Rounding::same(2.0),
|
||||||
|
expansion: 0.0,
|
||||||
|
},
|
||||||
|
hovered: WidgetVisuals {
|
||||||
|
bg_fill: Color32::from_gray(70),
|
||||||
|
bg_stroke: Stroke::new(1.0, Color32::from_gray(150)), // e.g. hover over window edge or button
|
||||||
|
fg_stroke: Stroke::new(1.5, Color32::from_gray(240)),
|
||||||
|
rounding: Rounding::same(3.0),
|
||||||
|
expansion: 1.0,
|
||||||
|
},
|
||||||
|
active: WidgetVisuals {
|
||||||
|
bg_fill: Color32::from_gray(55),
|
||||||
|
bg_stroke: Stroke::new(1.0, Color32::WHITE),
|
||||||
|
fg_stroke: Stroke::new(2.0, Color32::WHITE),
|
||||||
|
rounding: Rounding::same(2.0),
|
||||||
|
expansion: 1.0,
|
||||||
|
},
|
||||||
|
open: WidgetVisuals {
|
||||||
|
bg_fill: Color32::from_gray(27),
|
||||||
|
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)),
|
||||||
|
fg_stroke: Stroke::new(1.0, Color32::from_gray(210)),
|
||||||
|
rounding: Rounding::same(2.0),
|
||||||
|
expansion: 0.0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const STYLE: Visuals = dark();
|
||||||
|
|
||||||
|
pub const fn dark() -> Visuals {
|
||||||
|
Visuals {
|
||||||
|
dark_mode: true,
|
||||||
|
override_text_color: None,
|
||||||
|
widgets: widgets_dark(),
|
||||||
|
selection: Selection::default(),
|
||||||
|
hyperlink_color: Color32::from_rgb(90, 170, 255),
|
||||||
|
faint_bg_color: Color32::from_gray(35),
|
||||||
|
extreme_bg_color: Color32::from_gray(10), // e.g. TextEdit background
|
||||||
|
code_bg_color: Color32::from_gray(64),
|
||||||
|
window_rounding: Rounding::same(1.5),
|
||||||
|
window_shadow: Shadow::default(), // no shadow
|
||||||
|
popup_shadow: Shadow::default(), // no shadow
|
||||||
|
resize_corner_size: 12.0,
|
||||||
|
text_cursor_width: 2.0,
|
||||||
|
text_cursor_preview: false,
|
||||||
|
clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion
|
||||||
|
button_frame: true,
|
||||||
|
collapsing_header_frame: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SPACING: Spacing = spacing();
|
||||||
|
|
||||||
|
pub const fn spacing() -> Spacing {
|
||||||
|
Spacing {
|
||||||
|
item_spacing: vec2(8.0, 3.0),
|
||||||
|
window_margin: Margin::same(6.0),
|
||||||
|
button_padding: vec2(4.0, 1.0),
|
||||||
|
indent: 18.0, // match checkbox/radio-button with `button_padding.x + icon_width + icon_spacing`
|
||||||
|
interact_size: vec2(40.0, 18.0),
|
||||||
|
slider_width: 100.0,
|
||||||
|
text_edit_width: 280.0,
|
||||||
|
icon_width: 14.0,
|
||||||
|
icon_width_inner: 8.0,
|
||||||
|
icon_spacing: 4.0,
|
||||||
|
tooltip_width: 600.0,
|
||||||
|
combo_height: 200.0,
|
||||||
|
scroll_bar_width: 8.0,
|
||||||
|
indent_ends_with_horizontal_line: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user