From 815259f42aa25749d9f7d0da247c758a5603857f Mon Sep 17 00:00:00 2001 From: Ethan Roseman Date: Fri, 18 Oct 2024 17:28:19 +0900 Subject: [PATCH] More settings / theme fixes --- .gitignore | 3 +- app/src/app.rs | 71 ++++--- app/src/file_view.rs | 4 +- app/src/settings/mod.rs | 25 ++- app/src/settings/panels.rs | 49 ----- app/src/settings/theme.rs | 176 +++++++++--------- app/src/settings/ui.rs | 63 +++++++ app/src/tools/map_file.rs | 2 +- app/src/workspace.rs | 4 +- .../src}/byte_grouping.rs | 29 +-- hex_view/src/hex_view.rs | 13 +- hex_view/src/lib.rs | 2 + 12 files changed, 240 insertions(+), 201 deletions(-) delete mode 100644 app/src/settings/panels.rs create mode 100644 app/src/settings/ui.rs rename {app/src/settings => hex_view/src}/byte_grouping.rs (71%) diff --git a/.gitignore b/.gitignore index 1f2fd1b..a38da03 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ # will have compiled files and executables debug/ target/ -.idea/ # These are backup files generated by rustfmt **/*.rs.bk @@ -15,3 +14,5 @@ target/ # Test config bdiff.json + +/.idea/ \ No newline at end of file diff --git a/app/src/app.rs b/app/src/app.rs index 36d56bc..73c2127 100644 --- a/app/src/app.rs +++ b/app/src/app.rs @@ -1,15 +1,15 @@ -use crate::{diff_state::DiffState, settings}; +use crate::diff_state::DiffState; use std::{ path::{Path, PathBuf}, sync::atomic::Ordering, }; -use crate::settings::panels::settings_management_buttons; +use crate::settings::ui::{byte_grouping_slider, show_settings_management_buttons}; use crate::{ bin_file::BinFile, file_view::FileView, settings::{read_json_settings, show_theme_settings, write_json_settings, Settings}, - workspace::{read_json_config, write_json_config, Workspace, WorkspaceFile}, + workspace::{read_workspace_json, write_workspace_json, Workspace, WorkspaceFile}, }; use anyhow::Error; use bdiff_hex_view::{CursorState, HexViewSelection, HexViewSelectionSide, HexViewSelectionState}; @@ -31,18 +31,6 @@ struct OverwriteModal { open: bool, } -struct Options { - mirror_selection: bool, -} - -impl Default for Options { - fn default() -> Self { - Self { - mirror_selection: true, - } - } -} - #[derive(Default)] pub struct BdiffApp { next_hv_id: usize, @@ -51,11 +39,10 @@ pub struct BdiffApp { goto_modal: GotoModal, overwrite_modal: OverwriteModal, scroll_overflow: f32, - options: Options, global_selection: HexViewSelection, // the selection that all hex views will mirror selecting_hv: Option, last_selected_hv: Option, - bottom_bar_open: bool, + theme_editor_open: bool, settings: Settings, workspace: Workspace, started_with_arguments: bool, @@ -81,6 +68,8 @@ impl BdiffApp { let started_with_arguments = !paths.is_empty(); + let hv_style = settings.theme.hex_view_style.clone(); + let mut ret = Self { next_hv_id: 0, file_views: hex_views, @@ -104,11 +93,12 @@ impl BdiffApp { files: file_configs, } } else if config_path.exists() { - read_json_config(config_path).unwrap() + read_workspace_json(config_path).unwrap() } else { Workspace::default() }; + for file in config.files.iter() { match ret.open_file(&file.path) { Ok(fv) => { @@ -116,6 +106,7 @@ impl BdiffApp { fv.st.load_file(map); } fv.file.endianness = file.endianness; // TODO hook up endianness saving + fv.hv.set_style(hv_style.clone()); } Err(e) => { log::error!("Failed to open file: {}", e); @@ -432,6 +423,19 @@ impl eframe::App for BdiffApp { } } + // Theme editor + let prev_theme = self.settings.theme.clone(); + if self.theme_editor_open { + if show_theme_settings(ctx, &mut self.settings.theme) { + self.theme_editor_open = false; + } + if self.settings.theme != prev_theme { + for fv in self.file_views.iter_mut() { + fv.hv.set_style(self.settings.theme.hex_view_style.clone()); + } + } + } + // Standard HexView input if !(overwrite_modal.is_open() || goto_modal.is_open()) { self.handle_hex_view_input(ctx); @@ -488,7 +492,7 @@ impl eframe::App for BdiffApp { if self.started_with_arguments { self.overwrite_modal.open = true; } else { - write_json_config("bdiff.json", &self.workspace) + write_workspace_json("bdiff.json", &self.workspace) .expect("Failed to write config"); }; ui.close_menu(); @@ -521,7 +525,7 @@ impl eframe::App for BdiffApp { ui.menu_button("Options", |ui| { let diff_checkbox = Checkbox::new(&mut self.settings.diff_enabled, "Display diff"); let mirror_selection_checkbox = Checkbox::new( - &mut self.options.mirror_selection, + &mut self.settings.mirror_selection, "Mirror selection across files", ); @@ -539,25 +543,34 @@ impl eframe::App for BdiffApp { ui.separator(); ui.label("Interface"); - settings::byte_grouping_slider(ui, &mut self.settings.byte_grouping); + byte_grouping_slider(ui, &mut self.settings.byte_grouping); ui.add(Checkbox::new( - &mut self.bottom_bar_open, + &mut self.settings.show_quick_access_bar, "Show Quick Access bar", )); ui.separator(); - show_theme_settings(ui, &mut self.settings); - settings_management_buttons(ui, &mut self.settings); + if ui.button("Theme").clicked() { + self.theme_editor_open = !self.theme_editor_open; + } + + let prev_hv_style = self.settings.theme.hex_view_style.clone(); + show_settings_management_buttons(ui, &mut self.settings); + if self.settings.theme.hex_view_style != prev_hv_style { + for fv in self.file_views.iter_mut() { + fv.hv.set_style(self.settings.theme.hex_view_style.clone()); + } + } }); }) }); // Quick Access bar - if self.bottom_bar_open { + if self.settings.show_quick_access_bar { egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| { ui.with_layout(Layout::right_to_left(Align::LEFT), |ui| { - settings::byte_grouping_slider(ui, &mut self.settings.byte_grouping); + byte_grouping_slider(ui, &mut self.settings.byte_grouping); }); }); } @@ -574,9 +587,9 @@ impl eframe::App for BdiffApp { }; fv.show( + ctx, &self.settings, &self.diff_state, - ctx, cursor_state, can_selection_change, self.global_view_pos, @@ -622,7 +635,7 @@ impl eframe::App for BdiffApp { } } - if self.options.mirror_selection { + if self.settings.mirror_selection { for fv in self.file_views.iter_mut() { if fv.hv.selection != self.global_selection { fv.hv.selection = self.global_selection.clone(); @@ -705,7 +718,7 @@ impl BdiffApp { modal.buttons(ui, |ui| { if ui.button("Overwrite").clicked() { - write_json_config("bdiff.json", &self.workspace).unwrap(); + write_workspace_json("bdiff.json", &self.workspace).unwrap(); self.overwrite_modal.open = false; } if ui.button("Cancel").clicked() { diff --git a/app/src/file_view.rs b/app/src/file_view.rs index f37b41f..d71a904 100644 --- a/app/src/file_view.rs +++ b/app/src/file_view.rs @@ -83,9 +83,9 @@ impl FileView { pub fn show( &mut self, + ctx: &egui::Context, settings: &Settings, diff_state: &DiffState, - ctx: &egui::Context, cursor_state: CursorState, can_selection_change: bool, global_view_pos: usize, @@ -186,7 +186,7 @@ impl FileView { self.cur_pos, cursor_state, can_selection_change, - settings.byte_grouping.into(), + settings.byte_grouping, ); }); diff --git a/app/src/settings/mod.rs b/app/src/settings/mod.rs index 226efa7..70920f4 100644 --- a/app/src/settings/mod.rs +++ b/app/src/settings/mod.rs @@ -1,5 +1,6 @@ -use crate::settings::{byte_grouping::ByteGrouping, theme::ThemeSettings}; +use crate::settings::theme::ThemeSettings; use anyhow::{Context, Error}; +use bdiff_hex_view::byte_grouping::ByteGrouping; use serde::{Deserialize, Serialize}; use std::{ fs::{File, OpenOptions}, @@ -7,20 +8,32 @@ use std::{ path::PathBuf, }; -mod byte_grouping; -pub mod panels; +pub mod ui; pub mod theme; -pub use byte_grouping::byte_grouping_slider; pub use theme::show_theme_settings; -#[derive(Deserialize, Serialize, Default, PartialEq, PartialOrd, Clone)] +#[derive(Deserialize, Serialize, PartialEq, PartialOrd, Clone)] pub struct Settings { - pub byte_grouping: ByteGrouping, + pub mirror_selection: bool, pub diff_enabled: bool, + pub byte_grouping: ByteGrouping, + pub show_quick_access_bar: bool, pub theme: ThemeSettings, } +impl Default for Settings { + fn default() -> Self { + Self { + mirror_selection: true, + diff_enabled: true, + byte_grouping: ByteGrouping::default(), + show_quick_access_bar: false, + theme: ThemeSettings::default(), + } + } +} + impl SettingsControl for Settings { fn restore_defaults(&mut self) { *self = Settings::default(); diff --git a/app/src/settings/panels.rs b/app/src/settings/panels.rs deleted file mode 100644 index 107add4..0000000 --- a/app/src/settings/panels.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Common panels and controls found in the settings window. - -use crate::settings::SettingsControl; -use bdiff_hex_view::theme::Color; -use eframe::egui::{Align, Direction, Layout, RichText, Ui, Vec2}; - -pub fn settings_management_buttons(ui: &mut Ui, settings: &mut impl SettingsControl) { - ui.separator(); - ui.horizontal(|ui| { - if ui - .button(RichText::new(format!( - "{} Restore Defaults", - egui_phosphor::regular::ARROW_COUNTER_CLOCKWISE - ))) - .clicked() - { - settings.restore_defaults(); - } - if ui - .button(RichText::new(format!( - "{} Reload", - egui_phosphor::regular::FLOPPY_DISK - ))) - .clicked() - { - settings.reload(); - } - ui.separator(); - - if ui.button("Save").clicked() { - settings.save(); - } - }); -} - -pub fn color_selection(ui: &mut Ui, label: &str, color: &mut Color) { - ui.allocate_ui_with_layout( - Vec2::new(200.0, 20.0), - Layout::centered_and_justified(Direction::LeftToRight).with_main_justify(true), - |ui| { - ui.horizontal(|ui| { - ui.label(label); - ui.with_layout(Layout::right_to_left(Align::RIGHT), |ui| { - ui.color_edit_button_srgba_premultiplied(color.as_bytes_mut()); - }); - }); - }, - ); -} diff --git a/app/src/settings/theme.rs b/app/src/settings/theme.rs index 5089c58..9d3e715 100644 --- a/app/src/settings/theme.rs +++ b/app/src/settings/theme.rs @@ -1,12 +1,12 @@ -use crate::settings::panels::color_selection; -use crate::settings::Settings; +use crate::settings::ui::color_selection; use bdiff_hex_view::HexViewStyle; -use eframe::egui::{self, Ui}; +use eframe::egui::{self}; use eframe::egui::{Align, Layout}; use serde::{Deserialize, Serialize}; use std::fmt::Display; #[derive(Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[serde(rename_all = "lowercase")] pub enum VisualTheme { Decompme, Dark, @@ -16,7 +16,7 @@ pub enum VisualTheme { impl Display for VisualTheme { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let str = match self { - Self::Decompme => "Decomp.me", + Self::Decompme => "decomp.me", Self::Dark => "Dark", Self::Light => "Light", } @@ -39,92 +39,102 @@ impl Default for ThemeSettings { } } } -pub fn show_theme_settings(ui: &mut Ui, settings: &mut Settings) { - ui.label("Theme"); +pub fn show_theme_settings(ctx: &egui::Context, settings: &mut ThemeSettings) -> bool { + let mut closed = false; - // Font Colors - egui::Frame::group(ui.style()).show(ui, |ui| { - ui.with_layout(Layout::top_down(Align::LEFT), |ui| { - egui::CollapsingHeader::new("Offset Colors") - .default_open(true) - .show(ui, |ui| { - color_selection( - ui, - "Offset text color", - &mut settings.theme.hex_view_style.offset_text_color, - ); + egui::Window::new("theme").title_bar(false).show(ctx, |ui| { + ui.horizontal(|ui| { + ui.label("Theme"); + if ui.button("X").on_hover_text("Close").clicked() { + closed = true; + } + }); + + // Font Colors + egui::Frame::group(ui.style()).show(ui, |ui| { + ui.with_layout(Layout::top_down(Align::LEFT), |ui| { + egui::CollapsingHeader::new("Offset Colors") + .default_open(true) + .show(ui, |ui| { + color_selection( + ui, + "Offset text color", + &mut settings.hex_view_style.offset_text_color, + ); - color_selection( - ui, - "Leading zero color", - &mut settings - .theme - .hex_view_style - .offset_leading_zero_color, - ); - }); + color_selection( + ui, + "Leading zero color", + &mut settings + .hex_view_style + .offset_leading_zero_color, + ); + }); - egui::CollapsingHeader::new("Hex Area Colors") - .default_open(true) - .show(ui, |ui| { - color_selection( - ui, - "Selection color", - &mut settings.theme.hex_view_style.selection_color, - ); - color_selection( - ui, - "Diff color", - &mut settings.theme.hex_view_style.diff_color, - ); - color_selection( - ui, - "Null color", - &mut settings.theme.hex_view_style.hex_null_color, - ); - color_selection( - ui, - "Other color", - &mut settings.theme.hex_view_style.other_hex_color, - ); - }); + egui::CollapsingHeader::new("Hex Area Colors") + .default_open(true) + .show(ui, |ui| { + color_selection( + ui, + "Selection color", + &mut settings.hex_view_style.selection_color, + ); + color_selection( + ui, + "Diff color", + &mut settings.hex_view_style.diff_color, + ); + color_selection( + ui, + "Null color", + &mut settings.hex_view_style.hex_null_color, + ); + color_selection( + ui, + "Other color", + &mut settings.hex_view_style.other_hex_color, + ); + }); - egui::CollapsingHeader::new("Ascii Area Colors") - .default_open(true) - .show(ui, |ui| { - color_selection( - ui, - "Null color", - &mut settings.theme.hex_view_style.ascii_null_color, - ); + egui::CollapsingHeader::new("Ascii Area Colors") + .default_open(true) + .show(ui, |ui| { + color_selection( + ui, + "Null color", + &mut settings.hex_view_style.ascii_null_color, + ); - color_selection( - ui, - "Ascii color", - &mut settings.theme.hex_view_style.ascii_color, - ); + color_selection( + ui, + "Ascii color", + &mut settings.hex_view_style.ascii_color, + ); - color_selection( - ui, - "Other color", - &mut settings.theme.hex_view_style.other_ascii_color, - ); - }); + color_selection( + ui, + "Other color", + &mut settings.hex_view_style.other_ascii_color, + ); + }); + }); }); + + // /// Visual Theme + // egui::Frame::group(ui.style()).show(ui, |ui| { + // ui.vertical(|ui| { + // ui.add(egui::Label::new(RichText::new("Visual Theme").heading())); + // + // for theme in &[VisualTheme::Decompme, VisualTheme::UltraDark, VisualTheme::Light] { + // ui.radio_value( + // &mut settings.theme_settings.active_theme, + // theme.clone(), + // theme.to_string(), + // ); + // } + // }); + // }); }); - // /// Visual Theme - // egui::Frame::group(ui.style()).show(ui, |ui| { - // ui.vertical(|ui| { - // ui.add(egui::Label::new(RichText::new("Visual Theme").heading())); - // - // for theme in &[VisualTheme::Decompme, VisualTheme::UltraDark, VisualTheme::Light] { - // ui.radio_value( - // &mut settings.theme_settings.active_theme, - // theme.clone(), - // theme.to_string(), - // ); - // } - // }); - // }); + closed } diff --git a/app/src/settings/ui.rs b/app/src/settings/ui.rs new file mode 100644 index 0000000..bcc6bd0 --- /dev/null +++ b/app/src/settings/ui.rs @@ -0,0 +1,63 @@ +//! Common panels and controls found in the settings window. + +use crate::settings::SettingsControl; +use bdiff_hex_view::byte_grouping::ByteGrouping; +use bdiff_hex_view::theme::Color; +use eframe::egui; +use eframe::egui::{Align, Layout, RichText, Ui}; + +pub fn show_settings_management_buttons(ui: &mut Ui, settings: &mut impl SettingsControl) { + ui.separator(); + ui.horizontal(|ui| { + if ui + .button(RichText::new(format!( + "{} Restore Defaults", + egui_phosphor::regular::ARROW_COUNTER_CLOCKWISE + ))) + .clicked() + { + settings.restore_defaults(); + } + if ui + .button(RichText::new(format!( + "{} Reload", + egui_phosphor::regular::RECYCLE + ))) + .clicked() + { + settings.reload(); + } + ui.separator(); + + if ui.button(RichText::new(format!( + "{} Save", + egui_phosphor::regular::FLOPPY_DISK + ))).clicked() { + settings.save(); + } + }); +} + +pub fn color_selection(ui: &mut Ui, label: &str, color: &mut Color) { + // ui.allocate_ui_with_layout( + // Vec2::new(200.0, 20.0), + // Layout::centered_and_justified(Direction::LeftToRight).with_main_justify(true), + // |ui| { + ui.horizontal(|ui| { + ui.label(label); + ui.with_layout(Layout::right_to_left(Align::RIGHT), |ui| { + ui.color_edit_button_srgba_premultiplied(color.as_bytes_mut()); + }); + }); + // }, + // ); +} + +pub fn byte_grouping_slider(ui: &mut Ui, byte_grouping: &mut ByteGrouping) { + ui.add( + egui::Slider::new(byte_grouping, ByteGrouping::One..=ByteGrouping::Sixteen) + .text("Byte Grouping") + .logarithmic(true) + .integer(), + ); +} diff --git a/app/src/tools/map_file.rs b/app/src/tools/map_file.rs index 50fc9fa..633cd18 100644 --- a/app/src/tools/map_file.rs +++ b/app/src/tools/map_file.rs @@ -45,7 +45,7 @@ impl MapFile { ..Default::default() }; - match create_watcher(path, ret.modified.clone()).map_err(anyhow::Error::new) { + match create_watcher(path, ret.modified.clone()).map_err(Error::new) { Ok(watcher) => { ret.watcher = Some(watcher); } diff --git a/app/src/workspace.rs b/app/src/workspace.rs index 66f2ae4..0adfa9f 100644 --- a/app/src/workspace.rs +++ b/app/src/workspace.rs @@ -33,13 +33,13 @@ pub struct Workspace { pub files: Vec, } -pub fn read_json_config(config_path: &Path) -> Result { +pub fn read_workspace_json(config_path: &Path) -> Result { let mut reader = File::open(config_path) .with_context(|| format!("Failed to open config file at {}", config_path.display()))?; Ok(serde_json::from_reader(&mut reader)?) } -pub fn write_json_config>(config_path: P, config: &Workspace) -> Result<(), Error> { +pub fn write_workspace_json>(config_path: P, config: &Workspace) -> Result<(), Error> { let path: PathBuf = config_path.into(); let mut oo = OpenOptions::new(); let mut writer = oo diff --git a/app/src/settings/byte_grouping.rs b/hex_view/src/byte_grouping.rs similarity index 71% rename from app/src/settings/byte_grouping.rs rename to hex_view/src/byte_grouping.rs index 810b90e..b881f40 100644 --- a/app/src/settings/byte_grouping.rs +++ b/hex_view/src/byte_grouping.rs @@ -1,9 +1,9 @@ -use eframe::egui; -use eframe::emath::Numeric; +use egui::emath::Numeric; use serde::{Deserialize, Serialize}; use std::fmt::Display; #[derive(Deserialize, Serialize, Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[serde(rename_all = "lowercase")] pub enum ByteGrouping { One, Two, @@ -13,18 +13,6 @@ pub enum ByteGrouping { Sixteen, } -impl ByteGrouping { - pub fn get_all_options() -> Vec { - vec![ - ByteGrouping::One, - ByteGrouping::Two, - ByteGrouping::Four, - ByteGrouping::Eight, - ByteGrouping::Sixteen, - ] - } -} - impl Display for ByteGrouping { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let str = match self { @@ -34,13 +22,13 @@ impl Display for ByteGrouping { Self::Eight => "Eight", Self::Sixteen => "Sixteen", } - .to_string(); + .to_string(); write!(f, "{}", str) } } impl Numeric for ByteGrouping { - const INTEGRAL: bool = false; + const INTEGRAL: bool = true; const MIN: Self = Self::One; const MAX: Self = Self::Sixteen; @@ -77,12 +65,3 @@ impl From for usize { } } } - -pub fn byte_grouping_slider(ui: &mut egui::Ui, byte_grouping: &mut ByteGrouping) { - ui.add( - egui::Slider::new(byte_grouping, ByteGrouping::One..=ByteGrouping::Sixteen) - .text("Byte Grouping") - .logarithmic(true) - .integer(), - ); -} diff --git a/hex_view/src/hex_view.rs b/hex_view/src/hex_view.rs index d9d8124..1aea576 100644 --- a/hex_view/src/hex_view.rs +++ b/hex_view/src/hex_view.rs @@ -1,5 +1,6 @@ use crate::{spacer::Spacer, theme::HexViewStyle}; +use crate::byte_grouping::ByteGrouping; use egui::{self, Color32, FontId, Sense, Separator}; #[derive(Clone, Copy, Debug, PartialEq)] @@ -104,6 +105,10 @@ impl HexView { } } + pub fn set_style(&mut self, style: HexViewStyle) { + self.style = style; + } + pub fn get_selected_bytes<'data>(&self, data: &'data [u8]) -> &'data [u8] { match self.selection.state { HexViewSelectionState::None => &[], @@ -158,8 +163,8 @@ impl HexView { fn show_hex( &mut self, - byte_grouping: usize, ui: &mut egui::Ui, + byte_grouping: ByteGrouping, start_pos: isize, start_diff_pos: usize, row: &[Option], @@ -169,6 +174,8 @@ impl HexView { ) { let mut i = 0; while i < self.bytes_per_row { + let byte_grouping: usize = byte_grouping.into(); + if i > 0 && (i % byte_grouping) == 0 { ui.add(Spacer::default().spacing_x(4.0)); } @@ -299,7 +306,7 @@ impl HexView { file_pos: usize, cursor_state: CursorState, can_selection_change: bool, - byte_grouping: usize, + byte_grouping: ByteGrouping, ) { let grid_rect = egui::Grid::new(format!("hex_grid{}", self.id)) .striped(true) @@ -327,8 +334,8 @@ impl HexView { ui.add(Spacer::default().spacing_x(8.0)); self.show_hex( - byte_grouping, ui, + byte_grouping, current_pos, global_pos + (r * self.bytes_per_row), row, diff --git a/hex_view/src/lib.rs b/hex_view/src/lib.rs index 48dfe37..f5f450a 100644 --- a/hex_view/src/lib.rs +++ b/hex_view/src/lib.rs @@ -1,5 +1,7 @@ mod hex_view; mod spacer; pub mod theme; +pub mod byte_grouping; + pub use hex_view::*; pub use theme::*;