Make Light & Dark Visuals Customizable When Following The System Theme (#4744)
* Closes <https://github.com/emilk/egui/issues/4490> * [x] I have followed the instructions in the PR template --- Unfortunately, this PR contains a bunch of breaking changes because `Context` no longer has one style, but two. I could try to add some of the methods back if that's desired. The most subtle change is probably that `style_mut` mutates both the dark and the light style (which from the usage in egui itself felt like the right choice but might be surprising to users). I decided to deviate a bit from the data structure suggested in the linked issue. Instead of this: ```rust pub theme: Theme, // Dark or Light pub follow_system_theme: bool, // Change [`Self::theme`] based on `RawInput::system_theme`? ``` I decided to add a `ThemePreference` enum and track the current system theme separately. This has a couple of benefits: * The user's theme choice is not magically overwritten on the next frame. * A widget for changing the theme preference only needs to know the `ThemePreference` and not two values. * Persisting the `theme_preference` is fine (as opposed to persisting the `theme` field which may actually be the system theme). The `small_toggle_button` currently only toggles between dark and light (so you can never get back to following the system). I think it's easy to improve on this in a follow-up PR :) I made the function `pub(crate)` for now because it should eventually be a method on `ThemePreference`, not `Theme`. To showcase the new capabilities I added a new example that uses different "accent" colors in dark and light mode: <img src="https://github.com/user-attachments/assets/0bf728c6-2720-47b0-a908-18bd250d15a6" width="250" alt="A screenshot of egui's widget gallery demo in dark mode using a purple accent color instead of the default blue accent"> <img src="https://github.com/user-attachments/assets/e816b380-3e59-4f11-b841-8c20285988d6" width="250" alt="A screenshot of egui's widget gallery demo in light mode using a green accent color instead of the default blue accent"> --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
f4697bc007
commit
b5627c7d40
11
Cargo.lock
11
Cargo.lock
|
|
@ -1052,6 +1052,17 @@ dependencies = [
|
||||||
"env_logger",
|
"env_logger",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "custom_style"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"eframe",
|
||||||
|
"egui_demo_lib",
|
||||||
|
"egui_extras",
|
||||||
|
"env_logger",
|
||||||
|
"image",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "custom_window_frame"
|
name = "custom_window_frame"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ pub struct CreationContext<'s> {
|
||||||
/// The egui Context.
|
/// The egui Context.
|
||||||
///
|
///
|
||||||
/// You can use this to customize the look of egui, e.g to call [`egui::Context::set_fonts`],
|
/// You can use this to customize the look of egui, e.g to call [`egui::Context::set_fonts`],
|
||||||
/// [`egui::Context::set_visuals`] etc.
|
/// [`egui::Context::set_visuals_of`] etc.
|
||||||
pub egui_ctx: egui::Context,
|
pub egui_ctx: egui::Context,
|
||||||
|
|
||||||
/// Information about the surrounding environment.
|
/// Information about the surrounding environment.
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use crate::{
|
||||||
layers::GraphicLayers,
|
layers::GraphicLayers,
|
||||||
load,
|
load,
|
||||||
load::{Bytes, Loaders, SizedTexture},
|
load::{Bytes, Loaders, SizedTexture},
|
||||||
memory::Options,
|
memory::{Options, Theme},
|
||||||
menu,
|
menu,
|
||||||
os::OperatingSystem,
|
os::OperatingSystem,
|
||||||
output::FullOutput,
|
output::FullOutput,
|
||||||
|
|
@ -487,7 +487,7 @@ impl ContextImpl {
|
||||||
});
|
});
|
||||||
|
|
||||||
viewport.hits = if let Some(pos) = viewport.input.pointer.interact_pos() {
|
viewport.hits = if let Some(pos) = viewport.input.pointer.interact_pos() {
|
||||||
let interact_radius = self.memory.options.style.interaction.interact_radius;
|
let interact_radius = self.memory.options.style().interaction.interact_radius;
|
||||||
|
|
||||||
crate::hit_test::hit_test(
|
crate::hit_test::hit_test(
|
||||||
&viewport.prev_frame.widgets,
|
&viewport.prev_frame.widgets,
|
||||||
|
|
@ -583,7 +583,7 @@ impl ContextImpl {
|
||||||
crate::profile_scope!("preload_font_glyphs");
|
crate::profile_scope!("preload_font_glyphs");
|
||||||
// Preload the most common characters for the most common fonts.
|
// Preload the most common characters for the most common fonts.
|
||||||
// This is not very important to do, but may save a few GPU operations.
|
// This is not very important to do, but may save a few GPU operations.
|
||||||
for font_id in self.memory.options.style.text_styles.values() {
|
for font_id in self.memory.options.style().text_styles.values() {
|
||||||
fonts.lock().fonts.font(font_id).preload_common_characters();
|
fonts.lock().fonts.font(font_id).preload_common_characters();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1245,7 +1245,7 @@ impl Context {
|
||||||
pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
|
pub fn register_widget_info(&self, id: Id, make_info: impl Fn() -> crate::WidgetInfo) {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
self.write(|ctx| {
|
self.write(|ctx| {
|
||||||
if ctx.memory.options.style.debug.show_interactive_widgets {
|
if ctx.memory.options.style().debug.show_interactive_widgets {
|
||||||
ctx.viewport().this_frame.widgets.set_info(id, make_info());
|
ctx.viewport().this_frame.widgets.set_info(id, make_info());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1612,12 +1612,37 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`Style`] used by all subsequent windows, panels etc.
|
/// Does the OS use dark or light mode?
|
||||||
pub fn style(&self) -> Arc<Style> {
|
/// This is used when the theme preference is set to [`crate::ThemePreference::System`].
|
||||||
self.options(|opt| opt.style.clone())
|
pub fn system_theme(&self) -> Option<Theme> {
|
||||||
|
self.memory(|mem| mem.options.system_theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutate the [`Style`] used by all subsequent windows, panels etc.
|
/// The [`Theme`] used to select the appropriate [`Style`] (dark or light)
|
||||||
|
/// used by all subsequent windows, panels etc.
|
||||||
|
pub fn theme(&self) -> Theme {
|
||||||
|
self.options(|opt| opt.theme())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`Theme`] used to select between dark and light [`Self::style`]
|
||||||
|
/// as the active style used by all subsequent windows, panels etc.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// # let mut ctx = egui::Context::default();
|
||||||
|
/// ctx.set_theme(egui::Theme::Light); // Switch to light mode
|
||||||
|
/// ```
|
||||||
|
pub fn set_theme(&self, theme_preference: impl Into<crate::ThemePreference>) {
|
||||||
|
self.options_mut(|opt| opt.theme_preference = theme_preference.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The currently active [`Style`] used by all subsequent windows, panels etc.
|
||||||
|
pub fn style(&self) -> Arc<Style> {
|
||||||
|
self.options(|opt| opt.style().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutate the currently active [`Style`] used by all subsequent windows, panels etc.
|
||||||
|
/// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -1627,16 +1652,72 @@ impl Context {
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
|
pub fn style_mut(&self, mutate_style: impl FnOnce(&mut Style)) {
|
||||||
self.options_mut(|opt| mutate_style(std::sync::Arc::make_mut(&mut opt.style)));
|
self.options_mut(|opt| mutate_style(Arc::make_mut(opt.style_mut())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`Style`] used by all new windows, panels etc.
|
/// The currently active [`Style`] used by all new windows, panels etc.
|
||||||
///
|
///
|
||||||
/// You can also change this using [`Self::style_mut`]
|
/// Use [`Self::all_styles_mut`] to mutate both dark and light mode styles.
|
||||||
|
///
|
||||||
|
/// You can also change this using [`Self::style_mut`].
|
||||||
///
|
///
|
||||||
/// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
|
/// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
|
||||||
pub fn set_style(&self, style: impl Into<Arc<Style>>) {
|
pub fn set_style(&self, style: impl Into<Arc<Style>>) {
|
||||||
self.options_mut(|opt| opt.style = style.into());
|
self.options_mut(|opt| *opt.style_mut() = style.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutate the [`Style`]s used by all subsequent windows, panels etc. in both dark and light mode.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// # let mut ctx = egui::Context::default();
|
||||||
|
/// ctx.all_styles_mut(|style| {
|
||||||
|
/// style.spacing.item_spacing = egui::vec2(10.0, 20.0);
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
pub fn all_styles_mut(&self, mut mutate_style: impl FnMut(&mut Style)) {
|
||||||
|
self.options_mut(|opt| {
|
||||||
|
mutate_style(Arc::make_mut(&mut opt.dark_style));
|
||||||
|
mutate_style(Arc::make_mut(&mut opt.light_style));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`Style`] used by all subsequent windows, panels etc.
|
||||||
|
pub fn style_of(&self, theme: Theme) -> Arc<Style> {
|
||||||
|
self.options(|opt| match theme {
|
||||||
|
Theme::Dark => opt.dark_style.clone(),
|
||||||
|
Theme::Light => opt.light_style.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutate the [`Style`] used by all subsequent windows, panels etc.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// # let mut ctx = egui::Context::default();
|
||||||
|
/// ctx.style_mut_of(egui::Theme::Dark, |style| {
|
||||||
|
/// style.spacing.item_spacing = egui::vec2(10.0, 20.0);
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
pub fn style_mut_of(&self, theme: Theme, mutate_style: impl FnOnce(&mut Style)) {
|
||||||
|
self.options_mut(|opt| match theme {
|
||||||
|
Theme::Dark => mutate_style(Arc::make_mut(&mut opt.dark_style)),
|
||||||
|
Theme::Light => mutate_style(Arc::make_mut(&mut opt.light_style)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`Style`] used by all new windows, panels etc.
|
||||||
|
/// Use [`Self::set_theme`] to choose between dark and light mode.
|
||||||
|
///
|
||||||
|
/// You can also change this using [`Self::style_mut_of`].
|
||||||
|
///
|
||||||
|
/// You can use [`Ui::style_mut`] to change the style of a single [`Ui`].
|
||||||
|
pub fn set_style_of(&self, theme: Theme, style: impl Into<Arc<Style>>) {
|
||||||
|
let style = style.into();
|
||||||
|
self.options_mut(|opt| match theme {
|
||||||
|
Theme::Dark => opt.dark_style = style,
|
||||||
|
Theme::Light => opt.light_style = style,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`crate::Visuals`] used by all subsequent windows, panels etc.
|
/// The [`crate::Visuals`] used by all subsequent windows, panels etc.
|
||||||
|
|
@ -1646,10 +1727,10 @@ impl Context {
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// # let mut ctx = egui::Context::default();
|
/// # let mut ctx = egui::Context::default();
|
||||||
/// ctx.set_visuals(egui::Visuals::light()); // Switch to light mode
|
/// ctx.set_visuals_of(egui::Theme::Dark, egui::Visuals { panel_fill: egui::Color32::RED, ..Default::default() });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_visuals(&self, visuals: crate::Visuals) {
|
pub fn set_visuals_of(&self, theme: Theme, visuals: crate::Visuals) {
|
||||||
self.options_mut(|opt| std::sync::Arc::make_mut(&mut opt.style).visuals = visuals);
|
self.style_mut_of(theme, |style| style.visuals = visuals);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of physical pixels for each logical point.
|
/// The number of physical pixels for each logical point.
|
||||||
|
|
@ -2481,13 +2562,13 @@ impl Context {
|
||||||
/// Whether or not to debug widget layout on hover.
|
/// Whether or not to debug widget layout on hover.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub fn debug_on_hover(&self) -> bool {
|
pub fn debug_on_hover(&self) -> bool {
|
||||||
self.options(|opt| opt.style.debug.debug_on_hover)
|
self.options(|opt| opt.style().debug.debug_on_hover)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn on/off whether or not to debug widget layout on hover.
|
/// Turn on/off whether or not to debug widget layout on hover.
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
|
pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
|
||||||
self.style_mut(|style| style.debug.debug_on_hover = debug_on_hover);
|
self.all_styles_mut(|style| style.debug.debug_on_hover = debug_on_hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2871,11 +2952,11 @@ impl Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Edit the active [`Style`].
|
/// Edit the [`Style`].
|
||||||
pub fn style_ui(&self, ui: &mut Ui) {
|
pub fn style_ui(&self, ui: &mut Ui, theme: Theme) {
|
||||||
let mut style: Style = (*self.style()).clone();
|
let mut style: Style = (*self.style_of(theme)).clone();
|
||||||
style.ui(ui);
|
style.ui(ui);
|
||||||
self.set_style(style);
|
self.set_style_of(theme, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -462,7 +462,7 @@ pub use self::{
|
||||||
layers::{LayerId, Order},
|
layers::{LayerId, Order},
|
||||||
layout::*,
|
layout::*,
|
||||||
load::SizeHint,
|
load::SizeHint,
|
||||||
memory::{Memory, Options, Theme},
|
memory::{Memory, Options, Theme, ThemePreference},
|
||||||
painter::Painter,
|
painter::Painter,
|
||||||
response::{InnerResponse, Response},
|
response::{InnerResponse, Response},
|
||||||
sense::Sense,
|
sense::Sense,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod theme;
|
mod theme;
|
||||||
pub use theme::Theme;
|
pub use theme::{Theme, ThemePreference};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -168,24 +168,30 @@ impl FocusDirection {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
/// The default style for new [`Ui`](crate::Ui):s.
|
/// The default style for new [`Ui`](crate::Ui):s in dark mode.
|
||||||
#[cfg_attr(feature = "serde", serde(skip))]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
pub(crate) style: std::sync::Arc<Style>,
|
pub dark_style: std::sync::Arc<Style>,
|
||||||
|
|
||||||
/// Whether to update the visuals according to the system theme or not.
|
/// The default style for new [`Ui`](crate::Ui):s in light mode.
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
|
pub light_style: std::sync::Arc<Style>,
|
||||||
|
|
||||||
|
/// A preference for how to select between dark and light [`crate::Context::style`]
|
||||||
|
/// as the active style used by all subsequent windows, panels etc.
|
||||||
///
|
///
|
||||||
/// Default: `true`.
|
/// Default: `ThemePreference::System`.
|
||||||
pub follow_system_theme: bool,
|
pub theme_preference: ThemePreference,
|
||||||
|
|
||||||
/// Which theme to use in case [`Self::follow_system_theme`] is set
|
/// Which theme to use in case [`Self::theme_preference`] is [`ThemePreference::System`]
|
||||||
/// and egui fails to detect the system theme.
|
/// and egui fails to detect the system theme.
|
||||||
///
|
///
|
||||||
/// Default: [`crate::Theme::Dark`].
|
/// Default: [`crate::Theme::Dark`].
|
||||||
pub fallback_theme: Theme,
|
pub fallback_theme: Theme,
|
||||||
|
|
||||||
/// Used to detect changes in system theme
|
/// The current system theme, used to choose between
|
||||||
|
/// dark and light style in case [`Self::theme_preference`] is [`ThemePreference::System`].
|
||||||
#[cfg_attr(feature = "serde", serde(skip))]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
system_theme: Option<Theme>,
|
pub(crate) system_theme: Option<Theme>,
|
||||||
|
|
||||||
/// Global zoom factor of the UI.
|
/// Global zoom factor of the UI.
|
||||||
///
|
///
|
||||||
|
|
@ -282,8 +288,9 @@ impl Default for Options {
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
style: Default::default(),
|
dark_style: std::sync::Arc::new(Theme::Dark.default_style()),
|
||||||
follow_system_theme: true,
|
light_style: std::sync::Arc::new(Theme::Light.default_style()),
|
||||||
|
theme_preference: ThemePreference::System,
|
||||||
fallback_theme: Theme::Dark,
|
fallback_theme: Theme::Dark,
|
||||||
system_theme: None,
|
system_theme: None,
|
||||||
zoom_factor: 1.0,
|
zoom_factor: 1.0,
|
||||||
|
|
@ -305,21 +312,29 @@ impl Default for Options {
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
pub(crate) fn begin_frame(&mut self, new_raw_input: &RawInput) {
|
pub(crate) fn begin_frame(&mut self, new_raw_input: &RawInput) {
|
||||||
if self.follow_system_theme {
|
self.system_theme = new_raw_input.system_theme;
|
||||||
let theme_from_visuals = Theme::from_dark_mode(self.style.visuals.dark_mode);
|
}
|
||||||
let current_system_theme = self.system_theme.unwrap_or(theme_from_visuals);
|
|
||||||
let new_system_theme = new_raw_input.system_theme.unwrap_or(self.fallback_theme);
|
|
||||||
|
|
||||||
// Only update the visuals if the system theme has changed.
|
/// The currently active theme (may depend on the system theme).
|
||||||
// This allows users to change the visuals without them
|
pub(crate) fn theme(&self) -> Theme {
|
||||||
// getting reset on the next frame.
|
match self.theme_preference {
|
||||||
if current_system_theme != new_system_theme || self.system_theme.is_none() {
|
ThemePreference::Dark => Theme::Dark,
|
||||||
self.system_theme = Some(new_system_theme);
|
ThemePreference::Light => Theme::Light,
|
||||||
if theme_from_visuals != new_system_theme {
|
ThemePreference::System => self.system_theme.unwrap_or(self.fallback_theme),
|
||||||
let visuals = new_system_theme.default_visuals();
|
}
|
||||||
std::sync::Arc::make_mut(&mut self.style).visuals = visuals;
|
}
|
||||||
}
|
|
||||||
}
|
pub(crate) fn style(&self) -> &std::sync::Arc<Style> {
|
||||||
|
match self.theme() {
|
||||||
|
Theme::Dark => &self.dark_style,
|
||||||
|
Theme::Light => &self.light_style,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn style_mut(&mut self) -> &mut std::sync::Arc<Style> {
|
||||||
|
match self.theme() {
|
||||||
|
Theme::Dark => &mut self.dark_style,
|
||||||
|
Theme::Light => &mut self.light_style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,8 +343,9 @@ impl Options {
|
||||||
/// Show the options in the ui.
|
/// Show the options in the ui.
|
||||||
pub fn ui(&mut self, ui: &mut crate::Ui) {
|
pub fn ui(&mut self, ui: &mut crate::Ui) {
|
||||||
let Self {
|
let Self {
|
||||||
style, // covered above
|
dark_style, // covered above
|
||||||
follow_system_theme: _,
|
light_style,
|
||||||
|
theme_preference,
|
||||||
fallback_theme: _,
|
fallback_theme: _,
|
||||||
system_theme: _,
|
system_theme: _,
|
||||||
zoom_factor: _, // TODO(emilk)
|
zoom_factor: _, // TODO(emilk)
|
||||||
|
|
@ -370,7 +386,14 @@ impl Options {
|
||||||
CollapsingHeader::new("🎑 Style")
|
CollapsingHeader::new("🎑 Style")
|
||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
std::sync::Arc::make_mut(style).ui(ui);
|
theme_preference.radio_buttons(ui);
|
||||||
|
|
||||||
|
CollapsingHeader::new("Dark")
|
||||||
|
.default_open(true)
|
||||||
|
.show(ui, |ui| std::sync::Arc::make_mut(dark_style).ui(ui));
|
||||||
|
CollapsingHeader::new("Light")
|
||||||
|
.default_open(true)
|
||||||
|
.show(ui, |ui| std::sync::Arc::make_mut(light_style).ui(ui));
|
||||||
});
|
});
|
||||||
|
|
||||||
CollapsingHeader::new("✒ Painting")
|
CollapsingHeader::new("✒ Painting")
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::Button;
|
||||||
|
|
||||||
/// Dark or Light theme.
|
/// Dark or Light theme.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
|
@ -18,6 +20,14 @@ impl Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default style for this theme.
|
||||||
|
pub fn default_style(self) -> crate::Style {
|
||||||
|
crate::Style {
|
||||||
|
visuals: self.default_visuals(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Chooses between [`Self::Dark`] or [`Self::Light`] based on a boolean value.
|
/// Chooses between [`Self::Dark`] or [`Self::Light`] based on a boolean value.
|
||||||
pub fn from_dark_mode(dark_mode: bool) -> Self {
|
pub fn from_dark_mode(dark_mode: bool) -> Self {
|
||||||
if dark_mode {
|
if dark_mode {
|
||||||
|
|
@ -27,3 +37,64 @@ impl Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Theme {
|
||||||
|
/// Show small toggle-button for light and dark mode.
|
||||||
|
/// This is not the best design as it doesn't allow switching back to "follow system".
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn small_toggle_button(self, ui: &mut crate::Ui) -> Option<Self> {
|
||||||
|
#![allow(clippy::collapsible_else_if)]
|
||||||
|
if self == Self::Dark {
|
||||||
|
if ui
|
||||||
|
.add(Button::new("☀").frame(false))
|
||||||
|
.on_hover_text("Switch to light mode")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
return Some(Self::Light);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ui
|
||||||
|
.add(Button::new("🌙").frame(false))
|
||||||
|
.on_hover_text("Switch to dark mode")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
return Some(Self::Dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The user's theme preference.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub enum ThemePreference {
|
||||||
|
/// Dark mode: light text on a dark background.
|
||||||
|
Dark,
|
||||||
|
|
||||||
|
/// Light mode: dark text on a light background.
|
||||||
|
Light,
|
||||||
|
|
||||||
|
/// Follow the system's theme preference.
|
||||||
|
System,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Theme> for ThemePreference {
|
||||||
|
fn from(value: Theme) -> Self {
|
||||||
|
match value {
|
||||||
|
Theme::Dark => Self::Dark,
|
||||||
|
Theme::Light => Self::Light,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemePreference {
|
||||||
|
/// Show radio-buttons to switch between light mode, dark mode and following the system theme.
|
||||||
|
pub fn radio_buttons(&mut self, ui: &mut crate::Ui) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.selectable_value(self, Self::Light, "☀ Light");
|
||||||
|
ui.selectable_value(self, Self::Dark, "🌙 Dark");
|
||||||
|
ui.selectable_value(self, Self::System, "System");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,8 @@ impl From<TextStyle> for FontSelection {
|
||||||
/// Specifies the look and feel of egui.
|
/// Specifies the look and feel of egui.
|
||||||
///
|
///
|
||||||
/// You can change the visuals of a [`Ui`] with [`Ui::style_mut`]
|
/// You can change the visuals of a [`Ui`] with [`Ui::style_mut`]
|
||||||
/// and of everything with [`crate::Context::set_style`].
|
/// and of everything with [`crate::Context::set_style_of`].
|
||||||
|
/// To choose between dark and light style, use [`crate::Context::set_theme`].
|
||||||
///
|
///
|
||||||
/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.
|
/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
@ -206,12 +207,10 @@ pub struct Style {
|
||||||
/// use egui::FontFamily::Proportional;
|
/// use egui::FontFamily::Proportional;
|
||||||
/// use egui::FontId;
|
/// use egui::FontId;
|
||||||
/// use egui::TextStyle::*;
|
/// use egui::TextStyle::*;
|
||||||
///
|
/// use std::collections::BTreeMap;
|
||||||
/// // Get current context style
|
|
||||||
/// let mut style = (*ctx.style()).clone();
|
|
||||||
///
|
///
|
||||||
/// // Redefine text_styles
|
/// // Redefine text_styles
|
||||||
/// style.text_styles = [
|
/// let text_styles: BTreeMap<_, _> = [
|
||||||
/// (Heading, FontId::new(30.0, Proportional)),
|
/// (Heading, FontId::new(30.0, Proportional)),
|
||||||
/// (Name("Heading2".into()), FontId::new(25.0, Proportional)),
|
/// (Name("Heading2".into()), FontId::new(25.0, Proportional)),
|
||||||
/// (Name("Context".into()), FontId::new(23.0, Proportional)),
|
/// (Name("Context".into()), FontId::new(23.0, Proportional)),
|
||||||
|
|
@ -221,8 +220,8 @@ pub struct Style {
|
||||||
/// (Small, FontId::new(10.0, Proportional)),
|
/// (Small, FontId::new(10.0, Proportional)),
|
||||||
/// ].into();
|
/// ].into();
|
||||||
///
|
///
|
||||||
/// // Mutate global style with above changes
|
/// // Mutate global styles with new text styles
|
||||||
/// ctx.set_style(style);
|
/// ctx.all_styles_mut(move |style| style.text_styles = text_styles.clone());
|
||||||
/// ```
|
/// ```
|
||||||
pub text_styles: BTreeMap<TextStyle, FontId>,
|
pub text_styles: BTreeMap<TextStyle, FontId>,
|
||||||
|
|
||||||
|
|
@ -855,7 +854,7 @@ impl Default for TextCursorStyle {
|
||||||
/// Controls the visual style (colors etc) of egui.
|
/// Controls the visual style (colors etc) of egui.
|
||||||
///
|
///
|
||||||
/// You can change the visuals of a [`Ui`] with [`Ui::visuals_mut`]
|
/// You can change the visuals of a [`Ui`] with [`Ui::visuals_mut`]
|
||||||
/// and of everything with [`crate::Context::set_visuals`].
|
/// and of everything with [`crate::Context::set_visuals_of`].
|
||||||
///
|
///
|
||||||
/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.
|
/// If you want to change fonts, use [`crate::Context::set_fonts`] instead.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
@ -1492,7 +1491,7 @@ impl Default for Widgets {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
widgets::{reset_button, Button, DragValue, Slider, Widget},
|
widgets::{reset_button, DragValue, Slider, Widget},
|
||||||
Ui,
|
Ui,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1519,8 +1518,6 @@ impl Style {
|
||||||
scroll_animation,
|
scroll_animation,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
visuals.light_dark_radio_buttons(ui);
|
|
||||||
|
|
||||||
crate::Grid::new("_options").show(ui, |ui| {
|
crate::Grid::new("_options").show(ui, |ui| {
|
||||||
ui.label("Override font id");
|
ui.label("Override font id");
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
|
|
@ -1931,38 +1928,6 @@ impl WidgetVisuals {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visuals {
|
impl Visuals {
|
||||||
/// Show radio-buttons to switch between light and dark mode.
|
|
||||||
pub fn light_dark_radio_buttons(&mut self, ui: &mut crate::Ui) {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.selectable_value(self, Self::light(), "☀ Light");
|
|
||||||
ui.selectable_value(self, Self::dark(), "🌙 Dark");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show small toggle-button for light and dark mode.
|
|
||||||
#[must_use]
|
|
||||||
pub fn light_dark_small_toggle_button(&self, ui: &mut crate::Ui) -> Option<Self> {
|
|
||||||
#![allow(clippy::collapsible_else_if)]
|
|
||||||
if self.dark_mode {
|
|
||||||
if ui
|
|
||||||
.add(Button::new("☀").frame(false))
|
|
||||||
.on_hover_text("Switch to light mode")
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
return Some(Self::light());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ui
|
|
||||||
.add(Button::new("🌙").frame(false))
|
|
||||||
.on_hover_text("Switch to dark mode")
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
return Some(Self::dark());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut crate::Ui) {
|
pub fn ui(&mut self, ui: &mut crate::Ui) {
|
||||||
let Self {
|
let Self {
|
||||||
dark_mode: _,
|
dark_mode: _,
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ impl Ui {
|
||||||
/// Mutably borrow internal [`Style`].
|
/// Mutably borrow internal [`Style`].
|
||||||
/// Changes apply to this [`Ui`] and its subsequent children.
|
/// Changes apply to this [`Ui`] and its subsequent children.
|
||||||
///
|
///
|
||||||
/// To set the style of all [`Ui`]:s, use [`Context::set_style`].
|
/// To set the style of all [`Ui`]:s, use [`Context::set_style_of`].
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -334,7 +334,7 @@ impl Ui {
|
||||||
|
|
||||||
/// Changes apply to this [`Ui`] and its subsequent children.
|
/// Changes apply to this [`Ui`] and its subsequent children.
|
||||||
///
|
///
|
||||||
/// To set the visuals of all [`Ui`]:s, use [`Context::set_visuals`].
|
/// To set the visuals of all [`Ui`]:s, use [`Context::set_visuals_of`].
|
||||||
pub fn set_style(&mut self, style: impl Into<Arc<Style>>) {
|
pub fn set_style(&mut self, style: impl Into<Arc<Style>>) {
|
||||||
self.style = style.into();
|
self.style = style.into();
|
||||||
}
|
}
|
||||||
|
|
@ -374,7 +374,7 @@ impl Ui {
|
||||||
/// Mutably borrow internal `visuals`.
|
/// Mutably borrow internal `visuals`.
|
||||||
/// Changes apply to this [`Ui`] and its subsequent children.
|
/// Changes apply to this [`Ui`] and its subsequent children.
|
||||||
///
|
///
|
||||||
/// To set the visuals of all [`Ui`]:s, use [`Context::set_visuals`].
|
/// To set the visuals of all [`Ui`]:s, use [`Context::set_visuals_of`].
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
||||||
|
|
@ -369,7 +369,7 @@ fn color_picker_hsvag_2d(ui: &mut Ui, hsvag: &mut HsvaGamma, alpha: Alpha) {
|
||||||
fn input_type_button_ui(ui: &mut Ui) {
|
fn input_type_button_ui(ui: &mut Ui) {
|
||||||
let mut input_type = ui.ctx().style().visuals.numeric_color_space;
|
let mut input_type = ui.ctx().style().visuals.numeric_color_space;
|
||||||
if input_type.toggle_button_ui(ui).changed() {
|
if input_type.toggle_button_ui(ui).changed() {
|
||||||
ui.ctx().style_mut(|s| {
|
ui.ctx().all_styles_mut(|s| {
|
||||||
s.visuals.numeric_color_space = input_type;
|
s.visuals.numeric_color_space = input_type;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,17 +133,27 @@ pub fn stroke_ui(ui: &mut crate::Ui, stroke: &mut epaint::Stroke, text: &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show a small button to switch to/from dark/light mode (globally).
|
/// Show a small button to switch to/from dark/light mode (globally).
|
||||||
pub fn global_dark_light_mode_switch(ui: &mut Ui) {
|
pub fn global_theme_preference_switch(ui: &mut Ui) {
|
||||||
let style: crate::Style = (*ui.ctx().style()).clone();
|
if let Some(new_theme) = ui.ctx().theme().small_toggle_button(ui) {
|
||||||
let new_visuals = style.visuals.light_dark_small_toggle_button(ui);
|
ui.ctx().set_theme(new_theme);
|
||||||
if let Some(visuals) = new_visuals {
|
|
||||||
ui.ctx().set_visuals(visuals);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show larger buttons for switching between light and dark mode (globally).
|
/// Show larger buttons for switching between light and dark mode (globally).
|
||||||
pub fn global_dark_light_mode_buttons(ui: &mut Ui) {
|
pub fn global_theme_preference_buttons(ui: &mut Ui) {
|
||||||
let mut visuals = ui.ctx().style().visuals.clone();
|
let mut theme_preference = ui.ctx().options(|opt| opt.theme_preference);
|
||||||
visuals.light_dark_radio_buttons(ui);
|
theme_preference.radio_buttons(ui);
|
||||||
ui.ctx().set_visuals(visuals);
|
ui.ctx().set_theme(theme_preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show a small button to switch to/from dark/light mode (globally).
|
||||||
|
#[deprecated = "Use global_theme_preference_switch instead"]
|
||||||
|
pub fn global_dark_light_mode_switch(ui: &mut Ui) {
|
||||||
|
global_theme_preference_switch(ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show larger buttons for switching between light and dark mode (globally).
|
||||||
|
#[deprecated = "Use global_theme_preference_buttons instead"]
|
||||||
|
pub fn global_dark_light_mode_buttons(ui: &mut Ui) {
|
||||||
|
global_theme_preference_buttons(ui);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@ impl WrapApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bar_contents(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cmd: &mut Command) {
|
fn bar_contents(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cmd: &mut Command) {
|
||||||
egui::widgets::global_dark_light_mode_switch(ui);
|
egui::widgets::global_theme_preference_switch(ui);
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use egui::{
|
use egui::{
|
||||||
pos2, scroll_area::ScrollBarVisibility, Align, Align2, Color32, DragValue, NumExt, Rect,
|
pos2, scroll_area::ScrollBarVisibility, Align, Align2, Color32, DragValue, NumExt, Rect,
|
||||||
ScrollArea, Sense, Slider, Style, TextStyle, TextWrapMode, Ui, Vec2, Widget,
|
ScrollArea, Sense, Slider, TextStyle, TextWrapMode, Ui, Vec2, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
|
@ -119,9 +119,9 @@ impl ScrollAppearance {
|
||||||
visibility,
|
visibility,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let mut style: Style = (*ui.ctx().style()).clone();
|
let mut scroll = ui.ctx().style().spacing.scroll;
|
||||||
|
|
||||||
style.spacing.scroll.ui(ui);
|
scroll.ui(ui);
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
|
||||||
|
|
@ -135,8 +135,7 @@ impl ScrollAppearance {
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
|
||||||
ui.ctx().set_style(style.clone());
|
ui.ctx().all_styles_mut(|s| s.spacing.scroll = scroll);
|
||||||
ui.set_style(style);
|
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ impl CodeTheme {
|
||||||
|
|
||||||
/// Show UI for changing the color theme.
|
/// Show UI for changing the color theme.
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) {
|
pub fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
egui::widgets::global_theme_preference_buttons(ui);
|
||||||
|
|
||||||
for theme in SyntectTheme::all() {
|
for theme in SyntectTheme::all() {
|
||||||
if theme.is_dark() == self.dark_mode {
|
if theme.is_dark() == self.dark_mode {
|
||||||
|
|
@ -335,7 +335,7 @@ impl CodeTheme {
|
||||||
|
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.set_width(150.0);
|
ui.set_width(150.0);
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
egui::widgets::global_theme_preference_buttons(ui);
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use egui::{FontFamily, FontId, RichText, TextStyle};
|
use egui::{FontFamily, FontId, RichText, TextStyle};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
fn main() -> eframe::Result {
|
fn main() -> eframe::Result {
|
||||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||||
|
|
@ -28,8 +29,7 @@ fn heading3() -> TextStyle {
|
||||||
fn configure_text_styles(ctx: &egui::Context) {
|
fn configure_text_styles(ctx: &egui::Context) {
|
||||||
use FontFamily::{Monospace, Proportional};
|
use FontFamily::{Monospace, Proportional};
|
||||||
|
|
||||||
let mut style = (*ctx.style()).clone();
|
let text_styles: BTreeMap<TextStyle, FontId> = [
|
||||||
style.text_styles = [
|
|
||||||
(TextStyle::Heading, FontId::new(25.0, Proportional)),
|
(TextStyle::Heading, FontId::new(25.0, Proportional)),
|
||||||
(heading2(), FontId::new(22.0, Proportional)),
|
(heading2(), FontId::new(22.0, Proportional)),
|
||||||
(heading3(), FontId::new(19.0, Proportional)),
|
(heading3(), FontId::new(19.0, Proportional)),
|
||||||
|
|
@ -39,7 +39,7 @@ fn configure_text_styles(ctx: &egui::Context) {
|
||||||
(TextStyle::Small, FontId::new(8.0, Proportional)),
|
(TextStyle::Small, FontId::new(8.0, Proportional)),
|
||||||
]
|
]
|
||||||
.into();
|
.into();
|
||||||
ctx.set_style(style);
|
ctx.all_styles_mut(move |style| style.text_styles = text_styles.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content(ui: &mut egui::Ui) {
|
fn content(ui: &mut egui::Ui) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ fn main() -> eframe::Result {
|
||||||
options,
|
options,
|
||||||
Box::new(|cc| {
|
Box::new(|cc| {
|
||||||
// Use the dark theme
|
// Use the dark theme
|
||||||
cc.egui_ctx.set_visuals(egui::Visuals::dark());
|
cc.egui_ctx.set_theme(egui::Theme::Dark);
|
||||||
// This gives us image support:
|
// This gives us image support:
|
||||||
egui_extras::install_image_loaders(&cc.egui_ctx);
|
egui_extras::install_image_loaders(&cc.egui_ctx);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "custom_style"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.76"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
eframe = { workspace = true, features = [
|
||||||
|
"default",
|
||||||
|
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
|
||||||
|
] }
|
||||||
|
env_logger = { version = "0.10", default-features = false, features = [
|
||||||
|
"auto-color",
|
||||||
|
"humantime",
|
||||||
|
] }
|
||||||
|
egui_demo_lib.workspace = true
|
||||||
|
egui_extras = { workspace = true, features = ["image"] }
|
||||||
|
image = { workspace = true, features = ["png"] }
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
Example of how to customize the style.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo run -p custom_style
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
|
|
@ -0,0 +1,69 @@
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||||
|
#![allow(rustdoc::missing_crate_level_docs)] // it's an example
|
||||||
|
|
||||||
|
use eframe::egui::{
|
||||||
|
self, global_theme_preference_buttons, style::Selection, Color32, Stroke, Style, Theme,
|
||||||
|
};
|
||||||
|
use egui_demo_lib::{View, WidgetGallery};
|
||||||
|
|
||||||
|
fn main() -> eframe::Result {
|
||||||
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||||
|
let options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default().with_inner_size([350.0, 590.0]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
eframe::run_native(
|
||||||
|
"egui example: custom style",
|
||||||
|
options,
|
||||||
|
Box::new(|cc| Ok(Box::new(MyApp::new(cc)))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_custom_style(ctx: &egui::Context) {
|
||||||
|
ctx.style_mut_of(Theme::Light, use_light_green_accent);
|
||||||
|
ctx.style_mut_of(Theme::Dark, use_dark_purple_accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_light_green_accent(style: &mut Style) {
|
||||||
|
style.visuals.hyperlink_color = Color32::from_rgb(18, 180, 85);
|
||||||
|
style.visuals.text_cursor.stroke.color = Color32::from_rgb(28, 92, 48);
|
||||||
|
style.visuals.selection = Selection {
|
||||||
|
bg_fill: Color32::from_rgb(157, 218, 169),
|
||||||
|
stroke: Stroke::new(1.0, Color32::from_rgb(28, 92, 48)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_dark_purple_accent(style: &mut Style) {
|
||||||
|
style.visuals.hyperlink_color = Color32::from_rgb(202, 135, 227);
|
||||||
|
style.visuals.text_cursor.stroke.color = Color32::from_rgb(234, 208, 244);
|
||||||
|
style.visuals.selection = Selection {
|
||||||
|
bg_fill: Color32::from_rgb(105, 67, 119),
|
||||||
|
stroke: Stroke::new(1.0, Color32::from_rgb(234, 208, 244)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyApp {
|
||||||
|
widget_gallery: WidgetGallery,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MyApp {
|
||||||
|
fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
|
setup_custom_style(&cc.egui_ctx);
|
||||||
|
egui_extras::install_image_loaders(&cc.egui_ctx); // Needed for the "Widget Gallery" demo
|
||||||
|
Self {
|
||||||
|
widget_gallery: WidgetGallery::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for MyApp {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ui.heading("egui using a customized style");
|
||||||
|
ui.label("Switch between dark and light mode to see the different styles in action.");
|
||||||
|
global_theme_preference_buttons(ui);
|
||||||
|
ui.separator();
|
||||||
|
self.widget_gallery.ui(ui);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ impl eframe::App for MyApp {
|
||||||
ui.label("This is just the contents of the window.");
|
ui.label("This is just the contents of the window.");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("egui theme:");
|
ui.label("egui theme:");
|
||||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
egui::widgets::global_theme_preference_buttons(ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,9 @@ impl eframe::App for MyApp {
|
||||||
.add(egui::Label::new("hover me!").sense(egui::Sense::hover()))
|
.add(egui::Label::new("hover me!").sense(egui::Sense::hover()))
|
||||||
.hovered()
|
.hovered()
|
||||||
{
|
{
|
||||||
ctx.set_visuals(egui::Visuals::dark());
|
ctx.set_theme(egui::Theme::Dark);
|
||||||
} else {
|
} else {
|
||||||
ctx.set_visuals(egui::Visuals::light());
|
ctx.set_theme(egui::Theme::Light);
|
||||||
};
|
};
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot);
|
ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot);
|
||||||
} else if ui.button("take screenshot!").clicked() {
|
} else if ui.button("take screenshot!").clicked() {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ struct MyApp {
|
||||||
|
|
||||||
impl eframe::App for MyApp {
|
impl eframe::App for MyApp {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
ctx.style_mut(|style| style.interaction.tooltip_delay = 0.0);
|
ctx.all_styles_mut(|style| style.interaction.tooltip_delay = 0.0);
|
||||||
|
|
||||||
egui::SidePanel::left("side_panel_left").show(ctx, |ui| {
|
egui::SidePanel::left("side_panel_left").show(ctx, |ui| {
|
||||||
ui.heading("Information");
|
ui.heading("Information");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue