system_theme/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
3//!
4#![warn(missing_docs, rust_2018_idioms, future_incompatible, keyword_idents)]
5
6pub mod error;
7mod integration;
8mod platform;
9mod theme;
10
11use error::Error;
12
13use async_stream::stream;
14use futures_core::stream::Stream;
15use std::hash::Hash;
16use uuid::Uuid;
17
18#[doc(inline)]
19pub use theme::{Theme, ThemeColor, ThemeContrast, ThemeKind, ThemePalette, ThemeScheme};
20
21/// System theme implementation.
22pub struct SystemTheme {
23    platform: platform::Platform,
24    identifier: Uuid,
25}
26
27impl Hash for SystemTheme {
28    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
29        self.identifier.hash(state);
30    }
31}
32
33impl SystemTheme {
34    /// Create a new instance of SystemTheme.
35    pub fn new() -> Result<Self, Error> {
36        Ok(Self {
37            platform: platform::Platform::new()?,
38            identifier: Uuid::new_v4(),
39        })
40    }
41
42    /// Get the system theme kind.
43    pub fn get_kind(&self) -> Result<ThemeKind, Error> {
44        self.platform.theme_kind()
45    }
46
47    /// Get the system theme scheme.
48    pub fn get_scheme(&self) -> Result<ThemeScheme, Error> {
49        self.platform.theme_scheme()
50    }
51
52    /// Get the system theme contrast level.
53    pub fn get_contrast(&self) -> Result<ThemeContrast, Error> {
54        self.platform.theme_contrast()
55    }
56
57    /// Get the system theme accent color.
58    pub fn get_accent(&self) -> Result<ThemeColor, Error> {
59        self.platform.theme_accent()
60    }
61
62    /// Get the system theme.
63    ///
64    /// This is based on the system theme kind, scheme, and contrast level.
65    /// A fallback color is used if the platform does not provide it.
66    pub fn get_theme(&self) -> Theme {
67        let kind = self.get_kind().unwrap_or_default();
68
69        let scheme = self.get_scheme().unwrap_or_default();
70        let contrast = self.get_contrast().unwrap_or_default();
71
72        Theme::new(kind, scheme, contrast, self.get_accent().ok())
73    }
74
75    /// Subscribe to system theme changes.
76    pub fn subscribe(&self) -> impl Stream<Item = ()> {
77        let notify = self.platform.get_notify();
78        stream! {
79            let mut notified = notify.notified();
80            loop {
81                // Wait for notification
82                notified.await;
83                // Create new notified before yielding
84                notified = notify.notified();
85                yield ();
86            }
87        }
88    }
89}