notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 9e67f9dc8cbef6a4453b7a1809795178207cbc9f
parent 2ce845c1fcb6eced46c2611ae31fa34f0f8bfc1a
Author: kernelkind <kernelkind@gmail.com>
Date:   Wed, 11 Dec 2024 13:59:34 -0500

theme: persist across app close

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
Mcrates/notedeck_columns/src/app.rs | 18+++++++++++++++++-
Mcrates/notedeck_columns/src/app_style.rs | 25+------------------------
Mcrates/notedeck_columns/src/lib.rs | 1+
Acrates/notedeck_columns/src/theme_handler.rs | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/notedeck_columns/src/ui/side_panel.rs | 52++++++++++++++++++++++++++++++++++++++++++----------
5 files changed, 137 insertions(+), 35 deletions(-)

diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs @@ -2,6 +2,7 @@ use crate::{ accounts::Accounts, app_creation::setup_cc, app_size_handler::AppSizeHandler, + app_style::{dark_mode, light_mode}, args::Args, column::Columns, decks::{Decks, DecksCache, FALLBACK_PUBKEY}, @@ -16,9 +17,10 @@ use crate::{ storage::{self, DataPath, DataPathType, Directory, FileKeyStorage, KeyStorageType}, subscriptions::{SubKind, Subscriptions}, support::Support, + theme_handler::ThemeHandler, thread::Thread, timeline::{self, Timeline}, - ui::{self, DesktopSidePanel}, + ui::{self, is_compiled_as_mobile, DesktopSidePanel}, unknowns::UnknownIds, view_state::ViewState, Result, @@ -61,6 +63,7 @@ pub struct Damus { pub subscriptions: Subscriptions, pub app_rect_handler: AppSizeHandler, pub support: Support, + pub theme: ThemeHandler, frame_history: crate::frame_history::FrameHistory, @@ -408,6 +411,15 @@ impl Damus { 1024usize * 1024usize * 1024usize * 1024usize }; + let theme = ThemeHandler::new(&path); + ctx.options_mut(|o| { + let cur_theme = theme.load(); + info!("Loaded theme {:?} from disk", cur_theme); + o.theme_preference = cur_theme; + }); + ctx.set_visuals_of(egui::Theme::Dark, dark_mode(is_compiled_as_mobile())); + ctx.set_visuals_of(egui::Theme::Light, light_mode()); + let config = Config::new().set_ingester_threads(4).set_mapsize(mapsize); let keystore = if parsed_args.use_keystore { @@ -509,6 +521,7 @@ impl Damus { app_rect_handler, support, decks_cache, + theme, } } @@ -560,6 +573,7 @@ impl Damus { let decks_cache = DecksCache::default(); let path = DataPath::new(&data_path); + let theme = ThemeHandler::new(&path); let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir()); let _ = std::fs::create_dir_all(imgcache_dir.clone()); let debug = true; @@ -596,6 +610,7 @@ impl Damus { app_rect_handler, support, decks_cache, + theme, } } @@ -715,6 +730,7 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) { &mut app.decks_cache, &app.accounts, &mut app.support, + &mut app.theme, side_panel.action, ) { side_panel_action = Some(action); diff --git a/crates/notedeck_columns/src/app_style.rs b/crates/notedeck_columns/src/app_style.rs @@ -6,7 +6,7 @@ use crate::{ use egui::{ epaint::Shadow, style::{Interaction, Selection, WidgetVisuals, Widgets}, - Button, FontFamily, FontId, Rounding, Stroke, Style, TextStyle, Ui, Visuals, + FontFamily, FontId, Rounding, Stroke, Style, TextStyle, Visuals, }; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -28,29 +28,6 @@ pub fn dark_mode(mobile: bool) -> Visuals { ) } -pub fn user_requested_visuals_change( - oled: bool, - cur_darkmode: bool, - ui: &mut Ui, -) -> Option<Visuals> { - if cur_darkmode { - if ui - .add(Button::new("☀").frame(false)) - .on_hover_text("Switch to light mode") - .clicked() - { - return Some(light_mode()); - } - } else if ui - .add(Button::new("🌙").frame(false)) - .on_hover_text("Switch to dark mode") - .clicked() - { - return Some(dark_mode(oled)); - } - None -} - /// Create custom text sizes for any FontSizes pub fn add_custom_style(is_mobile: bool, style: &mut Style) { let font_size = if is_mobile { diff --git a/crates/notedeck_columns/src/lib.rs b/crates/notedeck_columns/src/lib.rs @@ -36,6 +36,7 @@ mod route; mod subscriptions; mod support; mod test_data; +mod theme_handler; mod thread; mod time; mod timecache; diff --git a/crates/notedeck_columns/src/theme_handler.rs b/crates/notedeck_columns/src/theme_handler.rs @@ -0,0 +1,76 @@ +use egui::ThemePreference; +use tracing::{error, info}; + +use crate::storage::{write_file, DataPath, DataPathType, Directory}; + +pub struct ThemeHandler { + directory: Directory, + fallback_theme: ThemePreference, +} + +const THEME_FILE: &str = "theme.txt"; + +impl ThemeHandler { + pub fn new(path: &DataPath) -> Self { + let directory = Directory::new(path.path(DataPathType::Setting)); + let fallback_theme = ThemePreference::Light; + Self { + directory, + fallback_theme, + } + } + + pub fn load(&self) -> ThemePreference { + match self.directory.get_file(THEME_FILE.to_owned()) { + Ok(contents) => match deserialize_theme(contents) { + Some(theme) => theme, + None => { + error!( + "Could not deserialize theme. Using fallback {:?} instead", + self.fallback_theme + ); + self.fallback_theme + } + }, + Err(e) => { + error!( + "Could not read {} file: {:?}\nUsing fallback {:?} instead", + THEME_FILE, e, self.fallback_theme + ); + self.fallback_theme + } + } + } + + pub fn save(&self, theme: ThemePreference) { + match write_file( + &self.directory.file_path, + THEME_FILE.to_owned(), + &theme_to_serialized(&theme), + ) { + Ok(_) => info!( + "Successfully saved {:?} theme change to {}", + theme, THEME_FILE + ), + Err(_) => error!("Could not save {:?} theme change to {}", theme, THEME_FILE), + } + } +} + +fn theme_to_serialized(theme: &ThemePreference) -> String { + match theme { + ThemePreference::Dark => "dark", + ThemePreference::Light => "light", + ThemePreference::System => "system", + } + .to_owned() +} + +fn deserialize_theme(serialized_theme: String) -> Option<ThemePreference> { + match serialized_theme.as_str() { + "dark" => Some(ThemePreference::Dark), + "light" => Some(ThemePreference::Light), + "system" => Some(ThemePreference::System), + _ => None, + } +} diff --git a/crates/notedeck_columns/src/ui/side_panel.rs b/crates/notedeck_columns/src/ui/side_panel.rs @@ -1,13 +1,13 @@ use egui::{ - vec2, Color32, InnerResponse, Label, Layout, Margin, RichText, ScrollArea, Separator, Stroke, - Widget, + vec2, Button, Color32, InnerResponse, Label, Layout, Margin, RichText, ScrollArea, Separator, + Stroke, ThemePreference, Widget, }; use tracing::{error, info}; use crate::{ accounts::{Accounts, AccountsRoute}, app::{get_active_columns_mut, get_decks_mut}, - app_style::{self, DECK_ICON_SIZE}, + app_style::DECK_ICON_SIZE, colors, column::Column, decks::{DecksAction, DecksCache}, @@ -15,6 +15,7 @@ use crate::{ nav::SwitchingAction, route::Route, support::Support, + theme_handler::ThemeHandler, user_account::UserAccount, Damus, }; @@ -55,6 +56,7 @@ pub enum SidePanelAction { NewDeck, SwitchDeck(usize), EditDeck(usize), + SaveTheme(ThemePreference), } pub struct SidePanelResponse { @@ -186,13 +188,33 @@ impl<'a> DesktopSidePanel<'a> { let pfp_resp = self.pfp_button(ui); let settings_resp = ui.add(settings_button(dark_mode)); - if let Some(new_visuals) = app_style::user_requested_visuals_change( - super::is_oled(), - ui.ctx().style().visuals.dark_mode, - ui, - ) { - ui.ctx().set_visuals(new_visuals) - } + let save_theme = if let Some((theme, resp)) = match ui.ctx().theme() { + egui::Theme::Dark => { + let resp = ui + .add(Button::new("☀").frame(false)) + .on_hover_text("Switch to light mode"); + if resp.clicked() { + Some((ThemePreference::Light, resp)) + } else { + None + } + } + egui::Theme::Light => { + let resp = ui + .add(Button::new("🌙").frame(false)) + .on_hover_text("Switch to dark mode"); + if resp.clicked() { + Some((ThemePreference::Dark, resp)) + } else { + None + } + } + } { + ui.ctx().set_theme(theme); + Some((theme, resp)) + } else { + None + }; let support_resp = ui.add(support_button()); @@ -211,6 +233,11 @@ impl<'a> DesktopSidePanel<'a> { SidePanelAction::Support, support_resp, )) + } else if let Some((theme, resp)) = save_theme { + Some(egui::InnerResponse::new( + SidePanelAction::SaveTheme(theme), + resp, + )) } else { None }; @@ -253,6 +280,7 @@ impl<'a> DesktopSidePanel<'a> { decks_cache: &mut DecksCache, accounts: &Accounts, support: &mut Support, + theme_handler: &mut ThemeHandler, action: SidePanelAction, ) -> Option<SwitchingAction> { let router = get_active_columns_mut(accounts, decks_cache).get_first_router(); @@ -345,6 +373,9 @@ impl<'a> DesktopSidePanel<'a> { } } } + SidePanelAction::SaveTheme(theme) => { + theme_handler.save(theme); + } } switching_response } @@ -656,6 +687,7 @@ mod preview { &mut self.app.decks_cache, &self.app.accounts, &mut self.app.support, + &mut self.app.theme, response.action, ); });