wip
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{App, Global, Pixels, SharedString, Window, px};
|
||||
|
||||
use crate::model::{
|
||||
AccentSelection, Appearance, SurfacePreference, SurfaceTreatment, ThemeRegistry,
|
||||
ThemeSelection, ThemeVariant,
|
||||
};
|
||||
use crate::resolved::{ResolvedAccentRoles, ResolvedColors};
|
||||
use crate::{NotificationSettings, PlatformKind, ScrollbarMode, sync_base};
|
||||
|
||||
impl From<gpui::WindowAppearance> for Appearance {
|
||||
fn from(appearance: gpui::WindowAppearance) -> Self {
|
||||
match appearance {
|
||||
gpui::WindowAppearance::Dark | gpui::WindowAppearance::VibrantDark => Self::Dark,
|
||||
gpui::WindowAppearance::Light | gpui::WindowAppearance::VibrantLight => Self::Light,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Theme {
|
||||
/// The active theme variant.
|
||||
pub variant: Arc<ThemeVariant>,
|
||||
|
||||
/// The active appearance (light or dark).
|
||||
pub appearance: Appearance,
|
||||
|
||||
/// The per-appearance variant selection.
|
||||
pub theme_selection: ThemeSelection,
|
||||
|
||||
/// The accent override applied on top of the variant.
|
||||
pub accent_selection: AccentSelection,
|
||||
|
||||
/// The user's surface treatment preference.
|
||||
pub surface_preference: SurfacePreference,
|
||||
|
||||
/// The surface treatment in effect for this session.
|
||||
pub surface: SurfaceTreatment,
|
||||
|
||||
/// The resolved accent roles.
|
||||
pub accent: ResolvedAccentRoles,
|
||||
|
||||
/// The resolved color snapshot.
|
||||
pub colors: ResolvedColors,
|
||||
|
||||
/// The font family for the application.
|
||||
pub font_family: SharedString,
|
||||
|
||||
/// The root font size for the application, default is 15px.
|
||||
pub font_size: Pixels,
|
||||
|
||||
/// Radius for the general elements.
|
||||
pub radius: Pixels,
|
||||
|
||||
/// Radius for the large elements, e.g.: dialog, notification.
|
||||
pub radius_lg: Pixels,
|
||||
|
||||
/// Enable shadow for the general elements. default is true
|
||||
pub shadow: bool,
|
||||
|
||||
/// Show the scrollbar mode, default: scrolling
|
||||
pub scrollbar_mode: ScrollbarMode,
|
||||
|
||||
/// Notification settings
|
||||
pub notification: NotificationSettings,
|
||||
}
|
||||
|
||||
impl Deref for Theme {
|
||||
type Target = ResolvedColors;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Theme {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl Global for Theme {}
|
||||
|
||||
impl Theme {
|
||||
/// Returns the global theme reference
|
||||
pub fn global(cx: &App) -> &Theme {
|
||||
cx.global::<Theme>()
|
||||
}
|
||||
|
||||
/// Returns the global theme mutable reference
|
||||
pub(crate) fn global_mut(cx: &mut App) -> &mut Theme {
|
||||
cx.global_mut::<Theme>()
|
||||
}
|
||||
|
||||
/// Returns true if the theme is dark.
|
||||
pub fn is_dark(&self) -> bool {
|
||||
self.appearance.is_dark()
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
let platform = PlatformKind::platform();
|
||||
let appearance = Appearance::default();
|
||||
|
||||
// Define the font family based on the platform.
|
||||
let font_family = match platform {
|
||||
PlatformKind::Linux => "Inter",
|
||||
_ => ".SystemUIFont",
|
||||
};
|
||||
|
||||
let selection = ThemeSelection::default();
|
||||
let registry = ThemeRegistry::active();
|
||||
let variant = registry.resolve(&selection, appearance).clone();
|
||||
let accent = variant.accent_for(AccentSelection::default());
|
||||
let surface = SurfacePreference::default().resolve(variant.recommended_surface_treatment);
|
||||
let resolved_accent = ResolvedAccentRoles::resolve(&accent, &variant.colors);
|
||||
let colors = ResolvedColors::resolve(&variant, &accent);
|
||||
|
||||
Self {
|
||||
variant: Arc::new(variant),
|
||||
appearance,
|
||||
theme_selection: selection,
|
||||
accent_selection: AccentSelection::default(),
|
||||
surface_preference: SurfacePreference::default(),
|
||||
surface,
|
||||
accent: resolved_accent,
|
||||
colors,
|
||||
font_family: font_family.into(),
|
||||
font_size: px(15.),
|
||||
radius: px(6.),
|
||||
radius_lg: px(10.),
|
||||
shadow: true,
|
||||
scrollbar_mode: ScrollbarMode::default(),
|
||||
notification: NotificationSettings::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync the theme with the system appearance
|
||||
pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) {
|
||||
let appearance = window
|
||||
.as_ref()
|
||||
.map(|window| window.appearance())
|
||||
.unwrap_or_else(|| cx.window_appearance());
|
||||
|
||||
Self::change(appearance, window, cx);
|
||||
}
|
||||
|
||||
/// Sync the Scrollbar showing behavior with the system
|
||||
pub fn sync_scrollbar_appearance(cx: &mut App) {
|
||||
Theme::global_mut(cx).scrollbar_mode = if cx.should_auto_hide_scrollbars() {
|
||||
ScrollbarMode::Scrolling
|
||||
} else {
|
||||
ScrollbarMode::Hover
|
||||
};
|
||||
}
|
||||
|
||||
/// Apply a theme variant by id for the current appearance.
|
||||
pub fn apply_variant(id: &str, window: Option<&mut Window>, cx: &mut App) {
|
||||
let appearance = Theme::global(cx).appearance;
|
||||
Theme::global_mut(cx)
|
||||
.theme_selection
|
||||
.set_variant(appearance, id);
|
||||
Self::change(appearance, window, cx);
|
||||
}
|
||||
|
||||
/// Replace the per-appearance variant selection.
|
||||
pub fn set_selection(selection: ThemeSelection, cx: &mut App) {
|
||||
Theme::global_mut(cx).theme_selection = selection;
|
||||
}
|
||||
|
||||
/// Replace the accent override and re-resolve the theme.
|
||||
pub fn set_accent_selection(selection: AccentSelection, cx: &mut App) {
|
||||
Theme::global_mut(cx).accent_selection = selection;
|
||||
}
|
||||
|
||||
/// Replace the surface treatment preference.
|
||||
pub fn set_surface_preference(preference: SurfacePreference, cx: &mut App) {
|
||||
Theme::global_mut(cx).surface_preference = preference;
|
||||
}
|
||||
|
||||
/// Change the app's appearance, resolving the selected variant (with its
|
||||
/// accent override) into a fresh color snapshot.
|
||||
pub fn change<M>(appearance: M, window: Option<&mut Window>, cx: &mut App)
|
||||
where
|
||||
M: Into<Appearance>,
|
||||
{
|
||||
if !cx.has_global::<Theme>() {
|
||||
cx.set_global(Theme::new());
|
||||
}
|
||||
|
||||
let appearance = appearance.into();
|
||||
let (selection, accent_selection, surface_preference) = {
|
||||
let theme = cx.global_mut::<Theme>();
|
||||
theme.appearance = appearance;
|
||||
(
|
||||
theme.theme_selection.clone(),
|
||||
theme.accent_selection,
|
||||
theme.surface_preference,
|
||||
)
|
||||
};
|
||||
|
||||
let registry = ThemeRegistry::active();
|
||||
let variant = registry.resolve(&selection, appearance).clone();
|
||||
let accent = variant.accent_for(accent_selection);
|
||||
let surface = surface_preference.resolve(variant.recommended_surface_treatment);
|
||||
let colors = ResolvedColors::resolve(&variant, &accent);
|
||||
|
||||
{
|
||||
let theme = cx.global_mut::<Theme>();
|
||||
theme.variant = Arc::new(variant.clone());
|
||||
theme.accent = ResolvedAccentRoles::resolve(&accent, &variant.colors);
|
||||
theme.surface = surface;
|
||||
theme.colors = colors;
|
||||
}
|
||||
|
||||
// Refresh the window if available
|
||||
if let Some(window) = window {
|
||||
window.refresh();
|
||||
}
|
||||
|
||||
// Keep the base-layer projection in step with the coop palette
|
||||
sync_base(cx);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user