theme: persist across app close
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::{
|
|||||||
accounts::Accounts,
|
accounts::Accounts,
|
||||||
app_creation::setup_cc,
|
app_creation::setup_cc,
|
||||||
app_size_handler::AppSizeHandler,
|
app_size_handler::AppSizeHandler,
|
||||||
|
app_style::{dark_mode, light_mode},
|
||||||
args::Args,
|
args::Args,
|
||||||
column::Columns,
|
column::Columns,
|
||||||
decks::{Decks, DecksCache, FALLBACK_PUBKEY},
|
decks::{Decks, DecksCache, FALLBACK_PUBKEY},
|
||||||
@@ -16,9 +17,10 @@ use crate::{
|
|||||||
storage::{self, DataPath, DataPathType, Directory, FileKeyStorage, KeyStorageType},
|
storage::{self, DataPath, DataPathType, Directory, FileKeyStorage, KeyStorageType},
|
||||||
subscriptions::{SubKind, Subscriptions},
|
subscriptions::{SubKind, Subscriptions},
|
||||||
support::Support,
|
support::Support,
|
||||||
|
theme_handler::ThemeHandler,
|
||||||
thread::Thread,
|
thread::Thread,
|
||||||
timeline::{self, Timeline},
|
timeline::{self, Timeline},
|
||||||
ui::{self, DesktopSidePanel},
|
ui::{self, is_compiled_as_mobile, DesktopSidePanel},
|
||||||
unknowns::UnknownIds,
|
unknowns::UnknownIds,
|
||||||
view_state::ViewState,
|
view_state::ViewState,
|
||||||
Result,
|
Result,
|
||||||
@@ -61,6 +63,7 @@ pub struct Damus {
|
|||||||
pub subscriptions: Subscriptions,
|
pub subscriptions: Subscriptions,
|
||||||
pub app_rect_handler: AppSizeHandler,
|
pub app_rect_handler: AppSizeHandler,
|
||||||
pub support: Support,
|
pub support: Support,
|
||||||
|
pub theme: ThemeHandler,
|
||||||
|
|
||||||
frame_history: crate::frame_history::FrameHistory,
|
frame_history: crate::frame_history::FrameHistory,
|
||||||
|
|
||||||
@@ -408,6 +411,15 @@ impl Damus {
|
|||||||
1024usize * 1024usize * 1024usize * 1024usize
|
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 config = Config::new().set_ingester_threads(4).set_mapsize(mapsize);
|
||||||
|
|
||||||
let keystore = if parsed_args.use_keystore {
|
let keystore = if parsed_args.use_keystore {
|
||||||
@@ -509,6 +521,7 @@ impl Damus {
|
|||||||
app_rect_handler,
|
app_rect_handler,
|
||||||
support,
|
support,
|
||||||
decks_cache,
|
decks_cache,
|
||||||
|
theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,6 +573,7 @@ impl Damus {
|
|||||||
let decks_cache = DecksCache::default();
|
let decks_cache = DecksCache::default();
|
||||||
|
|
||||||
let path = DataPath::new(&data_path);
|
let path = DataPath::new(&data_path);
|
||||||
|
let theme = ThemeHandler::new(&path);
|
||||||
let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir());
|
let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir());
|
||||||
let _ = std::fs::create_dir_all(imgcache_dir.clone());
|
let _ = std::fs::create_dir_all(imgcache_dir.clone());
|
||||||
let debug = true;
|
let debug = true;
|
||||||
@@ -596,6 +610,7 @@ impl Damus {
|
|||||||
app_rect_handler,
|
app_rect_handler,
|
||||||
support,
|
support,
|
||||||
decks_cache,
|
decks_cache,
|
||||||
|
theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,6 +730,7 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {
|
|||||||
&mut app.decks_cache,
|
&mut app.decks_cache,
|
||||||
&app.accounts,
|
&app.accounts,
|
||||||
&mut app.support,
|
&mut app.support,
|
||||||
|
&mut app.theme,
|
||||||
side_panel.action,
|
side_panel.action,
|
||||||
) {
|
) {
|
||||||
side_panel_action = Some(action);
|
side_panel_action = Some(action);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
use egui::{
|
use egui::{
|
||||||
epaint::Shadow,
|
epaint::Shadow,
|
||||||
style::{Interaction, Selection, WidgetVisuals, Widgets},
|
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::IntoEnumIterator;
|
||||||
use strum_macros::EnumIter;
|
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
|
/// Create custom text sizes for any FontSizes
|
||||||
pub fn add_custom_style(is_mobile: bool, style: &mut Style) {
|
pub fn add_custom_style(is_mobile: bool, style: &mut Style) {
|
||||||
let font_size = if is_mobile {
|
let font_size = if is_mobile {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ mod route;
|
|||||||
mod subscriptions;
|
mod subscriptions;
|
||||||
mod support;
|
mod support;
|
||||||
mod test_data;
|
mod test_data;
|
||||||
|
mod theme_handler;
|
||||||
mod thread;
|
mod thread;
|
||||||
mod time;
|
mod time;
|
||||||
mod timecache;
|
mod timecache;
|
||||||
|
|||||||
76
crates/notedeck_columns/src/theme_handler.rs
Normal file
76
crates/notedeck_columns/src/theme_handler.rs
Normal file
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
use egui::{
|
use egui::{
|
||||||
vec2, Color32, InnerResponse, Label, Layout, Margin, RichText, ScrollArea, Separator, Stroke,
|
vec2, Button, Color32, InnerResponse, Label, Layout, Margin, RichText, ScrollArea, Separator,
|
||||||
Widget,
|
Stroke, ThemePreference, Widget,
|
||||||
};
|
};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{Accounts, AccountsRoute},
|
accounts::{Accounts, AccountsRoute},
|
||||||
app::{get_active_columns_mut, get_decks_mut},
|
app::{get_active_columns_mut, get_decks_mut},
|
||||||
app_style::{self, DECK_ICON_SIZE},
|
app_style::DECK_ICON_SIZE,
|
||||||
colors,
|
colors,
|
||||||
column::Column,
|
column::Column,
|
||||||
decks::{DecksAction, DecksCache},
|
decks::{DecksAction, DecksCache},
|
||||||
@@ -15,6 +15,7 @@ use crate::{
|
|||||||
nav::SwitchingAction,
|
nav::SwitchingAction,
|
||||||
route::Route,
|
route::Route,
|
||||||
support::Support,
|
support::Support,
|
||||||
|
theme_handler::ThemeHandler,
|
||||||
user_account::UserAccount,
|
user_account::UserAccount,
|
||||||
Damus,
|
Damus,
|
||||||
};
|
};
|
||||||
@@ -55,6 +56,7 @@ pub enum SidePanelAction {
|
|||||||
NewDeck,
|
NewDeck,
|
||||||
SwitchDeck(usize),
|
SwitchDeck(usize),
|
||||||
EditDeck(usize),
|
EditDeck(usize),
|
||||||
|
SaveTheme(ThemePreference),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SidePanelResponse {
|
pub struct SidePanelResponse {
|
||||||
@@ -186,13 +188,33 @@ impl<'a> DesktopSidePanel<'a> {
|
|||||||
let pfp_resp = self.pfp_button(ui);
|
let pfp_resp = self.pfp_button(ui);
|
||||||
let settings_resp = ui.add(settings_button(dark_mode));
|
let settings_resp = ui.add(settings_button(dark_mode));
|
||||||
|
|
||||||
if let Some(new_visuals) = app_style::user_requested_visuals_change(
|
let save_theme = if let Some((theme, resp)) = match ui.ctx().theme() {
|
||||||
super::is_oled(),
|
egui::Theme::Dark => {
|
||||||
ui.ctx().style().visuals.dark_mode,
|
let resp = ui
|
||||||
ui,
|
.add(Button::new("☀").frame(false))
|
||||||
) {
|
.on_hover_text("Switch to light mode");
|
||||||
ui.ctx().set_visuals(new_visuals)
|
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());
|
let support_resp = ui.add(support_button());
|
||||||
|
|
||||||
@@ -211,6 +233,11 @@ impl<'a> DesktopSidePanel<'a> {
|
|||||||
SidePanelAction::Support,
|
SidePanelAction::Support,
|
||||||
support_resp,
|
support_resp,
|
||||||
))
|
))
|
||||||
|
} else if let Some((theme, resp)) = save_theme {
|
||||||
|
Some(egui::InnerResponse::new(
|
||||||
|
SidePanelAction::SaveTheme(theme),
|
||||||
|
resp,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -253,6 +280,7 @@ impl<'a> DesktopSidePanel<'a> {
|
|||||||
decks_cache: &mut DecksCache,
|
decks_cache: &mut DecksCache,
|
||||||
accounts: &Accounts,
|
accounts: &Accounts,
|
||||||
support: &mut Support,
|
support: &mut Support,
|
||||||
|
theme_handler: &mut ThemeHandler,
|
||||||
action: SidePanelAction,
|
action: SidePanelAction,
|
||||||
) -> Option<SwitchingAction> {
|
) -> Option<SwitchingAction> {
|
||||||
let router = get_active_columns_mut(accounts, decks_cache).get_first_router();
|
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
|
switching_response
|
||||||
}
|
}
|
||||||
@@ -656,6 +687,7 @@ mod preview {
|
|||||||
&mut self.app.decks_cache,
|
&mut self.app.decks_cache,
|
||||||
&self.app.accounts,
|
&self.app.accounts,
|
||||||
&mut self.app.support,
|
&mut self.app.support,
|
||||||
|
&mut self.app.theme,
|
||||||
response.action,
|
response.action,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user