mirror of
https://github.com/danbulant/cushy
synced 2026-07-07 12:10:44 +00:00
ThemeMode
This commit is contained in:
parent
68339dfb62
commit
27d5baef5d
11 changed files with 354 additions and 68 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -481,7 +481,7 @@ checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "figures"
|
name = "figures"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/khonsulabs/figures#3e12470aa10d36d0b38f0441a0e89ae03ccd448b"
|
source = "git+https://github.com/khonsulabs/figures#7b41393c44d4def606790e340c98450b603010b4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"euclid",
|
"euclid",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use gooey::styles::components::{TextColor, WidgetBackground};
|
||||||
use gooey::styles::{ColorSource, ColorTheme, FixedTheme, SurfaceTheme, Theme, ThemePair};
|
use gooey::styles::{ColorSource, ColorTheme, FixedTheme, SurfaceTheme, Theme, ThemePair};
|
||||||
use gooey::value::{Dynamic, MapEach};
|
use gooey::value::{Dynamic, MapEach};
|
||||||
use gooey::widget::MakeWidget;
|
use gooey::widget::MakeWidget;
|
||||||
use gooey::widgets::{Label, Scroll, Slider, Stack};
|
use gooey::widgets::{Label, Scroll, Slider, Stack, Themed};
|
||||||
|
use gooey::window::ThemeMode;
|
||||||
use gooey::Run;
|
use gooey::Run;
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
|
||||||
|
|
@ -20,6 +21,7 @@ fn main() -> gooey::Result {
|
||||||
let (neutral, neutral_editor) = color_editor(PRIMARY_HUE, 0.001, "Neutral");
|
let (neutral, neutral_editor) = color_editor(PRIMARY_HUE, 0.001, "Neutral");
|
||||||
let (neutral_variant, neutral_variant_editor) =
|
let (neutral_variant, neutral_variant_editor) =
|
||||||
color_editor(PRIMARY_HUE, 0.001, "Neutral Variant");
|
color_editor(PRIMARY_HUE, 0.001, "Neutral Variant");
|
||||||
|
let (theme_mode, theme_switcher) = dark_mode_slider();
|
||||||
|
|
||||||
let default_theme = (
|
let default_theme = (
|
||||||
&primary,
|
&primary,
|
||||||
|
|
@ -42,27 +44,49 @@ fn main() -> gooey::Result {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Stack::columns(
|
Themed::new(
|
||||||
Scroll::vertical(Stack::rows(
|
default_theme.clone(),
|
||||||
primary_editor
|
Stack::columns(
|
||||||
.and(secondary_editor)
|
Scroll::vertical(Stack::rows(
|
||||||
.and(tertiary_editor)
|
theme_switcher
|
||||||
.and(error_editor)
|
.and(primary_editor)
|
||||||
.and(neutral_editor)
|
.and(secondary_editor)
|
||||||
.and(neutral_variant_editor),
|
.and(tertiary_editor)
|
||||||
))
|
.and(error_editor)
|
||||||
.and(theme(default_theme.map_each(|theme| theme.dark), "Dark"))
|
.and(neutral_editor)
|
||||||
.and(theme(default_theme.map_each(|theme| theme.light), "Light"))
|
.and(neutral_variant_editor),
|
||||||
.and(fixed_themes(
|
))
|
||||||
default_theme.map_each(|theme| theme.primary_fixed),
|
.and(theme(default_theme.map_each(|theme| theme.dark), "Dark"))
|
||||||
default_theme.map_each(|theme| theme.secondary_fixed),
|
.and(theme(default_theme.map_each(|theme| theme.light), "Light"))
|
||||||
default_theme.map_each(|theme| theme.tertiary_fixed),
|
.and(fixed_themes(
|
||||||
)),
|
default_theme.map_each(|theme| theme.primary_fixed),
|
||||||
|
default_theme.map_each(|theme| theme.secondary_fixed),
|
||||||
|
default_theme.map_each(|theme| theme.tertiary_fixed),
|
||||||
|
)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.expand()
|
.expand()
|
||||||
|
.into_window()
|
||||||
|
.with_theme_mode(theme_mode)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dark_mode_slider() -> (Dynamic<ThemeMode>, impl MakeWidget) {
|
||||||
|
let on_off = Dynamic::new(true);
|
||||||
|
let theme_mode = on_off.map_each(|dark| {
|
||||||
|
if *dark {
|
||||||
|
ThemeMode::Dark
|
||||||
|
} else {
|
||||||
|
ThemeMode::Light
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(
|
||||||
|
theme_mode,
|
||||||
|
Stack::rows(Label::new("Theme Mode").and(Slider::<bool>::from_value(on_off))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn color_editor(
|
fn color_editor(
|
||||||
initial_hue: f32,
|
initial_hue: f32,
|
||||||
initial_saturation: impl Into<ZeroToOne>,
|
initial_saturation: impl Into<ZeroToOne>,
|
||||||
|
|
|
||||||
|
|
@ -674,6 +674,26 @@ impl LinearInterpolate for f64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LinearInterpolate for bool {
|
||||||
|
fn lerp(&self, target: &Self, percent: f32) -> Self {
|
||||||
|
if percent >= 0.5 {
|
||||||
|
*target
|
||||||
|
} else {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PercentBetween for bool {
|
||||||
|
fn percent_between(&self, min: &Self, max: &Self) -> ZeroToOne {
|
||||||
|
if *min == *max || *self == *min {
|
||||||
|
ZeroToOne::ZERO
|
||||||
|
} else {
|
||||||
|
ZeroToOne::ONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn integer_lerps() {
|
fn integer_lerps() {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
//! Types that provide access to the Gooey runtime.
|
//! Types that provide access to the Gooey runtime.
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -6,7 +7,6 @@ use std::sync::Arc;
|
||||||
use kludgine::app::winit::event::{
|
use kludgine::app::winit::event::{
|
||||||
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
|
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
|
||||||
};
|
};
|
||||||
use kludgine::app::winit::window;
|
|
||||||
use kludgine::figures::units::{Lp, Px, UPx};
|
use kludgine::figures::units::{Lp, Px, UPx};
|
||||||
use kludgine::figures::{IntoSigned, Point, Rect, ScreenScale, Size};
|
use kludgine::figures::{IntoSigned, Point, Rect, ScreenScale, Size};
|
||||||
use kludgine::shapes::{Shape, StrokeOptions};
|
use kludgine::shapes::{Shape, StrokeOptions};
|
||||||
|
|
@ -15,10 +15,10 @@ use kludgine::{Color, Kludgine};
|
||||||
use crate::graphics::Graphics;
|
use crate::graphics::Graphics;
|
||||||
use crate::styles::components::{HighlightColor, VisualOrder, WidgetBackground};
|
use crate::styles::components::{HighlightColor, VisualOrder, WidgetBackground};
|
||||||
use crate::styles::{ComponentDefaultvalue, ComponentDefinition, Styles, Theme, ThemePair};
|
use crate::styles::{ComponentDefaultvalue, ComponentDefinition, Styles, Theme, ThemePair};
|
||||||
use crate::value::Dynamic;
|
use crate::value::{Dynamic, Value};
|
||||||
use crate::widget::{EventHandling, ManagedWidget, WidgetId, WidgetInstance, WidgetRef};
|
use crate::widget::{EventHandling, ManagedWidget, WidgetId, WidgetInstance, WidgetRef};
|
||||||
use crate::window::sealed::WindowCommand;
|
use crate::window::sealed::WindowCommand;
|
||||||
use crate::window::RunningWindow;
|
use crate::window::{RunningWindow, ThemeMode};
|
||||||
use crate::ConstraintLimit;
|
use crate::ConstraintLimit;
|
||||||
|
|
||||||
/// A context to an event function.
|
/// A context to an event function.
|
||||||
|
|
@ -674,8 +674,9 @@ pub struct WidgetContext<'context, 'window> {
|
||||||
current_node: ManagedWidget,
|
current_node: ManagedWidget,
|
||||||
redraw_status: &'context RedrawStatus,
|
redraw_status: &'context RedrawStatus,
|
||||||
window: &'context mut RunningWindow<'window>,
|
window: &'context mut RunningWindow<'window>,
|
||||||
theme: &'context ThemePair,
|
theme: Cow<'context, ThemePair>,
|
||||||
pending_state: PendingState<'context>,
|
pending_state: PendingState<'context>,
|
||||||
|
theme_mode: ThemeMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'context, 'window> WidgetContext<'context, 'window> {
|
impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
|
|
@ -684,6 +685,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
redraw_status: &'context RedrawStatus,
|
redraw_status: &'context RedrawStatus,
|
||||||
theme: &'context ThemePair,
|
theme: &'context ThemePair,
|
||||||
window: &'context mut RunningWindow<'window>,
|
window: &'context mut RunningWindow<'window>,
|
||||||
|
theme_mode: ThemeMode,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pending_state: PendingState::Owned(PendingWidgetState {
|
pending_state: PendingState::Owned(PendingWidgetState {
|
||||||
|
|
@ -698,7 +700,8 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
}),
|
}),
|
||||||
current_node,
|
current_node,
|
||||||
redraw_status,
|
redraw_status,
|
||||||
theme,
|
theme: Cow::Borrowed(theme),
|
||||||
|
theme_mode,
|
||||||
window,
|
window,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -709,8 +712,9 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
current_node: self.current_node.clone(),
|
current_node: self.current_node.clone(),
|
||||||
redraw_status: self.redraw_status,
|
redraw_status: self.redraw_status,
|
||||||
window: &mut *self.window,
|
window: &mut *self.window,
|
||||||
theme: self.theme,
|
theme: Cow::Borrowed(self.theme.as_ref()),
|
||||||
pending_state: self.pending_state.borrowed(),
|
pending_state: self.pending_state.borrowed(),
|
||||||
|
theme_mode: self.theme_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -723,12 +727,26 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
Widget: ManageWidget,
|
Widget: ManageWidget,
|
||||||
Widget::Managed: MapManagedWidget<WidgetContext<'child, 'window>>,
|
Widget::Managed: MapManagedWidget<WidgetContext<'child, 'window>>,
|
||||||
{
|
{
|
||||||
widget.manage(self).map(|current_node| WidgetContext {
|
widget.manage(self).map(|current_node| {
|
||||||
current_node,
|
let (theme, theme_mode) = current_node.overidden_theme();
|
||||||
redraw_status: self.redraw_status,
|
let theme = if let Some(theme) = theme {
|
||||||
window: &mut *self.window,
|
Cow::Owned(theme.get_tracked(self))
|
||||||
theme: self.theme,
|
} else {
|
||||||
pending_state: self.pending_state.borrowed(),
|
Cow::Borrowed(self.theme.as_ref())
|
||||||
|
};
|
||||||
|
let theme_mode = if let Some(mode) = theme_mode {
|
||||||
|
mode.get_tracked(self)
|
||||||
|
} else {
|
||||||
|
self.theme_mode
|
||||||
|
};
|
||||||
|
WidgetContext {
|
||||||
|
current_node,
|
||||||
|
redraw_status: self.redraw_status,
|
||||||
|
window: &mut *self.window,
|
||||||
|
theme,
|
||||||
|
pending_state: self.pending_state.borrowed(),
|
||||||
|
theme_mode,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -872,10 +890,24 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
///
|
///
|
||||||
/// Style queries for children will return any values matching this
|
/// Style queries for children will return any values matching this
|
||||||
/// collection.
|
/// collection.
|
||||||
pub fn attach_styles(&self, styles: Styles) {
|
pub fn attach_styles(&self, styles: Value<Styles>) {
|
||||||
self.current_node.attach_styles(styles);
|
self.current_node.attach_styles(styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attaches `theme` to the widget hierarchy for this widget.
|
||||||
|
///
|
||||||
|
/// All children nodes will access this theme in their contexts.
|
||||||
|
pub fn attach_theme(&self, theme: Value<ThemePair>) {
|
||||||
|
self.current_node.attach_theme(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attaches `theme_mode` to the widget hierarchy for this widget.
|
||||||
|
///
|
||||||
|
/// All children nodes will use this theme mode.
|
||||||
|
pub fn attach_theme_mode(&self, theme_mode: Value<ThemeMode>) {
|
||||||
|
self.current_node.attach_theme_mode(theme_mode);
|
||||||
|
}
|
||||||
|
|
||||||
/// Queries the widget hierarchy for matching style components.
|
/// Queries the widget hierarchy for matching style components.
|
||||||
///
|
///
|
||||||
/// This function traverses up the widget hierarchy looking for the
|
/// This function traverses up the widget hierarchy looking for the
|
||||||
|
|
@ -890,7 +922,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
pub fn query_styles(&self, query: &[&dyn ComponentDefaultvalue]) -> Styles {
|
pub fn query_styles(&self, query: &[&dyn ComponentDefaultvalue]) -> Styles {
|
||||||
self.current_node
|
self.current_node
|
||||||
.tree
|
.tree
|
||||||
.query_styles(&self.current_node, query)
|
.query_styles(&self.current_node, query, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queries the widget hierarchy for a single style component.
|
/// Queries the widget hierarchy for a single style component.
|
||||||
|
|
@ -931,24 +963,24 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
|
||||||
/// Returns the theme pair for the window.
|
/// Returns the theme pair for the window.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn theme_pair(&self) -> &ThemePair {
|
pub fn theme_pair(&self) -> &ThemePair {
|
||||||
self.theme
|
self.theme.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current theme in either light or dark mode.
|
/// Returns the current theme in either light or dark mode.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn theme(&self) -> &Theme {
|
pub fn theme(&self) -> &Theme {
|
||||||
match self.window.theme() {
|
match self.theme_mode {
|
||||||
window::Theme::Light => &self.theme.light,
|
ThemeMode::Light => &self.theme.light,
|
||||||
window::Theme::Dark => &self.theme.dark,
|
ThemeMode::Dark => &self.theme.dark,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the opposite theme of [`Self::theme()`].
|
/// Returns the opposite theme of [`Self::theme()`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn inverse_theme(&self) -> &Theme {
|
pub fn inverse_theme(&self) -> &Theme {
|
||||||
match self.window.theme() {
|
match self.theme_mode {
|
||||||
window::Theme::Light => &self.theme.dark,
|
ThemeMode::Light => &self.theme.dark,
|
||||||
window::Theme::Dark => &self.theme.light,
|
ThemeMode::Dark => &self.theme.light,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
80
src/tree.rs
80
src/tree.rs
|
|
@ -7,8 +7,10 @@ use kludgine::figures::{Point, Rect};
|
||||||
|
|
||||||
use crate::context::WidgetContext;
|
use crate::context::WidgetContext;
|
||||||
use crate::styles::components::VisualOrder;
|
use crate::styles::components::VisualOrder;
|
||||||
use crate::styles::{ComponentDefaultvalue, ComponentDefinition, ComponentType, Styles};
|
use crate::styles::{ComponentDefaultvalue, ComponentDefinition, ComponentType, Styles, ThemePair};
|
||||||
|
use crate::value::Value;
|
||||||
use crate::widget::{ManagedWidget, WidgetId, WidgetInstance};
|
use crate::widget::{ManagedWidget, WidgetId, WidgetInstance};
|
||||||
|
use crate::window::ThemeMode;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Tree {
|
pub struct Tree {
|
||||||
|
|
@ -31,6 +33,8 @@ impl Tree {
|
||||||
parent: parent.map(ManagedWidget::id),
|
parent: parent.map(ManagedWidget::id),
|
||||||
layout: None,
|
layout: None,
|
||||||
styles: None,
|
styles: None,
|
||||||
|
theme: None,
|
||||||
|
theme_mode: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if widget.is_default() {
|
if widget.is_default() {
|
||||||
|
|
@ -277,20 +281,47 @@ impl Tree {
|
||||||
data.nodes.get(&id).expect("missing widget").parent
|
data.nodes.get(&id).expect("missing widget").parent
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_styles(&self, id: WidgetId, styles: Styles) {
|
pub(crate) fn attach_styles(&self, id: WidgetId, styles: Value<Styles>) {
|
||||||
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
data.nodes.get_mut(&id).expect("missing widget").styles = Some(styles);
|
data.nodes.get_mut(&id).expect("missing widget").styles = Some(styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn attach_theme(&self, id: WidgetId, theme: Value<ThemePair>) {
|
||||||
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
|
data.nodes.get_mut(&id).expect("missing widget").theme = Some(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn attach_theme_mode(&self, id: WidgetId, theme: Value<ThemeMode>) {
|
||||||
|
let mut data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
|
data.nodes.get_mut(&id).expect("missing widget").theme_mode = Some(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn overriden_theme(
|
||||||
|
&self,
|
||||||
|
id: WidgetId,
|
||||||
|
) -> (Option<Value<ThemePair>>, Option<Value<ThemeMode>>) {
|
||||||
|
let data = self.data.lock().map_or_else(PoisonError::into_inner, |g| g);
|
||||||
|
|
||||||
|
(
|
||||||
|
data.nodes.get(&id).expect("missing widget").theme.clone(),
|
||||||
|
data.nodes
|
||||||
|
.get(&id)
|
||||||
|
.expect("missing widget")
|
||||||
|
.theme_mode
|
||||||
|
.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn query_styles(
|
pub fn query_styles(
|
||||||
&self,
|
&self,
|
||||||
perspective: &ManagedWidget,
|
perspective: &ManagedWidget,
|
||||||
query: &[&dyn ComponentDefaultvalue],
|
query: &[&dyn ComponentDefaultvalue],
|
||||||
|
context: &WidgetContext<'_, '_>,
|
||||||
) -> Styles {
|
) -> Styles {
|
||||||
self.data
|
self.data
|
||||||
.lock()
|
.lock()
|
||||||
.map_or_else(PoisonError::into_inner, |g| g)
|
.map_or_else(PoisonError::into_inner, |g| g)
|
||||||
.query_styles(perspective.id(), query)
|
.query_styles(perspective.id(), query, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_style<Component: ComponentDefinition>(
|
pub fn query_style<Component: ComponentDefinition>(
|
||||||
|
|
@ -391,19 +422,22 @@ impl TreeData {
|
||||||
&self,
|
&self,
|
||||||
mut perspective: WidgetId,
|
mut perspective: WidgetId,
|
||||||
query: &[&dyn ComponentDefaultvalue],
|
query: &[&dyn ComponentDefaultvalue],
|
||||||
|
context: &WidgetContext<'_, '_>,
|
||||||
) -> Styles {
|
) -> Styles {
|
||||||
let mut query = query.iter().map(|n| n.name()).collect::<Vec<_>>();
|
let mut query = query.iter().map(|n| n.name()).collect::<Vec<_>>();
|
||||||
let mut resolved = Styles::new();
|
let mut resolved = Styles::new();
|
||||||
while !query.is_empty() {
|
while !query.is_empty() {
|
||||||
let node = &self.nodes[&perspective];
|
let node = &self.nodes[&perspective];
|
||||||
if let Some(styles) = &node.styles {
|
if let Some(styles) = &node.styles {
|
||||||
query.retain(|name| {
|
styles.map_tracked(context, |styles| {
|
||||||
if let Some(component) = styles.get_named(name) {
|
query.retain(|name| {
|
||||||
resolved.insert(name, component.clone());
|
if let Some(component) = styles.get_named(name) {
|
||||||
false
|
resolved.insert(name, component.clone());
|
||||||
} else {
|
false
|
||||||
true
|
} else {
|
||||||
}
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let Some(parent) = node.parent else { break };
|
let Some(parent) = node.parent else { break };
|
||||||
|
|
@ -422,13 +456,21 @@ impl TreeData {
|
||||||
loop {
|
loop {
|
||||||
let node = &self.nodes[&perspective];
|
let node = &self.nodes[&perspective];
|
||||||
if let Some(styles) = &node.styles {
|
if let Some(styles) = &node.styles {
|
||||||
if let Some(component) = styles.get_named(&name) {
|
match styles.map_tracked(context, |styles| {
|
||||||
let Ok(value) = <Component::ComponentType>::try_from_component(component.get())
|
if let Some(component) = styles.get_named(&name) {
|
||||||
else {
|
let Ok(value) =
|
||||||
break;
|
<Component::ComponentType>::try_from_component(component.get())
|
||||||
};
|
else {
|
||||||
component.redraw_when_changed(context);
|
return Err(());
|
||||||
return value;
|
};
|
||||||
|
component.redraw_when_changed(context);
|
||||||
|
return Ok(Some(value));
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}) {
|
||||||
|
Ok(Some(value)) => return value,
|
||||||
|
Ok(None) => {}
|
||||||
|
Err(()) => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let Some(parent) = node.parent else { break };
|
let Some(parent) = node.parent else { break };
|
||||||
|
|
@ -443,5 +485,7 @@ pub struct Node {
|
||||||
pub children: Vec<WidgetId>,
|
pub children: Vec<WidgetId>,
|
||||||
pub parent: Option<WidgetId>,
|
pub parent: Option<WidgetId>,
|
||||||
pub layout: Option<Rect<Px>>,
|
pub layout: Option<Rect<Px>>,
|
||||||
pub styles: Option<Styles>,
|
pub styles: Option<Value<Styles>>,
|
||||||
|
pub theme: Option<Value<ThemePair>>,
|
||||||
|
pub theme_mode: Option<Value<ThemeMode>>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@ use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, Size};
|
||||||
|
|
||||||
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext};
|
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext};
|
||||||
use crate::styles::components::VisualOrder;
|
use crate::styles::components::VisualOrder;
|
||||||
use crate::styles::{IntoComponentValue, NamedComponent, Styles};
|
use crate::styles::{IntoComponentValue, NamedComponent, Styles, ThemePair};
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
use crate::value::{IntoValue, Value};
|
use crate::value::{IntoValue, Value};
|
||||||
use crate::widgets::{Align, Expand, Scroll, Style};
|
use crate::widgets::{Align, Expand, Scroll, Style};
|
||||||
use crate::window::{RunningWindow, Window, WindowBehavior};
|
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior};
|
||||||
use crate::{ConstraintLimit, Run};
|
use crate::{ConstraintLimit, Run};
|
||||||
|
|
||||||
/// A type that makes up a graphical user interface.
|
/// A type that makes up a graphical user interface.
|
||||||
|
|
@ -448,7 +448,7 @@ pub trait MakeWidget: Sized {
|
||||||
/// Associates `styles` with this widget.
|
/// Associates `styles` with this widget.
|
||||||
///
|
///
|
||||||
/// This is equivalent to `Style::new(styles, self)`.
|
/// This is equivalent to `Style::new(styles, self)`.
|
||||||
fn with_styles(self, styles: impl Into<Styles>) -> Style
|
fn with_styles(self, styles: impl IntoValue<Styles>) -> Style
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
|
|
@ -951,10 +951,22 @@ impl ManagedWidget {
|
||||||
self.tree.parent(self.id()).is_some()
|
self.tree.parent(self.id()).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn attach_styles(&self, styles: Styles) {
|
pub(crate) fn attach_styles(&self, styles: Value<Styles>) {
|
||||||
self.tree.attach_styles(self.id(), styles);
|
self.tree.attach_styles(self.id(), styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn attach_theme(&self, theme: Value<ThemePair>) {
|
||||||
|
self.tree.attach_theme(self.id(), theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn attach_theme_mode(&self, theme: Value<ThemeMode>) {
|
||||||
|
self.tree.attach_theme_mode(self.id(), theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn overidden_theme(&self) -> (Option<Value<ThemePair>>, Option<Value<ThemeMode>>) {
|
||||||
|
self.tree.overriden_theme(self.id())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn reset_child_layouts(&self) {
|
pub(crate) fn reset_child_layouts(&self) {
|
||||||
self.tree.reset_child_layouts(self.id());
|
self.tree.reset_child_layouts(self.id());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@ mod canvas;
|
||||||
mod expand;
|
mod expand;
|
||||||
mod input;
|
mod input;
|
||||||
pub mod label;
|
pub mod label;
|
||||||
|
mod mode_switch;
|
||||||
mod resize;
|
mod resize;
|
||||||
pub mod scroll;
|
pub mod scroll;
|
||||||
mod slider;
|
mod slider;
|
||||||
mod space;
|
mod space;
|
||||||
pub mod stack;
|
pub mod stack;
|
||||||
mod style;
|
mod style;
|
||||||
|
mod themed;
|
||||||
mod tilemap;
|
mod tilemap;
|
||||||
|
|
||||||
pub use align::Align;
|
pub use align::Align;
|
||||||
|
|
@ -20,10 +22,12 @@ pub use canvas::Canvas;
|
||||||
pub use expand::Expand;
|
pub use expand::Expand;
|
||||||
pub use input::Input;
|
pub use input::Input;
|
||||||
pub use label::Label;
|
pub use label::Label;
|
||||||
|
pub use mode_switch::ModeSwitch;
|
||||||
pub use resize::Resize;
|
pub use resize::Resize;
|
||||||
pub use scroll::Scroll;
|
pub use scroll::Scroll;
|
||||||
pub use slider::Slider;
|
pub use slider::Slider;
|
||||||
pub use space::Space;
|
pub use space::Space;
|
||||||
pub use stack::Stack;
|
pub use stack::Stack;
|
||||||
pub use style::Style;
|
pub use style::Style;
|
||||||
|
pub use themed::Themed;
|
||||||
pub use tilemap::TileMap;
|
pub use tilemap::TileMap;
|
||||||
|
|
|
||||||
31
src/widgets/mode_switch.rs
Normal file
31
src/widgets/mode_switch.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::context::EventContext;
|
||||||
|
use crate::value::{IntoValue, Value};
|
||||||
|
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||||
|
use crate::window::ThemeMode;
|
||||||
|
|
||||||
|
/// A widget that applies a set of [`Styles`] to all contained widgets.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ModeSwitch {
|
||||||
|
mode: Value<ThemeMode>,
|
||||||
|
child: WidgetRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModeSwitch {
|
||||||
|
/// Returns a new widget that applies `mode` to all of its children.
|
||||||
|
pub fn new(mode: impl IntoValue<ThemeMode>, child: impl MakeWidget) -> Self {
|
||||||
|
Self {
|
||||||
|
mode: mode.into_value(),
|
||||||
|
child: WidgetRef::new(child),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WrapperWidget for ModeSwitch {
|
||||||
|
fn child_mut(&mut self) -> &mut WidgetRef {
|
||||||
|
&mut self.child
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mounted(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
|
context.attach_theme_mode(self.mode.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
use crate::context::EventContext;
|
use crate::context::EventContext;
|
||||||
use crate::styles::Styles;
|
use crate::styles::Styles;
|
||||||
|
use crate::value::{IntoValue, Value};
|
||||||
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||||
|
|
||||||
/// A widget that applies a set of [`Styles`] to all contained widgets.
|
/// A widget that applies a set of [`Styles`] to all contained widgets.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
styles: Styles,
|
styles: Value<Styles>,
|
||||||
child: WidgetRef,
|
child: WidgetRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
/// Returns a new widget that applies `styles` to `child` and any children
|
/// Returns a new widget that applies `styles` to `child` and any children
|
||||||
/// it may have.
|
/// it may have.
|
||||||
pub fn new(styles: impl Into<Styles>, child: impl MakeWidget) -> Self {
|
pub fn new(styles: impl IntoValue<Styles>, child: impl MakeWidget) -> Self {
|
||||||
Self {
|
Self {
|
||||||
styles: styles.into(),
|
styles: styles.into_value(),
|
||||||
child: WidgetRef::new(child),
|
child: WidgetRef::new(child),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
src/widgets/themed.rs
Normal file
31
src/widgets/themed.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::context::EventContext;
|
||||||
|
use crate::styles::ThemePair;
|
||||||
|
use crate::value::{IntoValue, Value};
|
||||||
|
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||||
|
|
||||||
|
/// A widget that applies a set of [`Styles`] to all contained widgets.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Themed {
|
||||||
|
theme: Value<ThemePair>,
|
||||||
|
child: WidgetRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Themed {
|
||||||
|
/// Returns a new widget that applies `theme` to all of its children.
|
||||||
|
pub fn new(theme: impl IntoValue<ThemePair>, child: impl MakeWidget) -> Self {
|
||||||
|
Self {
|
||||||
|
theme: theme.into_value(),
|
||||||
|
child: WidgetRef::new(child),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WrapperWidget for Themed {
|
||||||
|
fn child_mut(&mut self) -> &mut WidgetRef {
|
||||||
|
&mut self.child
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mounted(&mut self, context: &mut EventContext<'_, '_>) {
|
||||||
|
context.attach_theme(self.theme.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ use kludgine::app::winit::event::{
|
||||||
DeviceId, ElementState, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
|
DeviceId, ElementState, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
|
||||||
};
|
};
|
||||||
use kludgine::app::winit::keyboard::Key;
|
use kludgine::app::winit::keyboard::Key;
|
||||||
|
use kludgine::app::winit::window;
|
||||||
use kludgine::app::WindowBehavior as _;
|
use kludgine::app::WindowBehavior as _;
|
||||||
use kludgine::figures::units::{Px, UPx};
|
use kludgine::figures::units::{Px, UPx};
|
||||||
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size};
|
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size};
|
||||||
|
|
@ -32,7 +33,7 @@ use crate::styles::components::LayoutOrder;
|
||||||
use crate::styles::ThemePair;
|
use crate::styles::ThemePair;
|
||||||
use crate::tree::Tree;
|
use crate::tree::Tree;
|
||||||
use crate::utils::ModifiersExt;
|
use crate::utils::ModifiersExt;
|
||||||
use crate::value::{Dynamic, DynamicReader, IntoDynamic, Value};
|
use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value};
|
||||||
use crate::widget::{
|
use crate::widget::{
|
||||||
EventHandling, ManagedWidget, Widget, WidgetId, WidgetInstance, HANDLED, IGNORED,
|
EventHandling, ManagedWidget, Widget, WidgetId, WidgetInstance, HANDLED, IGNORED,
|
||||||
};
|
};
|
||||||
|
|
@ -105,6 +106,7 @@ where
|
||||||
pub theme: Value<ThemePair>,
|
pub theme: Value<ThemePair>,
|
||||||
occluded: Option<Dynamic<bool>>,
|
occluded: Option<Dynamic<bool>>,
|
||||||
focused: Option<Dynamic<bool>>,
|
focused: Option<Dynamic<bool>>,
|
||||||
|
theme_mode: Option<Value<ThemeMode>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Behavior> Default for Window<Behavior>
|
impl<Behavior> Default for Window<Behavior>
|
||||||
|
|
@ -156,6 +158,24 @@ impl Window<WidgetInstance> {
|
||||||
self.occluded = Some(occluded);
|
self.occluded = Some(occluded);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the [`ThemeMode`] for this window.
|
||||||
|
///
|
||||||
|
/// If a [`ThemeMode`] is provided, the window will be set to this theme
|
||||||
|
/// mode upon creation and will not be updated while the window is running.
|
||||||
|
///
|
||||||
|
/// If a [`Dynamic`] is provided, the initial value will be ignored and the
|
||||||
|
/// dynamic will be updated when the window opens with the user's current
|
||||||
|
/// theme mode. The dynamic will also be updated any time the user's theme
|
||||||
|
/// mode changes.
|
||||||
|
///
|
||||||
|
/// Setting the [`Dynamic`]'s value will also update the window with the new
|
||||||
|
/// mode until a mode change is detected, upon which the new mode will be
|
||||||
|
/// stored.
|
||||||
|
pub fn with_theme_mode(mut self, theme_mode: impl IntoValue<ThemeMode>) -> Self {
|
||||||
|
self.theme_mode = Some(theme_mode.into_value());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Behavior> Window<Behavior>
|
impl<Behavior> Window<Behavior>
|
||||||
|
|
@ -189,6 +209,7 @@ where
|
||||||
theme: Value::default(),
|
theme: Value::default(),
|
||||||
occluded: None,
|
occluded: None,
|
||||||
focused: None,
|
focused: None,
|
||||||
|
theme_mode: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -207,6 +228,7 @@ where
|
||||||
occluded: self.occluded,
|
occluded: self.occluded,
|
||||||
focused: self.focused,
|
focused: self.focused,
|
||||||
theme: Some(self.theme),
|
theme: Some(self.theme),
|
||||||
|
theme_mode: self.theme_mode,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
@ -260,6 +282,7 @@ struct GooeyWindow<T> {
|
||||||
max_inner_size: Option<Size<UPx>>,
|
max_inner_size: Option<Size<UPx>>,
|
||||||
theme: Option<DynamicReader<ThemePair>>,
|
theme: Option<DynamicReader<ThemePair>>,
|
||||||
current_theme: ThemePair,
|
current_theme: ThemePair,
|
||||||
|
theme_mode: Dynamic<ThemeMode>,
|
||||||
transparent: bool,
|
transparent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,6 +312,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -300,6 +324,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -313,6 +338,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
window,
|
window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -408,6 +434,14 @@ where
|
||||||
.theme
|
.theme
|
||||||
.take()
|
.take()
|
||||||
.expect("theme always present");
|
.expect("theme always present");
|
||||||
|
|
||||||
|
let theme_mode = match context.settings.borrow_mut().theme_mode.take() {
|
||||||
|
Some(Value::Dynamic(dynamic)) => {
|
||||||
|
dynamic.update(window.theme().into());
|
||||||
|
dynamic
|
||||||
|
}
|
||||||
|
Some(Value::Constant(_)) | None => Dynamic::new(window.theme().into()),
|
||||||
|
};
|
||||||
let transparent = context.settings.borrow().transparent;
|
let transparent = context.settings.borrow().transparent;
|
||||||
let mut behavior = T::initialize(
|
let mut behavior = T::initialize(
|
||||||
&mut RunningWindow::new(window, &focused, &occluded),
|
&mut RunningWindow::new(window, &focused, &occluded),
|
||||||
|
|
@ -439,6 +473,7 @@ where
|
||||||
max_inner_size: None,
|
max_inner_size: None,
|
||||||
current_theme,
|
current_theme,
|
||||||
theme,
|
theme,
|
||||||
|
theme_mode,
|
||||||
transparent,
|
transparent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -472,9 +507,11 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
gfx: Exclusive::Owned(Graphics::new(graphics)),
|
gfx: Exclusive::Owned(Graphics::new(graphics)),
|
||||||
};
|
};
|
||||||
|
context.redraw_when_changed(&self.theme_mode);
|
||||||
let mut layout_context = LayoutContext::new(&mut context);
|
let mut layout_context = LayoutContext::new(&mut context);
|
||||||
let window_size = layout_context.gfx.size();
|
let window_size = layout_context.gfx.size();
|
||||||
|
|
||||||
|
|
@ -557,12 +594,16 @@ where
|
||||||
fn initial_window_attributes(
|
fn initial_window_attributes(
|
||||||
context: &Self::Context,
|
context: &Self::Context,
|
||||||
) -> kludgine::app::WindowAttributes<WindowCommand> {
|
) -> kludgine::app::WindowAttributes<WindowCommand> {
|
||||||
context
|
let mut attrs = context
|
||||||
.settings
|
.settings
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.attributes
|
.attributes
|
||||||
.take()
|
.take()
|
||||||
.expect("called more than once")
|
.expect("called more than once");
|
||||||
|
if let Some(Value::Constant(theme_mode)) = &context.settings.borrow().theme_mode {
|
||||||
|
attrs.preferred_theme = Some((*theme_mode).into());
|
||||||
|
}
|
||||||
|
attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_requested(
|
fn close_requested(
|
||||||
|
|
@ -636,6 +677,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -665,6 +707,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -731,6 +774,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -765,6 +809,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -793,6 +838,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -807,6 +853,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -847,6 +894,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -871,6 +919,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
)
|
)
|
||||||
|
|
@ -886,6 +935,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
),
|
),
|
||||||
|
|
@ -920,6 +970,7 @@ where
|
||||||
&self.redraw_status,
|
&self.redraw_status,
|
||||||
&self.current_theme,
|
&self.current_theme,
|
||||||
&mut window,
|
&mut window,
|
||||||
|
self.theme_mode.get(),
|
||||||
),
|
),
|
||||||
kludgine,
|
kludgine,
|
||||||
);
|
);
|
||||||
|
|
@ -937,6 +988,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn theme_changed(
|
||||||
|
&mut self,
|
||||||
|
window: kludgine::app::Window<'_, WindowCommand>,
|
||||||
|
_kludgine: &mut Kludgine,
|
||||||
|
) {
|
||||||
|
self.theme_mode.update(window.theme().into());
|
||||||
|
}
|
||||||
|
|
||||||
fn event(
|
fn event(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut window: kludgine::app::Window<'_, WindowCommand>,
|
mut window: kludgine::app::Window<'_, WindowCommand>,
|
||||||
|
|
@ -975,7 +1034,7 @@ pub(crate) mod sealed {
|
||||||
|
|
||||||
use crate::styles::ThemePair;
|
use crate::styles::ThemePair;
|
||||||
use crate::value::{Dynamic, Value};
|
use crate::value::{Dynamic, Value};
|
||||||
use crate::window::WindowAttributes;
|
use crate::window::{ThemeMode, WindowAttributes};
|
||||||
|
|
||||||
pub struct Context<C> {
|
pub struct Context<C> {
|
||||||
pub user: C,
|
pub user: C,
|
||||||
|
|
@ -987,6 +1046,7 @@ pub(crate) mod sealed {
|
||||||
pub occluded: Option<Dynamic<bool>>,
|
pub occluded: Option<Dynamic<bool>>,
|
||||||
pub focused: Option<Dynamic<bool>>,
|
pub focused: Option<Dynamic<bool>>,
|
||||||
pub theme: Option<Value<ThemePair>>,
|
pub theme: Option<Value<ThemePair>>,
|
||||||
|
pub theme_mode: Option<Value<ThemeMode>>,
|
||||||
pub transparent: bool,
|
pub transparent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -995,3 +1055,30 @@ pub(crate) mod sealed {
|
||||||
// RequestClose,
|
// RequestClose,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Controls whether the light or dark theme is applied.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub enum ThemeMode {
|
||||||
|
/// Applies the light theme
|
||||||
|
Light,
|
||||||
|
/// Applies the dark theme
|
||||||
|
Dark,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<window::Theme> for ThemeMode {
|
||||||
|
fn from(value: window::Theme) -> Self {
|
||||||
|
match value {
|
||||||
|
window::Theme::Light => Self::Light,
|
||||||
|
window::Theme::Dark => Self::Dark,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ThemeMode> for window::Theme {
|
||||||
|
fn from(value: ThemeMode) -> Self {
|
||||||
|
match value {
|
||||||
|
ThemeMode::Light => Self::Light,
|
||||||
|
ThemeMode::Dark => Self::Dark,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue