diff --git a/Cargo.toml b/Cargo.toml index 7610905..372e8b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ced" -version = "0.2.0" +version = "0.2.1" edition = "2024" [dependencies] diff --git a/src/app.rs b/src/app.rs index 93aef0e..32ec686 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,3 +1,4 @@ +pub mod actions; pub mod config; pub mod shortcuts; pub mod state; diff --git a/src/app/actions.rs b/src/app/actions.rs new file mode 100644 index 0000000..c644eb1 --- /dev/null +++ b/src/app/actions.rs @@ -0,0 +1,30 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ShortcutAction { + NewFile, + OpenFile, + SaveFile, + SaveAsFile, + NewTab, + CloseTab, + ToggleLineNumbers, + ToggleLineSide, + ToggleWordWrap, + ToggleAutoHideToolbar, + ToggleBottomBar, + ToggleFind, + ToggleReplace, + ToggleMarkdown, + FocusFind, + NextTab, + PrevTab, + PageUp, + PageDown, + ZoomIn, + ZoomOut, + GlobalZoomIn, + GlobalZoomOut, + ResetZoom, + Escape, + Preferences, + ToggleVimMode, +} diff --git a/src/app/config.rs b/src/app/config.rs index 4de8736..6a7f4bd 100644 --- a/src/app/config.rs +++ b/src/app/config.rs @@ -4,72 +4,36 @@ use std::path::PathBuf; use super::theme::Theme; #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(default)] pub struct Config { - #[serde(default = "default_state_cache")] pub state_cache: bool, - #[serde(default = "default_auto_hide_toolbar")] pub auto_hide_toolbar: bool, - #[serde(default = "default_hide_tab_bar")] pub hide_tab_bar: bool, - #[serde(default = "default_show_line_numbers")] + pub hide_bottom_bar: bool, pub show_line_numbers: bool, - #[serde(default = "default_word_wrap")] pub word_wrap: bool, - #[serde(default = "Theme::default")] pub theme: Theme, - #[serde(default = "default_line_side")] pub line_side: bool, - #[serde(default = "default_font_family")] pub font_family: String, - #[serde(default = "default_font_size")] pub font_size: f32, - #[serde(default = "default_syntax_highlighting")] pub syntax_highlighting: bool, // pub vim_mode: bool, } -fn default_state_cache() -> bool { - false -} - -fn default_auto_hide_toolbar() -> bool { - false -} -fn default_hide_tab_bar() -> bool { - true -} -fn default_show_line_numbers() -> bool { - false -} -fn default_word_wrap() -> bool { - true -} -fn default_line_side() -> bool { - false -} -fn default_font_family() -> String { - "Proportional".to_string() -} -fn default_font_size() -> f32 { - 14.0 -} -fn default_syntax_highlighting() -> bool { - false -} - impl Default for Config { fn default() -> Self { Self { - state_cache: default_state_cache(), - auto_hide_toolbar: default_auto_hide_toolbar(), - hide_tab_bar: default_hide_tab_bar(), - show_line_numbers: default_show_line_numbers(), - word_wrap: default_word_wrap(), + state_cache: false, + auto_hide_toolbar: false, + hide_tab_bar: true, + hide_bottom_bar: false, + show_line_numbers: false, + word_wrap: true, theme: Theme::default(), - line_side: default_line_side(), - font_family: default_font_family(), - font_size: default_font_size(), - syntax_highlighting: default_syntax_highlighting(), + line_side: false, + font_family: "Proportional".to_string(), + font_size: 14.0, + syntax_highlighting: false, // vim_mode: false, } } diff --git a/src/app/shortcuts.rs b/src/app/shortcuts.rs index d327a85..ec9c7be 100644 --- a/src/app/shortcuts.rs +++ b/src/app/shortcuts.rs @@ -1,38 +1,7 @@ +use crate::app::actions::ShortcutAction; use crate::app::state::TextEditor; -use crate::io; use eframe::egui; -#[derive(Debug, Clone, Copy)] -enum ShortcutAction { - NewFile, - OpenFile, - SaveFile, - SaveAsFile, - NewTab, - CloseTab, - ToggleLineNumbers, - ToggleLineSide, - ToggleWordWrap, - ToggleAutoHideToolbar, - ToggleBottomBar, - ToggleFind, - ToggleReplace, - ToggleMarkdown, - FocusFind, - NextTab, - PrevTab, - PageUp, - PageDown, - ZoomIn, - ZoomOut, - GlobalZoomIn, - GlobalZoomOut, - ResetZoom, - Escape, - Preferences, - ToggleVimMode, -} - type ShortcutDefinition = (egui::Modifiers, egui::Key, ShortcutAction); fn get_shortcuts() -> Vec { @@ -168,154 +137,7 @@ fn get_shortcuts() -> Vec { } fn execute_action(action: ShortcutAction, editor: &mut TextEditor) -> bool { - match action { - ShortcutAction::NewFile => { - io::new_file(editor); - false - } - ShortcutAction::OpenFile => { - io::open_file(editor); - false - } - ShortcutAction::SaveFile => { - io::save_file(editor); - false - } - ShortcutAction::SaveAsFile => { - io::save_as_file(editor); - false - } - ShortcutAction::NewTab => { - editor.add_new_tab(); - false - } - ShortcutAction::CloseTab => { - if editor.tabs.len() > 1 { - if let Some(current_tab) = editor.get_active_tab() { - if current_tab.is_modified { - editor.pending_unsaved_action = Some( - super::state::UnsavedAction::CloseTab(editor.active_tab_index), - ); - } else { - editor.close_tab(editor.active_tab_index); - } - } - } - false - } - ShortcutAction::ToggleLineNumbers => { - editor.show_line_numbers = !editor.show_line_numbers; - editor.save_config(); - false - } - ShortcutAction::ToggleLineSide => { - editor.line_side = !editor.line_side; - editor.save_config(); - false - } - ShortcutAction::ToggleWordWrap => { - editor.word_wrap = !editor.word_wrap; - editor.save_config(); - false - } - ShortcutAction::ToggleAutoHideToolbar => { - editor.auto_hide_toolbar = !editor.auto_hide_toolbar; - editor.save_config(); - false - } - ShortcutAction::ToggleBottomBar => { - editor.hide_bottom_bar = !editor.hide_bottom_bar; - editor.save_config(); - false - } - ShortcutAction::NextTab => { - let next_tab_index = editor.active_tab_index + 1; - if next_tab_index < editor.tabs.len() { - editor.switch_to_tab(next_tab_index); - } else { - editor.switch_to_tab(0); - } - false - } - ShortcutAction::PrevTab => { - if editor.active_tab_index == 0 { - editor.switch_to_tab(editor.tabs.len() - 1); - } else { - editor.switch_to_tab(editor.active_tab_index - 1); - } - false - } - ShortcutAction::PageUp | ShortcutAction::PageDown => { - false - } - ShortcutAction::ZoomIn => { - editor.font_size += 1.0; - true - } - ShortcutAction::ZoomOut => { - editor.font_size -= 1.0; - true - } - ShortcutAction::GlobalZoomIn => { - editor.zoom_factor += 0.1; - false - } - ShortcutAction::GlobalZoomOut => { - editor.zoom_factor -= 0.1; - if editor.zoom_factor < 0.1 { - editor.zoom_factor = 0.1; - } - false - } - ShortcutAction::ResetZoom => { - editor.zoom_factor = 1.0; - false - } - ShortcutAction::ToggleVimMode => { - // editor.vim_mode = !editor.vim_mode; - false - } - ShortcutAction::Escape => { - editor.show_about = false; - editor.show_shortcuts = false; - if editor.show_find { - editor.should_select_current_match = true; - } - editor.show_find = false; - editor.show_preferences = false; - editor.pending_unsaved_action = None; - false - } - ShortcutAction::ToggleFind => { - editor.show_find = !editor.show_find; - if editor.show_find && !editor.find_query.is_empty() { - editor.update_find_matches(); - } - false - } - ShortcutAction::ToggleReplace => { - editor.show_find = !editor.show_find; - editor.show_replace_section = true; - if editor.show_find && !editor.find_query.is_empty() { - editor.update_find_matches(); - } - false - } - ShortcutAction::FocusFind => { - if editor.show_find { - editor.focus_find = true; - } - false - } - ShortcutAction::Preferences => { - editor.show_preferences = !editor.show_preferences; - false - } - ShortcutAction::ToggleMarkdown => { - editor.show_markdown = !editor.show_markdown; - false - } - } + editor.perform_action(action) } pub fn handle(editor: &mut TextEditor, ctx: &egui::Context) { diff --git a/src/app/state/config.rs b/src/app/state/config.rs index fad9e93..882ae18 100644 --- a/src/app/state/config.rs +++ b/src/app/state/config.rs @@ -12,6 +12,7 @@ impl TextEditor { word_wrap: config.word_wrap, auto_hide_toolbar: config.auto_hide_toolbar, hide_tab_bar: config.hide_tab_bar, + hide_bottom_bar: config.hide_bottom_bar, theme: config.theme, line_side: config.line_side, font_family: config.font_family, @@ -89,6 +90,7 @@ impl TextEditor { auto_hide_toolbar: self.auto_hide_toolbar, show_line_numbers: self.show_line_numbers, hide_tab_bar: self.hide_tab_bar, + hide_bottom_bar: self.hide_bottom_bar, word_wrap: self.word_wrap, theme: self.theme, line_side: self.line_side, diff --git a/src/app/state/editor.rs b/src/app/state/editor.rs index 7bc231b..079061c 100644 --- a/src/app/state/editor.rs +++ b/src/app/state/editor.rs @@ -1,5 +1,7 @@ +use crate::app::actions::ShortcutAction; use crate::app::tab::Tab; use crate::app::theme::Theme; +use crate::io; use eframe::egui; use std::sync::{Arc, Mutex}; use std::thread; @@ -78,3 +80,153 @@ pub struct TextEditor { pub(crate) should_select_current_match: bool, pub(crate) previous_cursor_position: Option, } + +impl TextEditor { + pub fn perform_action(&mut self, action: ShortcutAction) -> bool { + match action { + ShortcutAction::NewFile => { + io::new_file(self); + false + } + ShortcutAction::OpenFile => { + io::open_file(self); + false + } + ShortcutAction::SaveFile => { + io::save_file(self); + false + } + ShortcutAction::SaveAsFile => { + io::save_as_file(self); + false + } + ShortcutAction::NewTab => { + self.add_new_tab(); + false + } + ShortcutAction::CloseTab => { + if self.tabs.len() > 1 { + if let Some(current_tab) = self.get_active_tab() { + if current_tab.is_modified { + self.pending_unsaved_action = + Some(UnsavedAction::CloseTab(self.active_tab_index)); + } else { + self.close_tab(self.active_tab_index); + } + } + } + false + } + ShortcutAction::ToggleLineNumbers => { + self.show_line_numbers = !self.show_line_numbers; + self.save_config(); + false + } + ShortcutAction::ToggleLineSide => { + self.line_side = !self.line_side; + self.save_config(); + false + } + ShortcutAction::ToggleWordWrap => { + self.word_wrap = !self.word_wrap; + self.save_config(); + false + } + ShortcutAction::ToggleAutoHideToolbar => { + self.auto_hide_toolbar = !self.auto_hide_toolbar; + self.save_config(); + false + } + ShortcutAction::ToggleBottomBar => { + self.hide_bottom_bar = !self.hide_bottom_bar; + self.save_config(); + false + } + ShortcutAction::NextTab => { + let next_tab_index = self.active_tab_index + 1; + if next_tab_index < self.tabs.len() { + self.switch_to_tab(next_tab_index); + } else { + self.switch_to_tab(0); + } + false + } + ShortcutAction::PrevTab => { + if self.active_tab_index == 0 { + self.switch_to_tab(self.tabs.len() - 1); + } else { + self.switch_to_tab(self.active_tab_index - 1); + } + false + } + ShortcutAction::PageUp | ShortcutAction::PageDown => false, + ShortcutAction::ZoomIn => { + self.font_size += 1.0; + true + } + ShortcutAction::ZoomOut => { + self.font_size -= 1.0; + true + } + ShortcutAction::GlobalZoomIn => { + self.zoom_factor += 0.1; + false + } + ShortcutAction::GlobalZoomOut => { + self.zoom_factor -= 0.1; + if self.zoom_factor < 0.1 { + self.zoom_factor = 0.1; + } + false + } + ShortcutAction::ResetZoom => { + self.zoom_factor = 1.0; + false + } + ShortcutAction::ToggleVimMode => { + // self.vim_mode = !self.vim_mode; + false + } + ShortcutAction::Escape => { + self.show_about = false; + self.show_shortcuts = false; + if self.show_find { + self.should_select_current_match = true; + } + self.show_find = false; + self.show_preferences = false; + self.pending_unsaved_action = None; + false + } + ShortcutAction::ToggleFind => { + self.show_find = !self.show_find; + if self.show_find && !self.find_query.is_empty() { + self.update_find_matches(); + } + false + } + ShortcutAction::ToggleReplace => { + self.show_find = !self.show_find; + self.show_replace_section = true; + if self.show_find && !self.find_query.is_empty() { + self.update_find_matches(); + } + false + } + ShortcutAction::FocusFind => { + if self.show_find { + self.focus_find = true; + } + false + } + ShortcutAction::Preferences => { + self.show_preferences = !self.show_preferences; + false + } + ShortcutAction::ToggleMarkdown => { + self.show_markdown = !self.show_markdown; + false + } + } + } +} diff --git a/src/app/state/ui.rs b/src/app/state/ui.rs index d3c3b75..199d28e 100644 --- a/src/app/state/ui.rs +++ b/src/app/state/ui.rs @@ -65,12 +65,12 @@ impl TextEditor { let processing_result = self.get_text_processing_result(); let line_count = processing_result.line_count; - let font_id = self.get_font_id(); + let monospace_font_id = egui::FontId::monospace(self.font_size); let line_count_digits = line_count.to_string().len(); let sample_text = "9".repeat(line_count_digits); let base_line_number_width = ui.fonts_mut(|fonts| { fonts - .layout(sample_text, font_id, egui::Color32::WHITE, f32::INFINITY) + .layout_no_wrap(sample_text, monospace_font_id, egui::Color32::WHITE) .size() .x }); diff --git a/src/ui/central_panel.rs b/src/ui/central_panel.rs index 54d9ea6..4c8effc 100644 --- a/src/ui/central_panel.rs +++ b/src/ui/central_panel.rs @@ -26,6 +26,7 @@ pub(crate) fn central_panel(app: &mut TextEditor, ctx: &egui::Context) { let word_wrap = app.word_wrap; let line_side = app.line_side; let font_size = app.font_size; + let monospace = if app.font_family.as_str() == "Monospace" { true } else { false }; let font_id = app.get_font_id(); let show_markdown = app.show_markdown; let is_markdown_file = is_markdown_tab(app); @@ -160,6 +161,7 @@ pub(crate) fn central_panel(app: &mut TextEditor, ctx: &egui::Context) { word_wrap, line_side, font_size, + monospace, ); }; diff --git a/src/ui/central_panel/editor.rs b/src/ui/central_panel/editor.rs index 716dcab..1a3e4ea 100644 --- a/src/ui/central_panel/editor.rs +++ b/src/ui/central_panel/editor.rs @@ -101,7 +101,7 @@ pub(super) fn editor_view_ui(ui: &mut egui::Ui, app: &mut TextEditor) -> egui::R if syntax_highlighting_enabled && language != "txt" { for section in &mut layout_job.sections { - section.format.font_id = font_id.clone(); + section.format.font_id = font_id.to_owned(); } } diff --git a/src/ui/central_panel/line_numbers.rs b/src/ui/central_panel/line_numbers.rs index 0f1ab05..208a0b3 100644 --- a/src/ui/central_panel/line_numbers.rs +++ b/src/ui/central_panel/line_numbers.rs @@ -57,12 +57,23 @@ pub(super) fn render_line_numbers( word_wrap: bool, line_side: bool, font_size: f32, + monospace: bool, ) { ui.vertical(|ui| { ui.disable(); ui.set_width(line_number_width); - ui.spacing_mut().item_spacing.y = 0.0; - ui.add_space(1.0); // Text Editor default top margin + + let spacing_adjustment = if monospace { + 0.0 + } else { + match font_size { + 10.0 | 16.0 | 22.0 | 23.0 | 28.0 | 29.0 | 30.0 => -1.0, + _ => 0.0, + } + }; + + ui.spacing_mut().item_spacing.y = spacing_adjustment; + ui.add_space(if monospace { 1.0 } else { 2.0 }); // Text Editor default top margin let text_color = ui.visuals().weak_text_color(); let bg_color = ui.visuals().extreme_bg_color; diff --git a/src/ui/menu_bar.rs b/src/ui/menu_bar.rs index ba875bf..3e087a1 100644 --- a/src/ui/menu_bar.rs +++ b/src/ui/menu_bar.rs @@ -1,4 +1,5 @@ -use crate::{app::TextEditor, io}; +use crate::app::TextEditor; +use crate::app::actions::ShortcutAction; use eframe::egui::{self, Frame}; use egui::UiKind; @@ -48,25 +49,25 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) { ui.menu_button("File", |ui| { app.menu_interaction_active = true; if ui.button("New").clicked() { - io::new_file(app); + app.perform_action(ShortcutAction::NewFile); ui.close_kind(UiKind::Menu); } if ui.button("Open...").clicked() { - io::open_file(app); + app.perform_action(ShortcutAction::OpenFile); ui.close_kind(UiKind::Menu); } ui.separator(); if ui.button("Save").clicked() { - io::save_file(app); + app.perform_action(ShortcutAction::SaveFile); ui.close_kind(UiKind::Menu); } if ui.button("Save As...").clicked() { - io::save_as_file(app); + app.perform_action(ShortcutAction::SaveAsFile); ui.close_kind(UiKind::Menu); } ui.separator(); if ui.button("Preferences").clicked() { - app.show_preferences = true; + app.perform_action(ShortcutAction::Preferences); ui.close_kind(UiKind::Menu); } if ui.button("Exit").clicked() { @@ -136,7 +137,8 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) { .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|| "untitled".to_string()); let text_edit_id = egui::Id::new("main_text_editor").with(&id_source); - if let Some(state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) { + if let Some(state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) + { let current_state = ( state.cursor.char_range().unwrap_or_default(), active_tab.content.to_string(), @@ -149,7 +151,10 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) { false }; - if ui.add_enabled(can_undo, egui::Button::new("Undo")).clicked() { + if ui + .add_enabled(can_undo, egui::Button::new("Undo")) + .clicked() + { if let Some(active_tab) = app.get_active_tab_mut() { let id_source = active_tab .file_path @@ -196,7 +201,8 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) { .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|| "untitled".to_string()); let text_edit_id = egui::Id::new("main_text_editor").with(&id_source); - if let Some(state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) { + if let Some(state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) + { let current_state = ( state.cursor.char_range().unwrap_or_default(), active_tab.content.to_string(), @@ -209,7 +215,10 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) { false }; - if ui.add_enabled(can_redo, egui::Button::new("Redo")).clicked() { + if ui + .add_enabled(can_redo, egui::Button::new("Redo")) + .clicked() + { if let Some(active_tab) = app.get_active_tab_mut() { let id_source = active_tab .file_path @@ -281,7 +290,10 @@ pub(crate) fn menu_bar(app: &mut TextEditor, ctx: &egui::Context) { app.save_config(); ui.close_kind(UiKind::Menu); } - if ui.checkbox(&mut app.hide_bottom_bar, "Hide Bottom Bar").clicked() { + if ui + .checkbox(&mut app.hide_bottom_bar, "Hide Bottom Bar") + .clicked() + { app.save_config(); ui.close_kind(UiKind::Menu); } diff --git a/src/ui/preferences_window.rs b/src/ui/preferences_window.rs index f49b1b1..0bb9ac8 100644 --- a/src/ui/preferences_window.rs +++ b/src/ui/preferences_window.rs @@ -29,9 +29,6 @@ pub(crate) fn preferences_window(app: &mut TextEditor, ctx: &egui::Context) { }) .show(ctx, |ui| { ui.vertical_centered(|ui| { - ui.heading("Editor Settings"); - ui.add_space(MEDIUM); - ui.horizontal(|ui| { ui.vertical(|ui| { if ui @@ -90,14 +87,12 @@ pub(crate) fn preferences_window(app: &mut TextEditor, ctx: &egui::Context) { ui.add_space(SMALL); ui.separator(); - ui.add_space(LARGE); - ui.heading("Font Settings"); - ui.add_space(MEDIUM); + ui.add_space(SMALL); ui.horizontal(|ui| { ui.vertical(|ui| { ui.label("Font Family:"); - ui.add_space(SMALL); + ui.add_space(MEDIUM); ui.label("Font Size:"); }); @@ -152,7 +147,7 @@ pub(crate) fn preferences_window(app: &mut TextEditor, ctx: &egui::Context) { response.request_focus(); } - ui.label("px"); + ui.label("pt"); if response.lost_focus() { if let Ok(new_size) = font_size_text.parse::() { diff --git a/src/ui/shortcuts_window.rs b/src/ui/shortcuts_window.rs index e5ae7f2..eda6ce4 100644 --- a/src/ui/shortcuts_window.rs +++ b/src/ui/shortcuts_window.rs @@ -54,7 +54,7 @@ fn render_shortcuts_content(ui: &mut egui::Ui) { // .size(14.0) // ); ui.add_space(VLARGE); - ui.separator(); + //ui.separator(); }); } @@ -98,7 +98,8 @@ pub(crate) fn shortcuts_window(app: &mut TextEditor, ctx: &egui::Context) { ); ui.vertical_centered(|ui| { - ui.add_space(MEDIUM); + //ui.add_space(MEDIUM); + ui.separator(); let visuals = ui.visuals(); let close_button = egui::Button::new("Close") .fill(visuals.widgets.inactive.bg_fill)