Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: support desktop portal color-scheme, and accent variables #310

Merged
merged 4 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ xdg-portal = ["ashpd"]

[dependencies]
apply = "0.3.0"
ashpd = { version = "0.6.8", default-features = false, optional = true }
ashpd = { version = "0.7.0", default-features = false, optional = true }
async-fs = { version = "2.1", optional = true }
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "e65fa5e", optional = true }
cosmic-config = { path = "cosmic-config" }
Expand Down
27 changes: 26 additions & 1 deletion cosmic-theme/src/model/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
DARK_PALETTE, LIGHT_PALETTE, NAME,
};
use cosmic_config::{Config, CosmicConfigEntry};
use palette::{IntoColor, Srgb, Srgba};
use palette::{IntoColor, Oklcha, Srgb, Srgba};
use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize;

Expand Down Expand Up @@ -495,6 +495,31 @@ impl Theme {
.map_err(|e| (vec![e], Self::default()))?;
Self::get_entry(&config)
}

#[must_use]
/// Rebuild the current theme with the provided accent
pub fn with_accent(&self, c: Srgba) -> Self {
let mut oklcha: Oklcha = c.into_color();
let cur_oklcha: Oklcha = self.accent_color().into_color();
oklcha.l = cur_oklcha.l;
let adjusted_c: Srgb = oklcha.into_color();

let is_dark = self.is_dark;

let mut builder = if is_dark {
ThemeBuilder::dark_config()
.ok()
.and_then(|h| ThemeBuilder::get_entry(&h).ok())
.unwrap_or_else(ThemeBuilder::dark)
} else {
ThemeBuilder::light_config()
.ok()
.and_then(|h| ThemeBuilder::get_entry(&h).ok())
.unwrap_or_else(ThemeBuilder::light)
};
builder = builder.accent(adjusted_c);
builder.build()
}
}

impl From<CosmicPalette> for Theme {
Expand Down
3 changes: 2 additions & 1 deletion examples/application/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ edition = "2021"
[dependencies]
tracing = "0.1.37"
tracing-subscriber = "0.3.17"
tracing-log = "0.2.0"

[dependencies.libcosmic]
path = "../../"
default-features = false
features = ["debug", "winit", "tokio"]
features = ["debug", "winit", "tokio", "xdg-portal"]
6 changes: 4 additions & 2 deletions examples/application/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ impl Page {
/// Runs application with these settings
#[rustfmt::skip]
fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let _ = tracing_log::LogTracer::init();

let input = vec![
(Page::Page1, "🖖 Hello from libcosmic.".into()),
(Page::Page2, "🌟 This is an example application.".into()),
Expand All @@ -44,8 +47,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.default_icon_theme("Pop")
.default_text_size(16.0)
.scale_factor(1.0)
.size(Size::new(1024., 768.))
.theme(cosmic::Theme::dark());
.size(Size::new(1024., 768.));

cosmic::app::run::<App>(settings, input)?;

Expand Down
2 changes: 1 addition & 1 deletion examples/cosmic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ publish = false
[dependencies]
apply = "0.3.0"
fraction = "0.14.0"
libcosmic = { path = "../..", features = ["debug", "winit", "tokio", "single-instance", "dbus-config", "a11y", "wgpu" ] }
libcosmic = { path = "../..", features = ["debug", "winit", "tokio", "single-instance", "dbus-config", "a11y", "wgpu", "xdg-portal"] }
once_cell = "1.18"
slotmap = "1.0.6"
env_logger = "0.10"
Expand Down
19 changes: 18 additions & 1 deletion src/app/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::collections::HashMap;
use cosmic_config::CosmicConfigEntry;
use cosmic_theme::ThemeMode;
use iced_core::window::Id;
use palette::Srgba;

use crate::Theme;

Expand Down Expand Up @@ -59,9 +60,15 @@ pub struct Core {
/// Last known system theme
pub(super) system_theme: Theme,

/// Theme mode
/// Configured theme mode
pub(super) system_theme_mode: ThemeMode,

pub(super) portal_is_dark: Option<bool>,

pub(super) portal_accent: Option<Srgba>,

pub(super) portal_is_high_contrast: Option<bool>,

pub(super) title: HashMap<Id, String>,

pub window: Window,
Expand Down Expand Up @@ -121,6 +128,9 @@ impl Default for Core {
single_instance: false,
#[cfg(feature = "dbus-config")]
settings_daemon: None,
portal_is_dark: None,
portal_accent: None,
portal_is_high_contrast: None,
}
}
}
Expand Down Expand Up @@ -260,4 +270,11 @@ impl Core {
T::VERSION,
)
}

/// Whether the application should use a dark theme, according to the system
#[must_use]
pub fn system_is_dark(&self) -> bool {
self.portal_is_dark
.unwrap_or(self.system_theme_mode.is_dark)
}
}
146 changes: 129 additions & 17 deletions src/app/cosmic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ use iced::event::PlatformSpecific;
use iced::multi_window::Application as IcedApplication;
#[cfg(feature = "wayland")]
use iced::wayland::Application as IcedApplication;
use iced::window;
#[cfg(not(any(feature = "multi-window", feature = "wayland")))]
use iced::Application as IcedApplication;
use iced::{window, Command};
use iced_futures::event::listen_with;
#[cfg(not(feature = "wayland"))]
use iced_runtime::command::Action;
#[cfg(not(feature = "wayland"))]
use iced_runtime::window::Action as WindowAction;
use palette::color_difference::EuclideanDistance;

/// A message managed internally by COSMIC.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -53,9 +54,9 @@ pub enum Message {
/// Toggles the condensed status of the nav bar.
ToggleNavBarCondensed,
/// Notification of system theme changes.
SystemThemeChange(Theme),
SystemThemeChange(Vec<&'static str>, Theme),
/// Notification of system theme mode changes.
SystemThemeModeChange(ThemeMode),
SystemThemeModeChange(Vec<&'static str>, ThemeMode),
/// Updates the window maximized state
WindowMaximized(window::Id, bool),
/// Updates the tracked window geometry.
Expand All @@ -72,6 +73,8 @@ pub enum Message {
/// Activate the application
Activate(String),
ShowWindowMenu,
#[cfg(feature = "xdg-portal")]
DesktopSettings(crate::theme::portal::Desktop),
}

#[derive(Default)]
Expand Down Expand Up @@ -190,7 +193,10 @@ where
for e in update.errors {
tracing::error!("{e}");
}
Message::SystemThemeChange(crate::theme::Theme::system(Arc::new(update.config)))
Message::SystemThemeChange(
update.keys,
crate::theme::Theme::system(Arc::new(update.config)),
)
})
.map(super::Message::Cosmic),
self.app
Expand All @@ -200,16 +206,14 @@ where
for e in update.errors {
tracing::error!("{e}");
}
Message::SystemThemeModeChange(update.config)
Message::SystemThemeModeChange(update.keys, update.config)
})
.map(super::Message::Cosmic),
window_events.map(super::Message::Cosmic),
#[cfg(feature = "single-instance")]
self.app
.core()
.single_instance
.then(|| super::single_instance_subscription::<T>())
.unwrap_or_else(Subscription::none),
#[cfg(feature = "xdg-portal")]
crate::theme::portal::desktop_settings()
.map(Message::DesktopSettings)
.map(super::Message::Cosmic),
];

if self.app.core().keyboard_nav {
Expand All @@ -220,6 +224,11 @@ where
);
}

#[cfg(feature = "single-instance")]
if self.app.core().single_instance {
subscriptions.push(super::single_instance_subscription::<T>());
}

Subscription::batch(subscriptions)
}

Expand Down Expand Up @@ -363,7 +372,15 @@ impl<T: Application> Cosmic<T> {
// Apply last-known system theme if the system theme is preferred.
if let ThemeType::System(_) = theme.theme_type {
self.app.core_mut().theme_sub_counter += 1;

theme = self.app.core().system_theme.clone();
let portal_accent = self.app.core().portal_accent;
if let Some(a) = portal_accent {
let t_inner = theme.cosmic();
if a.distance_squared(*t_inner.accent_color()) > 0.00001 {
theme = Theme::system(Arc::new(t_inner.with_accent(a)));
}
};
}

THEME.with(move |t| {
Expand All @@ -372,17 +389,31 @@ impl<T: Application> Cosmic<T> {
});
}

Message::SystemThemeChange(theme) => {
Message::SystemThemeChange(keys, theme) => {
let cmd = self.app.system_theme_update(&keys, theme.cosmic());
// Record the last-known system theme in event that the current theme is custom.
self.app.core_mut().system_theme = theme.clone();
let portal_accent = self.app.core().portal_accent;
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 let ThemeType::System(_) = cosmic_theme.theme_type {
cosmic_theme.set_theme(theme.theme_type);
let new_theme = if let Some(a) = portal_accent {
let t_inner = theme.cosmic();
if a.distance_squared(*t_inner.accent_color()) > 0.00001 {
Theme::system(Arc::new(t_inner.with_accent(a)))
} else {
theme
}
} else {
theme
};
cosmic_theme.set_theme(new_theme.theme_type);
}
});

return cmd;
}

Message::ScaleFactor(factor) => {
Expand All @@ -393,13 +424,35 @@ impl<T: Application> Cosmic<T> {
self.app.on_app_exit();
return self.close();
}
Message::SystemThemeModeChange(mode) => {
Message::SystemThemeModeChange(keys, mode) => {
let mut cmds = vec![self.app.system_theme_mode_update(&keys, &mode)];

let core = self.app.core_mut();
let changed = core.system_theme_mode.is_dark != mode.is_dark;
let prev_is_dark = core.system_is_dark();
core.system_theme_mode = mode;
core.theme_sub_counter += 1;
let is_dark = core.system_is_dark();
let changed = prev_is_dark != is_dark;
if changed {
let new_theme = crate::theme::system_preference();
core.theme_sub_counter += 1;
let mut new_theme = if is_dark {
crate::theme::system_dark()
} else {
crate::theme::system_light()
};
cmds.push(self.app.system_theme_update(&[], new_theme.cosmic()));

let core = self.app.core_mut();
new_theme = if let Some(a) = core.portal_accent {
let t_inner = new_theme.cosmic();
if a.distance_squared(*t_inner.accent_color()) > 0.00001 {
Theme::system(Arc::new(t_inner.with_accent(a)))
} else {
new_theme
}
} else {
new_theme
};

core.system_theme = new_theme.clone();
THEME.with(move |t| {
let mut cosmic_theme = t.borrow_mut();
Expand All @@ -410,6 +463,7 @@ impl<T: Application> Cosmic<T> {
}
});
}
return Command::batch(cmds);
}
Message::Activate(_token) => {
#[cfg(feature = "wayland")]
Expand All @@ -428,6 +482,64 @@ impl<T: Application> Cosmic<T> {
#[cfg(not(feature = "wayland"))]
return window::show_window_menu(window::Id::MAIN);
}
#[cfg(feature = "xdg-portal")]
Message::DesktopSettings(crate::theme::portal::Desktop::ColorScheme(s)) => {
use ashpd::desktop::settings::ColorScheme;
let is_dark = match s {
ColorScheme::NoPreference => None,
ColorScheme::PreferDark => Some(true),
ColorScheme::PreferLight => Some(false),
};
let core = self.app.core_mut();
let prev_is_dark = core.system_is_dark();
core.portal_is_dark = is_dark;
let is_dark = core.system_is_dark();
let changed = prev_is_dark != is_dark;
if changed {
core.theme_sub_counter += 1;
let new_theme = if is_dark {
crate::theme::system_dark()
} else {
crate::theme::system_light()
};
core.system_theme = new_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 let ThemeType::System(_) = cosmic_theme.theme_type {
cosmic_theme.set_theme(new_theme.theme_type);
}
});
}
}
#[cfg(feature = "xdg-portal")]
Message::DesktopSettings(crate::theme::portal::Desktop::Accent(c)) => {
use palette::Srgba;

let c = Srgba::new(c.red() as f32, c.green() as f32, c.blue() as f32, 1.0);
let core = self.app.core_mut();
core.portal_accent = Some(c);
let cur_accent = core.system_theme.cosmic().accent_color();

if cur_accent.distance_squared(*c) < 0.00001 {
// skip calculations if we already have the same color
return iced::Command::none();
}

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 let ThemeType::System(t) = cosmic_theme.theme_type.clone() {
cosmic_theme.set_theme(ThemeType::System(Arc::new(t.with_accent(c))));
}
});
}
#[cfg(feature = "xdg-portal")]
Message::DesktopSettings(crate::theme::portal::Desktop::Contrast(_)) => {
// TODO when high contrast is integrated in settings and all custom themes
}
}

iced::Command::none()
Expand Down
Loading
Loading