lots of refactoring

This commit is contained in:
Simon Gardling 2022-04-01 11:21:48 -04:00
parent 3377cc46df
commit 0130f14562
5 changed files with 172 additions and 167 deletions

View File

@ -3,19 +3,13 @@
use crate::math_app::AppSettings;
use crate::misc::*;
use crate::parsing::{process_func_str, BackingFunction};
use crate::suggestions::{generate_hint, HintEnum};
use crate::widgets::AutoComplete;
use eframe::{egui, epaint};
use egui::{
plot::{BarChart, PlotUi, Value},
text::CCursor,
text_edit::CursorRange,
widgets::plot::Bar,
Key, TextEdit, Widget,
};
use epaint::{
text::cursor::{Cursor, PCursor, RCursor},
Color32,
};
use epaint::Color32;
use std::fmt::{self, Debug};
#[cfg(threading)]
@ -38,37 +32,6 @@ lazy_static::lazy_static! {
pub static ref DEFAULT_FUNCTION_ENTRY: FunctionEntry = FunctionEntry::default();
}
#[derive(Clone)]
struct AutoComplete {
pub i: usize,
pub hint: HintEnum<'static>,
pub func_str: Option<String>,
pub changed: bool,
}
impl Default for AutoComplete {
fn default() -> AutoComplete {
AutoComplete {
i: 0,
hint: HintEnum::None,
func_str: None,
changed: true,
}
}
}
impl AutoComplete {
fn changed(&mut self, string: String) {
if self.func_str != Some(string.clone()) {
self.changed = true;
self.func_str = Some(string.clone());
self.hint = generate_hint(string);
} else {
self.changed = false;
}
}
}
/// `FunctionEntry` is a function that can calculate values, integrals,
/// derivatives, etc etc
#[derive(Clone)]
@ -130,117 +93,15 @@ impl FunctionEntry {
pub fn auto_complete(
&mut self, ui: &mut egui::Ui, string: &mut String,
) -> (bool, bool, Option<String>) {
// Put here so these key presses don't interact with other elements
let enter_pressed = ui
.input_mut()
.consume_key(egui::Modifiers::NONE, Key::Enter);
let tab_pressed = ui.input_mut().consume_key(egui::Modifiers::NONE, Key::Tab);
let (output_string, in_focus) = self.autocomplete.ui(ui, string.to_string());
let te_id = ui.make_persistent_id("text_edit_ac".to_string());
// update self.autocomplete
self.autocomplete.changed(string.clone());
let mut func_edit = egui::TextEdit::singleline(string)
.hint_forward(true)
.lock_focus(true);
if self.autocomplete.hint.is_none() {
func_edit.id(te_id).ui(ui);
return (false, false, self.get_test_result());
}
if let Some(single_hint) = self.autocomplete.hint.get_single() {
let func_edit_2 = func_edit;
func_edit = func_edit_2.hint_text(&single_hint);
}
let re = func_edit.id(te_id).ui(ui);
let func_edit_focus = re.has_focus();
// If in focus and right arrow key was pressed, apply hint
if func_edit_focus {
let mut push_cursor: bool = false;
let apply_key = ui.input().key_pressed(Key::ArrowRight) | enter_pressed | tab_pressed;
if apply_key && let Some(single_hint) = self.autocomplete.hint.get_single() {
push_cursor = true;
*string = string.clone() + &single_hint;
} else if self.autocomplete.hint.is_multi() {
let selections = self.autocomplete.hint.ensure_many();
let max_i = selections.len() as i16 - 1;
let mut i = self.autocomplete.i as i16;
if ui.input().key_pressed(Key::ArrowDown) {
i += 1;
if i > max_i {
i = 0;
}
} else if ui.input().key_pressed(Key::ArrowUp) {
i -= 1;
if 0 > i {
i = max_i
}
}
self.autocomplete.i = i as usize;
let popup_id = ui.make_persistent_id("autocomplete_popup");
let mut clicked = false;
egui::popup_below_widget(ui, popup_id, &re, |ui| {
for (i, candidate) in selections.iter().enumerate() {
if ui
.selectable_label(i == self.autocomplete.i, *candidate)
.clicked()
{
clicked = true;
self.autocomplete.i = i;
}
}
});
if clicked | apply_key {
*string += selections[self.autocomplete.i];
push_cursor = true;
// don't need this here as it simply won't be display next frame in `math_app.rs`
// ui.memory().close_popup();
} else {
ui.memory().open_popup(popup_id);
}
}
// Push cursor to end if needed
if push_cursor {
let mut state = TextEdit::load_state(ui.ctx(), te_id).unwrap();
state.set_cursor_range(Some(CursorRange::one(Cursor {
ccursor: CCursor {
index: 0,
prefer_next_row: false,
},
rcursor: RCursor { row: 0, column: 0 },
pcursor: PCursor {
paragraph: 0,
offset: 10000,
prefer_next_row: false,
},
})));
TextEdit::store_state(ui.ctx(), te_id, state);
}
}
let changed = *string != self.get_func_raw();
let changed = output_string != *string;
if changed {
self.update_string(&*string);
*string = output_string.clone();
self.update_string(&output_string);
}
(func_edit_focus, changed, self.get_test_result())
(in_focus, changed, self.get_test_result())
}
pub fn get_test_result(&self) -> Option<String> { self.test_result.clone() }

View File

@ -11,6 +11,7 @@ mod math_app;
mod misc;
mod parsing;
mod suggestions;
mod widgets;
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {

View File

@ -11,6 +11,7 @@ mod math_app;
mod misc;
mod parsing;
mod suggestions;
mod widgets;
// For running the program natively! (Because why not?)
#[cfg(not(target_arch = "wasm32"))]

View File

@ -7,6 +7,7 @@ use egui::{
FontFamily, Key, RichText, SidePanel, Slider, TopBottomPanel, Vec2, Visuals, Window,
};
use instant::Duration;
use std::collections::HashMap;
use std::{collections::BTreeMap, io::Read, ops::BitXorAssign, str};
#[cfg(threading)]
@ -291,20 +292,14 @@ pub struct MathApp {
/// took for the last frame (the Duration). Stored in a Tuple.
last_info: (Vec<Option<f64>>, Duration),
/// Stores whether or not the Help window is open
help_open: bool,
/// Stores whether or not the Info window is open
info_open: bool,
/// Stores whether or not dark mode is enabled
dark_mode: bool,
/// Stores whether or not the text boxes are focused
text_boxes_focused: bool,
/// Stores whether or not the side panel is shown or not
show_side_panel: bool,
/// Stores opened windows/elements for later reference
opened: HashMap<&'static str, bool>,
/// Stores settings (pretty self-explanatory)
settings: AppSettings,
@ -318,11 +313,9 @@ impl Default for MathApp {
func_errors: vec![None],
exists_error: false,
last_info: (vec![None], Duration::ZERO),
help_open: true,
info_open: false,
dark_mode: true,
text_boxes_focused: false,
show_side_panel: true,
opened: HashMap::from([("help", true), ("info", false), ("side_panel", true)]),
settings: AppSettings::default(),
}
}
@ -356,6 +349,10 @@ impl MathApp {
Self::default() // initialize `MathApp`
}
fn get_opened_mut(&mut self, id: &str) -> &mut bool { self.opened.get_mut(id).unwrap() }
fn get_opened(&self, id: &str) -> bool { *self.opened.get(id).unwrap() }
/// Creates SidePanel which contains configuration options
fn side_panel(&mut self, ctx: &Context) {
// Side Panel which contains vital options to the operation of the application
@ -555,7 +552,7 @@ impl epi::App for MathApp {
// this is behind this check as if it wasn't, it would trigger if the user
// presses the h key in a text box as well
if !self.text_boxes_focused {
self.show_side_panel
self.get_opened_mut("side_panel")
.bitxor_assign(ctx.input().key_down(Key::H));
}
@ -566,9 +563,10 @@ impl epi::App for MathApp {
TopBottomPanel::top("top_bar").show(ctx, |ui| {
ui.horizontal(|ui| {
// Button in top bar to toggle showing the side panel
self.show_side_panel.bitxor_assign(
let side_curr_open = self.get_opened("help");
self.get_opened_mut("side_panel").bitxor_assign(
ui.add(Button::new("Panel"))
.on_hover_text(match self.show_side_panel {
.on_hover_text(match side_curr_open {
true => "Hide Side Panel",
false => "Show Side Panel",
})
@ -587,9 +585,10 @@ impl epi::App for MathApp {
}
// Toggles opening the Help window
self.help_open.bitxor_assign(
let help_curr_open = self.get_opened("help");
self.get_opened_mut("help").bitxor_assign(
ui.add(Button::new("Help"))
.on_hover_text(match self.help_open {
.on_hover_text(match help_curr_open {
true => "Close Help Window",
false => "Open Help Window",
})
@ -597,9 +596,10 @@ impl epi::App for MathApp {
);
// Toggles opening the Info window
self.info_open.bitxor_assign(
let info_curr_open = self.get_opened("info");
self.get_opened_mut("info").bitxor_assign(
ui.add(Button::new("Info"))
.on_hover_text(match self.info_open {
.on_hover_text(match info_curr_open {
true => "Close Info Window",
false => "Open Info Window",
})
@ -631,7 +631,7 @@ impl epi::App for MathApp {
// Help window with information for users
Window::new("Help")
.default_pos([200.0, 200.0])
.open(&mut self.help_open)
.open(self.get_opened_mut("help"))
.resizable(false)
.collapsible(false)
.show(ctx, |ui| {
@ -661,7 +661,7 @@ impl epi::App for MathApp {
// Window with information about the build and current commit
Window::new("Info")
.default_pos([200.0, 200.0])
.open(&mut self.info_open)
.open(self.get_opened_mut("info"))
.resizable(false)
.collapsible(false)
.show(ctx, |ui| {
@ -669,7 +669,7 @@ impl epi::App for MathApp {
});
// If side panel is enabled, show it.
if self.show_side_panel {
if self.get_opened("side_panel") {
self.side_panel(ctx);
} else {
self.text_boxes_focused = false;

142
src/widgets.rs Normal file
View File

@ -0,0 +1,142 @@
use crate::suggestions::{generate_hint, HintEnum};
use eframe::{egui, epaint};
use egui::{text::CCursor, text_edit::CursorRange, Key, Modifiers, TextEdit, Widget};
use epaint::text::cursor::{Cursor, PCursor, RCursor};
#[derive(Clone)]
pub struct AutoComplete {
pub i: usize,
pub hint: HintEnum<'static>,
pub func_str: Option<String>,
pub changed: bool,
}
impl Default for AutoComplete {
fn default() -> AutoComplete {
AutoComplete {
i: 0,
hint: HintEnum::None,
func_str: None,
changed: true,
}
}
}
impl AutoComplete {
fn changed(&mut self, string: String) {
if self.func_str != Some(string.clone()) {
self.changed = true;
self.func_str = Some(string.clone());
self.hint = generate_hint(string);
} else {
self.changed = false;
}
}
pub fn ui(&mut self, ui: &mut egui::Ui, string: String) -> (String, bool) {
let mut new_string = string.clone();
// Put here so these key presses don't interact with other elements
let enter_pressed = ui.input_mut().consume_key(Modifiers::NONE, Key::Enter);
let tab_pressed = ui.input_mut().consume_key(Modifiers::NONE, Key::Tab);
let te_id = ui.make_persistent_id("text_edit_ac".to_string());
// update self
self.changed(string.clone());
let mut func_edit = egui::TextEdit::singleline(&mut new_string)
.hint_forward(true)
.lock_focus(true);
if self.hint.is_none() {
return (string, func_edit.id(te_id).ui(ui).has_focus());
}
if let Some(single_hint) = self.hint.get_single() {
let func_edit_2 = func_edit;
func_edit = func_edit_2.hint_text(&single_hint);
}
let re = func_edit.id(te_id).ui(ui);
let func_edit_focus = re.has_focus();
// If in focus and right arrow key was pressed, apply hint
if func_edit_focus {
let mut push_cursor: bool = false;
let apply_key = ui.input().key_pressed(Key::ArrowRight) | enter_pressed | tab_pressed;
if apply_key && let Some(single_hint) = self.hint.get_single() {
push_cursor = true;
new_string = string + &single_hint;
} else if self.hint.is_multi() {
let selections = self.hint.ensure_many();
let max_i = selections.len() as i16 - 1;
let mut i = self.i as i16;
if ui.input().key_pressed(Key::ArrowDown) {
i += 1;
if i > max_i {
i = 0;
}
} else if ui.input().key_pressed(Key::ArrowUp) {
i -= 1;
if 0 > i {
i = max_i
}
}
self.i = i as usize;
let popup_id = ui.make_persistent_id("autocomplete_popup");
let mut clicked = false;
egui::popup_below_widget(ui, popup_id, &re, |ui| {
for (i, candidate) in selections.iter().enumerate() {
if ui
.selectable_label(i == self.i, *candidate)
.clicked()
{
clicked = true;
self.i = i;
}
}
});
if clicked | apply_key {
new_string += selections[self.i];
push_cursor = true;
// don't need this here as it simply won't be display next frame in `math_app.rs`
// ui.memory().close_popup();
} else {
ui.memory().open_popup(popup_id);
}
}
// Push cursor to end if needed
if push_cursor {
let mut state = TextEdit::load_state(ui.ctx(), te_id).unwrap();
state.set_cursor_range(Some(CursorRange::one(Cursor {
ccursor: CCursor {
index: 0,
prefer_next_row: false,
},
rcursor: RCursor { row: 0, column: 0 },
pcursor: PCursor {
paragraph: 0,
offset: 10000,
prefer_next_row: false,
},
})));
TextEdit::store_state(ui.ctx(), te_id, state);
}
return (new_string, true);
}
(string, false)
}
}