From e0b7339b355a41693c53c83fb978c182f88f0ef4 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 23 Feb 2024 09:55:31 -0700 Subject: [PATCH] Move recents to state --- src/config.rs | 20 ++++++++--- src/main.rs | 92 +++++++++++++++++++++++++++++++++++++++++---------- src/menu.rs | 11 +++--- 3 files changed, 96 insertions(+), 27 deletions(-) diff --git a/src/config.rs b/src/config.rs index 2b20926..8756395 100644 --- a/src/config.rs +++ b/src/config.rs @@ -35,9 +35,6 @@ pub struct Config { pub font_size: u16, pub highlight_current_line: bool, pub line_numbers: bool, - //TODO: move to state? - pub recent_files: VecDeque, - pub recent_projects: VecDeque, pub syntax_theme_dark: String, pub syntax_theme_light: String, pub tab_width: u16, @@ -54,8 +51,6 @@ impl Default for Config { font_size: 14, highlight_current_line: true, line_numbers: true, - recent_files: VecDeque::new(), - recent_projects: VecDeque::new(), syntax_theme_dark: "COSMIC Dark".to_string(), syntax_theme_light: "COSMIC Light".to_string(), tab_width: 4, @@ -83,3 +78,18 @@ impl Config { } } } + +#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct ConfigState { + pub recent_files: VecDeque, + pub recent_projects: VecDeque, +} + +impl Default for ConfigState { + fn default() -> Self { + Self { + recent_files: VecDeque::new(), + recent_projects: VecDeque::new(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 96d329f..4246e09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,7 +31,7 @@ use std::{ }; use tokio::time; -use config::{AppTheme, Config, CONFIG_VERSION}; +use config::{AppTheme, Config, ConfigState, CONFIG_VERSION}; mod config; use git::{GitDiff, GitDiffLine, GitRepository, GitStatus, GitStatusKind}; @@ -146,6 +146,23 @@ fn main() -> Result<(), Box> { } }; + let (config_state_handler, config_state) = + match cosmic_config::Config::new_state(App::APP_ID, CONFIG_VERSION) { + Ok(config_state_handler) => { + let config_state = ConfigState::get_entry(&config_state_handler).unwrap_or_else( + |(errs, config_state)| { + log::info!("errors loading config_state: {:?}", errs); + config_state + }, + ); + (Some(config_state_handler), config_state) + } + Err(err) => { + log::error!("failed to create config_state handler: {}", err); + (None, ConfigState::default()) + } + }; + let mut settings = Settings::default(); settings = settings.theme(config.app_theme.theme()); settings = settings.size_limits(Limits::NONE.min_width(360.0).min_height(180.0)); @@ -153,6 +170,8 @@ fn main() -> Result<(), Box> { let flags = Flags { config_handler, config, + config_state_handler, + config_state, }; cosmic::app::run::(settings, flags)?; @@ -256,6 +275,8 @@ impl Action { pub struct Flags { config_handler: Option, config: Config, + config_state_handler: Option, + config_state: ConfigState, } #[derive(Debug)] @@ -280,6 +301,7 @@ impl PartialEq for WatcherWrapper { pub enum Message { AppTheme(AppTheme), Config(Config), + ConfigState(ConfigState), CloseFile, CloseProject(usize), Copy, @@ -377,6 +399,8 @@ pub struct App { tab_model: segmented_button::SingleSelectModel, config_handler: Option, config: Config, + config_state_handler: Option, + config_state: ConfigState, key_binds: HashMap, app_themes: Vec, font_names: Vec, @@ -488,10 +512,12 @@ impl App { self.projects.push((name.to_string(), path.to_path_buf())); // Add to recent projects, ensuring only one entry - self.config.recent_projects.retain(|x| x != path); - self.config.recent_projects.push_front(path.to_path_buf()); - self.config.recent_projects.truncate(10); - self.save_config_no_update(); + self.config_state.recent_projects.retain(|x| x != path); + self.config_state + .recent_projects + .push_front(path.to_path_buf()); + self.config_state.recent_projects.truncate(10); + self.save_config_state(); } _ => { log::error!("failed to open project {:?}: not a directory", path); @@ -546,10 +572,12 @@ impl App { } // Add to recent files, ensuring only one entry - self.config.recent_files.retain(|x| x != &canonical); - self.config.recent_files.push_front(canonical.to_path_buf()); - self.config.recent_files.truncate(10); - self.save_config_no_update(); + self.config_state.recent_files.retain(|x| x != &canonical); + self.config_state + .recent_files + .push_front(canonical.to_path_buf()); + self.config_state.recent_files.truncate(10); + self.save_config_state(); let mut tab = EditorTab::new(&self.config); tab.open(canonical); @@ -583,16 +611,20 @@ impl App { } fn save_config(&mut self) -> Command { - self.save_config_no_update(); - self.update_config() - } - - fn save_config_no_update(&mut self) { if let Some(ref config_handler) = self.config_handler { if let Err(err) = self.config.write_entry(config_handler) { log::error!("failed to save config: {}", err); } } + self.update_config() + } + + fn save_config_state(&mut self) { + if let Some(ref config_state_handler) = self.config_state_handler { + if let Err(err) = self.config_state.write_entry(config_state_handler) { + log::error!("failed to save config_state: {}", err); + } + } } fn update_focus(&self) -> Command { @@ -1129,6 +1161,8 @@ impl Application for App { tab_model: segmented_button::Model::builder().build(), config_handler: flags.config_handler, config: flags.config, + config_state_handler: flags.config_state_handler, + config_state: flags.config_state, key_binds: key_binds(), app_themes, font_names, @@ -1305,6 +1339,12 @@ impl Application for App { return self.update_config(); } } + Message::ConfigState(config_state) => { + if config_state != self.config_state { + log::info!("update config state"); + self.config_state = config_state; + } + } Message::CloseFile => { return self.update(Message::TabClose(self.tab_model.active())); } @@ -1629,13 +1669,13 @@ impl Application for App { } } Message::OpenRecentFile(index) => { - if let Some(path) = self.config.recent_files.get(index).cloned() { + if let Some(path) = self.config_state.recent_files.get(index).cloned() { self.open_tab(Some(path)); return self.update_tab(); } } Message::OpenRecentProject(index) => { - if let Some(path) = self.config.recent_projects.get(index).cloned() { + if let Some(path) = self.config_state.recent_projects.get(index).cloned() { self.open_project(path); } } @@ -2088,7 +2128,12 @@ impl Application for App { } fn header_start(&self) -> Vec> { - vec![menu_bar(&self.config, &self.key_binds, &self.projects)] + vec![menu_bar( + &self.config, + &self.config_state, + &self.key_binds, + &self.projects, + )] } fn view(&self) -> Element { @@ -2349,6 +2394,7 @@ impl Application for App { fn subscription(&self) -> subscription::Subscription { struct WatcherSubscription; struct ConfigSubscription; + struct ConfigStateSubscription; struct ThemeSubscription; subscription::Subscription::batch([ @@ -2435,6 +2481,18 @@ impl Application for App { Message::Config(update.config) }), + cosmic_config::config_state_subscription( + TypeId::of::(), + Self::APP_ID.into(), + CONFIG_VERSION, + ) + .map(|update| { + for error in update.errors { + log::error!("error loading config: {error:?}"); + } + + Message::ConfigState(update.config) + }), cosmic_config::config_subscription::<_, cosmic_theme::ThemeMode>( TypeId::of::(), cosmic_theme::THEME_MODE_ID.into(), diff --git a/src/menu.rs b/src/menu.rs index 187db41..2550220 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -17,7 +17,7 @@ use cosmic::{ }; use std::{collections::HashMap, path::PathBuf}; -use crate::{fl, icon_cache_get, Action, Config, KeyBind, Message}; +use crate::{fl, icon_cache_get, Action, Config, ConfigState, KeyBind, Message}; macro_rules! menu_button { ($($x:expr),+ $(,)?) => ( @@ -86,6 +86,7 @@ pub fn context_menu<'a>( pub fn menu_bar<'a>( config: &Config, + config_state: &ConfigState, key_binds: &HashMap, projects: &Vec<(String, PathBuf)>, ) -> Element<'a, Message> { @@ -161,13 +162,13 @@ pub fn menu_bar<'a>( path.display().to_string() }; - let mut recent_files = Vec::with_capacity(config.recent_files.len()); - for (i, path) in config.recent_files.iter().enumerate() { + let mut recent_files = Vec::with_capacity(config_state.recent_files.len()); + for (i, path) in config_state.recent_files.iter().enumerate() { recent_files.push(menu_item(format_path(path), Action::OpenRecentFile(i))); } - let mut recent_projects = Vec::with_capacity(config.recent_projects.len()); - for (i, path) in config.recent_projects.iter().enumerate() { + let mut recent_projects = Vec::with_capacity(config_state.recent_projects.len()); + for (i, path) in config_state.recent_projects.iter().enumerate() { recent_projects.push(menu_item(format_path(path), Action::OpenRecentProject(i))); }