refactor: introduce thread local THEME variable and distinguish between custom and system theme settings

This commit is contained in:
Ashley Wulber 2023-08-04 15:48:34 -04:00 committed by Ashley Wulber
parent 40efcbbe31
commit 6c57e04e36
8 changed files with 80 additions and 36 deletions

View file

@ -1,7 +1,10 @@
/// Copyright 2022 System76 <info@system76.com> /// Copyright 2022 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use cosmic::{ use cosmic::{
cosmic_config::config_subscription, cosmic_theme::{
palette::{rgb::Rgb, Srgba},
ThemeBuilder,
},
font::load_fonts, font::load_fonts,
iced::{self, Application, Command, Length, Subscription}, iced::{self, Application, Command, Length, Subscription},
iced::{ iced::{
@ -10,7 +13,7 @@ use cosmic::{
window::{self, close, drag, minimize, toggle_maximize}, window::{self, close, drag, minimize, toggle_maximize},
}, },
keyboard_nav, keyboard_nav,
theme::{self, CosmicTheme, Theme}, theme::{self, Theme},
widget::{ widget::{
header_bar, icon, list, nav_bar, nav_bar_toggle, scrollable, segmented_button, settings, header_bar, icon, list, nav_bar, nav_bar_toggle, scrollable, segmented_button, settings,
warning, IconSource, warning, IconSource,
@ -18,9 +21,7 @@ use cosmic::{
Element, ElementExt, Element, ElementExt,
}; };
use cosmic_time::{Instant, Timeline}; use cosmic_time::{Instant, Timeline};
use log::error;
use std::{ use std::{
borrow::Cow,
cell::RefCell, cell::RefCell,
rc::Rc, rc::Rc,
sync::{ sync::{
@ -163,7 +164,6 @@ pub struct Window {
warning_message: String, warning_message: String,
scale_factor: f64, scale_factor: f64,
scale_factor_string: String, scale_factor_string: String,
system_theme: Arc<CosmicTheme>,
timeline: Rc<RefCell<Timeline>>, timeline: Rc<RefCell<Timeline>>,
} }
@ -209,7 +209,6 @@ pub enum Message {
ToggleNavBarCondensed, ToggleNavBarCondensed,
ToggleWarning, ToggleWarning,
FontsLoaded, FontsLoaded,
SystemTheme(CosmicTheme),
Tick(Instant), Tick(Instant),
} }
@ -389,17 +388,6 @@ impl Application for Window {
Subscription::batch(vec![ Subscription::batch(vec![
window_break.map(|_| Message::CondensedViewToggle), window_break.map(|_| Message::CondensedViewToggle),
keyboard_nav::subscription().map(Message::KeyboardNav), keyboard_nav::subscription().map(Message::KeyboardNav),
config_subscription::<_, CosmicTheme>(0, Cow::from("com.system76.CosmicTheme"), 1).map(
|(_, update)| match update {
Ok(t) => Message::SystemTheme(t),
Err((errors, t)) => {
for error in errors {
error!("{:?}", error);
}
Message::SystemTheme(t)
}
},
),
self.timeline self.timeline
.borrow() .borrow()
.as_subscription() .as_subscription()
@ -429,7 +417,18 @@ impl Application for Window {
demo::ThemeVariant::Dark => Theme::dark(), demo::ThemeVariant::Dark => Theme::dark(),
demo::ThemeVariant::HighContrastDark => Theme::dark_hc(), demo::ThemeVariant::HighContrastDark => Theme::dark_hc(),
demo::ThemeVariant::HighContrastLight => Theme::light_hc(), demo::ThemeVariant::HighContrastLight => Theme::light_hc(),
demo::ThemeVariant::Custom => Theme::custom(self.system_theme.clone()), demo::ThemeVariant::Custom => Theme::custom(Arc::new(
ThemeBuilder::light()
.bg_color(Srgba::new(1.0, 0.9, 0.9, 1.0))
.text_tint(Rgb::new(0.0, 1.0, 0.0))
.neutral_tint(Rgb::new(0.0, 0.5, 1.0))
.accent(Rgb::new(0.5, 0.1, 0.5))
.success(Rgb::new(0.0, 0.5, 0.3))
.warning(Rgb::new(0.894, 0.816, 0.039))
.destructive(Rgb::new(0.890, 0.145, 0.420))
.build(),
)),
demo::ThemeVariant::System => cosmic::theme::theme(),
}; };
} }
Some(demo::Output::ToggleWarning) => self.toggle_warning(), Some(demo::Output::ToggleWarning) => self.toggle_warning(),
@ -460,9 +459,6 @@ impl Application for Window {
}, },
Message::ToggleWarning => self.toggle_warning(), Message::ToggleWarning => self.toggle_warning(),
Message::FontsLoaded => {} Message::FontsLoaded => {}
Message::SystemTheme(t) => {
self.system_theme = Arc::new(t);
}
Message::Tick(instant) => self.timeline.borrow_mut().now(instant), Message::Tick(instant) => self.timeline.borrow_mut().now(instant),
} }
ret ret

View file

@ -27,6 +27,7 @@ pub enum ThemeVariant {
HighContrastDark, HighContrastDark,
HighContrastLight, HighContrastLight,
Custom, Custom,
System,
} }
impl From<&ThemeType> for ThemeVariant { impl From<&ThemeType> for ThemeVariant {
@ -37,6 +38,7 @@ impl From<&ThemeType> for ThemeVariant {
ThemeType::HighContrastDark => ThemeVariant::HighContrastDark, ThemeType::HighContrastDark => ThemeVariant::HighContrastDark,
ThemeType::HighContrastLight => ThemeVariant::HighContrastLight, ThemeType::HighContrastLight => ThemeVariant::HighContrastLight,
ThemeType::Custom(_) => ThemeVariant::Custom, ThemeType::Custom(_) => ThemeVariant::Custom,
ThemeType::System(_) => ThemeVariant::System,
} }
} }
} }
@ -210,8 +212,9 @@ impl State {
ThemeVariant::Light, ThemeVariant::Light,
ThemeVariant::Dark, ThemeVariant::Dark,
ThemeVariant::HighContrastLight, ThemeVariant::HighContrastLight,
ThemeVariant::HighContrastLight, ThemeVariant::HighContrastDark,
ThemeVariant::Custom, ThemeVariant::Custom,
ThemeVariant::System,
] ]
.into_iter() .into_iter()
.fold( .fold(

View file

@ -193,7 +193,7 @@ impl State {
} }
fn view_desktop_wallpaper<'a>(&'a self, window: &'a Window) -> Element<'a, Message> { fn view_desktop_wallpaper<'a>(&'a self, window: &'a Window) -> Element<'a, Message> {
let mut image_paths: Vec<std::path::PathBuf> = Vec::new(); let image_paths: Vec<std::path::PathBuf> = Vec::new();
/* /*
//TODO: load image paths, do this asynchronously somehow //TODO: load image paths, do this asynchronously somehow
if let Ok(entries) = std::fs::read_dir("/usr/share/backgrounds") { if let Ok(entries) = std::fs::read_dir("/usr/share/backgrounds") {

View file

@ -1,5 +1,4 @@
use apply::Apply; use cosmic::iced::widget::{horizontal_space, row};
use cosmic::iced::widget::{horizontal_space, row, scrollable};
use cosmic::iced::{Alignment, Length}; use cosmic::iced::{Alignment, Length};
use cosmic::widget::{button, segmented_button, view_switcher}; use cosmic::widget::{button, segmented_button, view_switcher};
use cosmic::{theme, Element}; use cosmic::{theme, Element};
@ -60,7 +59,7 @@ impl State {
self.pages.remove(id); self.pages.remove(id);
} }
pub(super) fn view<'a>(&'a self, window: &'a super::Window) -> Element<'a, Message> { pub(super) fn view<'a>(&'a self, _window: &'a super::Window) -> Element<'a, Message> {
let tabs = view_switcher::horizontal(&self.pages) let tabs = view_switcher::horizontal(&self.pages)
.show_close_icon_on_hover(true) .show_close_icon_on_hover(true)
.on_activate(Message::Activate) .on_activate(Message::Activate)

View file

@ -40,7 +40,7 @@ pub struct Core {
/// Scaling factor used by the application /// Scaling factor used by the application
scale_factor: f32, scale_factor: f32,
pub theme: Theme, pub system_theme: Theme,
pub(crate) title: String, pub(crate) title: String,
pub window: Window, pub window: Window,
} }
@ -56,7 +56,7 @@ impl Default for Core {
toggled_condensed: true, toggled_condensed: true,
}, },
scale_factor: 1.0, scale_factor: 1.0,
theme: theme::theme(), system_theme: theme::theme(),
title: String::new(), title: String::new(),
window: Window { window: Window {
can_fullscreen: false, can_fullscreen: false,

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use super::{command, Application, ApplicationExt, Core, Subscription}; use super::{command, Application, ApplicationExt, Core, Subscription};
use crate::theme::{self, Theme}; use crate::theme::{self, Theme, ThemeType, THEME};
use crate::widget::nav_bar; use crate::widget::nav_bar;
use crate::{keyboard_nav, Element}; use crate::{keyboard_nav, Element};
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
@ -36,6 +36,8 @@ pub enum Message {
ScaleFactor(f32), ScaleFactor(f32),
/// Requests theme changes. /// Requests theme changes.
ThemeChange(Theme), ThemeChange(Theme),
/// Notification of system theme changes.
SystemThemeChange(Theme),
/// Toggles visibility of the nav bar. /// Toggles visibility of the nav bar.
ToggleNavBar, ToggleNavBar,
/// Toggles the condensed status of the nav bar. /// Toggles the condensed status of the nav bar.
@ -146,14 +148,14 @@ where
.map(Message::KeyboardNav) .map(Message::KeyboardNav)
.map(super::Message::Cosmic), .map(super::Message::Cosmic),
theme::subscription(0) theme::subscription(0)
.map(Message::ThemeChange) .map(Message::SystemThemeChange)
.map(super::Message::Cosmic), .map(super::Message::Cosmic),
window_events.map(super::Message::Cosmic), window_events.map(super::Message::Cosmic),
]) ])
} }
fn theme(&self) -> Self::Theme { fn theme(&self) -> Self::Theme {
self.app.core().theme.clone() THEME.with(|t| t.borrow().clone())
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
@ -267,12 +269,32 @@ impl<T: Application> Cosmic<T> {
} }
Message::ThemeChange(theme) => { Message::ThemeChange(theme) => {
self.app.core_mut().theme = theme; // our system theme is always receiving updates so we should use it instead
let theme = if matches!(theme.theme_type, ThemeType::System(_)) {
self.app.core().system_theme.clone()
} else {
theme
};
THEME.with(move |t| {
let mut cosmic_theme = t.borrow_mut();
cosmic_theme.set_theme(theme.theme_type);
});
} }
Message::ScaleFactor(factor) => { Message::ScaleFactor(factor) => {
self.app.core_mut().set_scale_factor(factor); self.app.core_mut().set_scale_factor(factor);
} }
Message::SystemThemeChange(theme) => {
self.app.core_mut().system_theme = theme.clone();
THEME.with(move |t| {
let mut cosmic_theme = t.borrow_mut();
// only apply update if the theme is set to load a system theme
if matches!(cosmic_theme.theme_type, ThemeType::System(_)) {
cosmic_theme.set_theme(theme.theme_type);
}
});
}
} }
iced::Command::none() iced::Command::none()

View file

@ -33,6 +33,7 @@ pub mod message {
pub use self::core::Core; pub use self::core::Core;
pub use self::settings::Settings; pub use self::settings::Settings;
use crate::theme::THEME;
use crate::widget::nav_bar; use crate::widget::nav_bar;
use crate::{Element, ElementExt}; use crate::{Element, ElementExt};
use apply::Apply; use apply::Apply;
@ -58,7 +59,10 @@ pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Res
core.set_scale_factor(settings.scale_factor); core.set_scale_factor(settings.scale_factor);
core.set_window_width(settings.size.0); core.set_window_width(settings.size.0);
core.set_window_height(settings.size.1); core.set_window_height(settings.size.1);
core.theme = settings.theme; THEME.with(move |t| {
let mut cosmic_theme = t.borrow_mut();
cosmic_theme.set_theme(settings.theme.theme_type);
});
let mut iced = iced::Settings::with_flags((core, flags)); let mut iced = iced::Settings::with_flags((core, flags));

View file

@ -4,6 +4,7 @@
pub mod expander; pub mod expander;
mod segmented_button; mod segmented_button;
use std::cell::RefCell;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::hash::Hash; use std::hash::Hash;
use std::hash::Hasher; use std::hash::Hasher;
@ -64,6 +65,10 @@ lazy_static::lazy_static! {
}; };
} }
thread_local! {
pub(crate) static THEME: RefCell<Theme> = RefCell::new(Theme { theme_type: ThemeType::Dark, layer: cosmic_theme::Layer::Background });
}
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Debug, Clone, PartialEq, Default)]
pub enum ThemeType { pub enum ThemeType {
#[default] #[default]
@ -72,6 +77,7 @@ pub enum ThemeType {
HighContrastDark, HighContrastDark,
HighContrastLight, HighContrastLight,
Custom(Arc<CosmicTheme>), Custom(Arc<CosmicTheme>),
System(Arc<CosmicTheme>),
} }
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Debug, Clone, PartialEq, Default)]
@ -88,7 +94,7 @@ impl Theme {
ThemeType::Light => &COSMIC_LIGHT, ThemeType::Light => &COSMIC_LIGHT,
ThemeType::HighContrastDark => &COSMIC_HC_DARK, ThemeType::HighContrastDark => &COSMIC_HC_DARK,
ThemeType::HighContrastLight => &COSMIC_HC_LIGHT, ThemeType::HighContrastLight => &COSMIC_HC_LIGHT,
ThemeType::Custom(ref t) => t.as_ref(), ThemeType::Custom(ref t) | ThemeType::System(ref t) => t.as_ref(),
} }
} }
@ -132,6 +138,14 @@ impl Theme {
} }
} }
#[must_use]
pub fn system(theme: Arc<CosmicTheme>) -> Self {
Self {
theme_type: ThemeType::System(theme),
..Default::default()
}
}
/// get current container /// get current container
/// can be used in a component that is intended to be a child of a `CosmicContainer` /// can be used in a component that is intended to be a child of a `CosmicContainer`
#[must_use] #[must_use]
@ -142,6 +156,11 @@ impl Theme {
cosmic_theme::Layer::Secondary => &self.cosmic().secondary, cosmic_theme::Layer::Secondary => &self.cosmic().secondary,
} }
} }
/// set the theme
pub fn set_theme(&mut self, theme: ThemeType) {
self.theme_type = theme;
}
} }
impl LayeredTheme for Theme { impl LayeredTheme for Theme {
@ -1215,7 +1234,7 @@ pub fn theme() -> Theme {
} }
theme theme
}); });
crate::theme::Theme::custom(Arc::new(t)) crate::theme::Theme::system(Arc::new(t))
} }
pub fn subscription(id: u64) -> Subscription<crate::theme::Theme> { pub fn subscription(id: u64) -> Subscription<crate::theme::Theme> {
@ -1231,7 +1250,8 @@ pub fn subscription(id: u64) -> Subscription<crate::theme::Theme> {
} }
theme theme
}); });
crate::theme::Theme::custom(Arc::new(theme))
crate::theme::Theme::system(Arc::new(theme))
}) })
} }