diff --git a/Cargo.lock b/Cargo.lock index c778163..cdedca4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -570,7 +570,7 @@ dependencies = [ [[package]] name = "calcifer" -version = "1.0.3" +version = "1.0.4" dependencies = [ "eframe", "egui_extras", diff --git a/Cargo.toml b/Cargo.toml index a7fa7fa..2112680 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "calcifer" -version = "1.0.3" +version = "1.0.4" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 1d401a4..103461b 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,8 @@ Added Alt+Arrows to move through tabs Added Zoom Added cd Added terminal color +Max tabs 8 => 20 +Max framerate => 30 fps (less cpu usage) - +# 1.0.4 : +Added close tab and refresh confirm prompt diff --git a/calcifer_save.json b/calcifer_save.json index 1336658..5b4d04e 100644 --- a/calcifer_save.json +++ b/calcifer_save.json @@ -1 +1 @@ -{"tabs":["/home/penwing/Documents/notes/victory2.txt","/home/penwing/Documents/projects/rust/calcifer/src/calcifer/code_editor/themes/sonokai.rs"],"theme":6} \ No newline at end of file +{"tabs":["/home/penwing/Documents/notes/victory2.txt","untitled"],"theme":6} \ No newline at end of file diff --git a/src/calcifer.rs b/src/calcifer.rs index da48a29..eb5bff4 100644 --- a/src/calcifer.rs +++ b/src/calcifer.rs @@ -139,8 +139,13 @@ impl Calcifer { ui.style_mut().visuals.override_text_color = None; - if ui.link("X").clicked() { - self.selected_tab = self.delete_tab(index); + if ui.link("X").clicked() && !self.close_tab_confirm.visible { + if tab.saved { + self.delete_tab(index); + } else { + self.close_tab_confirm.ask(); + self.tab_to_close = index; + } } ui.separator(); } @@ -180,7 +185,7 @@ impl Calcifer { let lines = current_tab.code.chars().filter(|&c| c == '\n').count() + 1; let mut override_cursor : Option = None; - if !self.search.result_selected && self.search.tab_selected { + if !self.search.result_selected { override_cursor = Some(CCursorRange::two( CCursor::new(self.search.get_cursor_start()), CCursor::new(self.search.get_cursor_end()), diff --git a/src/calcifer/app_base.rs b/src/calcifer/app_base.rs index deabd67..6098d9c 100644 --- a/src/calcifer/app_base.rs +++ b/src/calcifer/app_base.rs @@ -10,6 +10,18 @@ use crate::SAVE_PATH; impl Calcifer { + pub fn handle_confirm(&mut self) { + if self.close_tab_confirm.proceed { + self.close_tab_confirm.close(); + self.delete_tab(self.tab_to_close); + } + + if self.refresh_confirm.proceed { + self.refresh_confirm.close(); + self.tabs[self.selected_tab.to_index()].refresh(); + } + } + pub fn save_tab(&self) -> Option { if self.tabs[self.selected_tab.to_index()].path.file_name().expect("Could not get Tab Name").to_string_lossy().to_string() == "untitled" { return self.save_tab_as(); @@ -131,9 +143,9 @@ impl Calcifer { } - pub fn delete_tab(&mut self, index : usize) -> tools::TabNumber { + pub fn delete_tab(&mut self, index : usize) { self.tabs.remove(index); - return tools::TabNumber::from_index(min(index, self.tabs.len() - 1)) + self.selected_tab = tools::TabNumber::from_index(min(index, self.tabs.len() - 1)); } diff --git a/src/calcifer_base.rs b/src/calcifer_base.rs index 2f021e6..4e2bb38 100644 --- a/src/calcifer_base.rs +++ b/src/calcifer_base.rs @@ -1,6 +1,12 @@ // Hello there, Master impl super::Calcifer { + pub fn handle_confirm(&mut self) { + if self.close_tab_confirm.proceed { + self.close_tab_confirm.close(); + self.delete_tab(self.tab_to_close); + } + } pub fn save_tab(&self) -> Option { if self.tabs[self.selected_tab.to_index()].path.file_name().expect("Could not get Tab Name").to_string_lossy().to_string() == "untitled" { return self.save_tab_as(); @@ -118,9 +124,9 @@ impl super::Calcifer { } } - fn delete_tab(&mut self, index : usize) -> tools::TabNumber { + fn delete_tab(&mut self, index : usize) { self.tabs.remove(index); - return tools::TabNumber::from_index(min(index, self.tabs.len() - 1)) + self.selected_tab = tools::TabNumber::from_index(min(index, self.tabs.len() - 1)); } fn toggle(&self, ui: &mut egui::Ui, display : bool, title : &str) -> bool { @@ -134,4 +140,5 @@ impl super::Calcifer { return !display } return display - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9595233..7f865e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,6 @@ use eframe::egui; use calcifer::code_editor::ColorTheme; use std::{path::Path, sync::Arc, time, thread}; -use tools::Demo; use calcifer::code_editor::themes::DEFAULT_THEMES; const TERMINAL_HEIGHT : f32 = 200.0; @@ -41,7 +40,7 @@ fn main() -> Result<(), eframe::Error> { } eframe::run_native( - "Calcifer v1.0.3", + "Calcifer v1.0.4", options, Box::new(move |_cc| Box::from(Calcifer::from_app_state(app_state))), ) @@ -59,13 +58,16 @@ struct Calcifer { font_size: f32, search: tools::search::SearchWindow, - searching: bool, debug_display: bool, time_watch: Vec, next_frame: time::Instant, tree_display: bool, + + close_tab_confirm: tools::confirm::ConfirmWindow, + tab_to_close: usize, + refresh_confirm: tools::confirm::ConfirmWindow, } @@ -82,13 +84,16 @@ impl Default for Calcifer { font_size: 14.0, search: tools::search::SearchWindow::default(), - searching: false, debug_display: false, time_watch: vec![0.0; TIME_LABELS.len()], next_frame: time::Instant::now(), tree_display: false, + + close_tab_confirm: tools::confirm::ConfirmWindow::new("You have some unsaved changes, Do you still want to close this document ?", "Confirm Close"), + tab_to_close: 0, + refresh_confirm: tools::confirm::ConfirmWindow::new("You have some unsaved changes, Do you still want to refresh this document ?", "Confirm Refresh"), } } } @@ -101,8 +106,12 @@ impl eframe::App for Calcifer { let mut watch = time::Instant::now(); - if ctx.input( |i| i.key_pressed(egui::Key::T) && i.modifiers.ctrl) { - self.tabs[self.selected_tab.to_index()].refresh(); + if ctx.input( |i| i.key_pressed(egui::Key::T) && i.modifiers.ctrl) && !self.refresh_confirm.visible { + if self.tabs[self.selected_tab.to_index()].saved { + self.tabs[self.selected_tab.to_index()].refresh(); + } else { + self.refresh_confirm.ask(); + } } if ctx.input( |i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) { @@ -130,10 +139,8 @@ impl eframe::App for Calcifer { } if ctx.input( |i| i.key_pressed(egui::Key::F) && i.modifiers.ctrl) { - self.searching = !self.searching.clone(); - if self.searching { - self.search.initialized = false; - } + self.search.visible = !self.search.visible.clone(); + self.search.initialized = !self.search.visible.clone(); } self.draw_settings(ctx); @@ -160,15 +167,11 @@ impl eframe::App for Calcifer { self.time_watch[4] = watch.elapsed().as_micros() as f32 / 1000.0; - if self.searching { - self.search.show(ctx, &mut self.searching, &mut self.tabs, &mut self.selected_tab); - } - - if !self.search.tab_selected && self.search.get_tab() != self.selected_tab { - self.selected_tab = self.search.get_tab(); - println!("changed tab to {}", self.selected_tab.to_index()); - } - self.search.tab_selected = true; + self.search.show(ctx, &mut self.tabs, &mut self.selected_tab); + self.close_tab_confirm.show(ctx, &mut self.tabs, &mut self.selected_tab); + self.refresh_confirm.show(ctx, &mut self.tabs, &mut self.selected_tab); + + self.handle_confirm(); } fn on_exit(&mut self, _gl : std::option::Option<&eframe::glow::Context>) { diff --git a/src/tools/confirm.rs b/src/tools/confirm.rs new file mode 100644 index 0000000..88a0c7d --- /dev/null +++ b/src/tools/confirm.rs @@ -0,0 +1,59 @@ +use eframe::egui; +use crate::tools::{Tab, TabNumber}; + + +pub struct ConfirmWindow { + pub visible: bool, + pub proceed: bool, + prompt: String, + id: String, +} + + +impl ConfirmWindow { + pub fn new(prompt: &str, id: &str) -> Self { + Self { + visible: false, + proceed: false, + prompt: prompt.to_string(), + id: id.to_string(), + } + } + + + pub fn show(&mut self, ctx: &egui::Context, tabs: &mut Vec, selected_tab: &mut TabNumber) { + let mut visible = self.visible.clone(); + egui::Window::new(self.id.clone()) + .open(&mut visible) //I want it to be able to change its visibility (if user close manually) + .vscroll(true) + .hscroll(true) + .show(ctx, |ui| self.ui(ui, tabs, selected_tab)); //but I want to edit the rest of the parameters and maybe close automatically + self.visible = self.visible.clone() && visible; + } + + + fn ui(&mut self, ui: &mut egui::Ui, _tabs: &mut Vec, _selected_tab: &mut TabNumber) { + ui.set_min_width(250.0); + ui.label(self.prompt.clone()); + ui.vertical_centered(|ui| { + if ui.add(egui::Button::new("Yes")).clicked() { + self.proceed = true; + } + + if ui.add(egui::Button::new("Cancel")).clicked() { + self.visible = false; + } + }); + } + + pub fn ask(&mut self) { + self.visible = true; + self.proceed = false; + } + + + pub fn close(&mut self) { + self.visible = false; + self.proceed = false; + } +} \ No newline at end of file diff --git a/src/tools/mod.rs b/src/tools/mod.rs index 81fafad..af402e1 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -6,6 +6,7 @@ use crate::DISPLAY_PATH_DEPTH; //my tools; pub mod search; +pub mod confirm; pub mod terminal; pub use terminal::*; @@ -14,21 +15,6 @@ pub mod tabs; pub use tabs::*; - -pub trait View { - fn ui(&mut self, ui: &mut egui::Ui, tabs: &mut Vec, selected_tab: &mut TabNumber); -} - -/// Something to view -pub trait Demo { - fn name(&self) -> &str; //'static - /// Show windows, etc - fn show(&mut self, ctx: &egui::Context, open: &mut bool, tabs: &mut Vec, selected_tab: &mut TabNumber); -} - - - - #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct AppState { pub tabs: Vec, diff --git a/src/tools/search.rs b/src/tools/search.rs index 508dec3..404a59f 100644 --- a/src/tools/search.rs +++ b/src/tools/search.rs @@ -1,7 +1,8 @@ -use eframe::egui; -use crate::tools::{View, Demo, tabs::Tab, tabs::TabNumber}; use std::{cmp::min}; +use eframe::egui; + use crate::RED; +use crate::tools::{tabs::Tab, tabs::TabNumber}; enum Action { @@ -33,6 +34,8 @@ impl Default for Selection { pub struct SearchWindow { + pub visible: bool, + search_text: String, searched_text: String, replace_text: String, @@ -44,7 +47,6 @@ pub struct SearchWindow { results: Vec, current_result: usize, - pub tab_selected: bool, pub result_selected: bool, } @@ -52,6 +54,8 @@ pub struct SearchWindow { impl Default for SearchWindow { fn default() -> Self { Self { + visible: false, + search_text: "".into(), searched_text: "".into(), replace_text: "".into(), @@ -63,29 +67,24 @@ impl Default for SearchWindow { results: vec![], current_result: 0, - tab_selected: true, result_selected: true, } } } -impl Demo for SearchWindow { - fn name(&self) -> &str { //'static - "Search" - } - - fn show(&mut self, ctx: &egui::Context, open: &mut bool, tabs: &mut Vec, selected_tab: &mut TabNumber) { - egui::Window::new(self.name()) - .open(open) +impl SearchWindow { + pub fn show(&mut self, ctx: &egui::Context, tabs: &mut Vec, selected_tab: &mut TabNumber) { + let mut visible = self.visible.clone(); + egui::Window::new("Search") + .open(&mut visible) //I want it to be able to change its visibility (if user close manually) .vscroll(true) .hscroll(true) - .show(ctx, |ui| self.ui(ui, tabs, selected_tab)); + .show(ctx, |ui| self.ui(ui, tabs, selected_tab)); //but I want to edit the rest of the parameters and maybe close automatically + self.visible = self.visible.clone() && visible; } -} - - -impl View for SearchWindow { + + fn ui(&mut self, ui: &mut egui::Ui, tabs: &mut Vec, selected_tab: &mut TabNumber) { ui.set_min_width(250.0); @@ -142,28 +141,25 @@ impl View for SearchWindow { match action { Action::Update => self.search(tabs, selected_tab), - Action::Next => self.find_next(tabs, selected_tab), - Action::Previous => self.find_previous(tabs, selected_tab), + Action::Next => self.find_result(tabs, selected_tab, 1), + Action::Previous => self.find_result(tabs, selected_tab, -1), Action::Replace => self.replace(tabs, selected_tab), Action::None => (), } } -} - -impl SearchWindow { - pub fn get_tab(&self) -> TabNumber { - self.results[self.current_result].tab.clone() - } - + + pub fn get_cursor_start(&self) -> usize { self.results[self.current_result].start.clone() } + pub fn get_cursor_end(&self) -> usize { self.results[self.current_result].end.clone() } + - fn search(&mut self, tabs: &Vec, selected_tab: &TabNumber) { + fn search(&mut self, tabs: &Vec, selected_tab: &mut TabNumber) { if self.search_text.len() == 0 { return } @@ -184,10 +180,11 @@ impl SearchWindow { self.current_result = 0; if self.results.len() > 0 { self.result_selected = false; - self.tab_selected = false; + *selected_tab = self.results[0].tab.clone(); } } + fn match_text(&self, tab_text: String, tab_number: TabNumber) -> Vec { let matches = tab_text.match_indices(&self.search_text.clone()).map(|(i, _)| Selection { tab : tab_number.clone(), @@ -198,33 +195,21 @@ impl SearchWindow { matches } - fn find_next(&mut self, tabs: &Vec, selected_tab: &TabNumber) { - if self.searched_text != self.search_text { - self.search(tabs, selected_tab); - } else if self.results.len() > 1 { - self.current_result = (self.current_result.clone() + 1) % self.results.len(); - self.result_selected = false; - if self.results[self.current_result].tab != *selected_tab { - self.tab_selected = false; - } - } + + fn find_result(&mut self, tabs: &Vec, selected_tab: &mut TabNumber, direction: i32) { + if self.searched_text != self.search_text { + self.search(tabs, &mut *selected_tab); + } else if self.results.len() > 1 { + self.current_result = (self.current_result as i32 + direction + self.results.len() as i32) as usize % self.results.len(); + self.result_selected = false; + *selected_tab = self.results[self.current_result].tab.clone(); + } } - fn find_previous(&mut self, tabs: &Vec, selected_tab: &TabNumber) { - if self.searched_text != self.search_text { - self.search(tabs, selected_tab); - } else { - self.current_result = self.current_result.checked_sub(1).unwrap_or(self.results.len() - 1); - self.result_selected = false; - if self.results[self.current_result].tab != *selected_tab { - self.tab_selected = false; - } - } - } - fn replace(&mut self, tabs: &mut Vec, selected_tab: &TabNumber) { + fn replace(&mut self, tabs: &mut Vec, selected_tab: &mut TabNumber) { if self.searched_text != self.search_text { - self.search(tabs, selected_tab); + self.search(tabs, &mut *selected_tab); } for element in &self.results { diff --git a/src/tools/tabs.rs b/src/tools/tabs.rs index 3cf3d3c..cf12862 100644 --- a/src/tools/tabs.rs +++ b/src/tools/tabs.rs @@ -5,24 +5,24 @@ use crate::MAX_TABS; #[derive(Debug, PartialEq, Eq, Clone)] pub enum TabNumber { - Open, - Number(u8), // Using a range for numeric values + Open, + Number(u8), // Using a range for numeric values } impl TabNumber { - pub fn from_index(n: usize) -> TabNumber { - match n { - 0..=MAX_TABS => TabNumber::Number(n as u8), - _ => TabNumber::Number(0), - } - } + pub fn from_index(n: usize) -> TabNumber { + match n { + 0..=MAX_TABS => TabNumber::Number(n as u8), + _ => TabNumber::Number(0), + } + } - pub fn to_index(&self) -> usize { - match self { - TabNumber::Number(n) => *n as usize, - _ => 0, - } - } + pub fn to_index(&self) -> usize { + match self { + TabNumber::Number(n) => *n as usize, + _ => 0, + } + } } @@ -68,6 +68,7 @@ impl Tab { pub fn refresh(&mut self) { self.code = read_to_string(self.path.clone()).expect("Not able to read the file").replace(&" ".repeat(4), "\t"); + self.saved = true; println!("refreshed {}", self.path.display()); } } \ No newline at end of file