egui_theme_switcher/
lib.rsuse std::sync::RwLock;
use egui::{
lerp, pos2, vec2, Align2, Color32, FontId, Response, Sense, ThemePreference, Ui, Widget,
WidgetInfo, WidgetType,
};
static TOGGLE_STORAGE: RwLock<ThemePreference> = RwLock::new(ThemePreference::System);
#[non_exhaustive]
#[derive(Default)]
pub enum Dimension {
#[default]
S,
M,
L,
XL,
Custom(f32),
}
impl Dimension {
fn multiplier(&self) -> f32 {
match self {
Dimension::S => 1.,
Dimension::M => 3.,
Dimension::L => 5.,
Dimension::XL => 7.,
Dimension::Custom(mul) => *mul,
}
}
}
pub fn theme_switcher_ui(ui: &mut Ui, dim: Dimension) -> Response {
let desired_size =
ui.spacing().interact_size.y * vec2(5. * dim.multiplier(), 1. * dim.multiplier());
let mut font = FontId::default();
font.size *= dim.multiplier();
let (rect, mut response) = ui.allocate_exact_size(desired_size, Sense::click());
response.widget_info(|| {
WidgetInfo::selected(
WidgetType::RadioButton,
ui.is_enabled(),
true,
"theme switcher",
)
});
let theme = TOGGLE_STORAGE
.read()
.map(|v| *v)
.unwrap_or(ThemePreference::System);
let how_on = match theme {
ThemePreference::Dark => 1.,
ThemePreference::Light => 0.,
ThemePreference::System => 0.5,
};
ui.ctx().set_theme(theme);
if ui.is_rect_visible(rect) {
egui_material_icons::initialize(ui.ctx());
let rect_visuals = ui.style().interact_selectable(&response, false);
let circle_visuals = ui.style().interact_selectable(&response, true);
let rect = rect.expand(rect_visuals.expansion);
let radius = 0.5 * rect.height();
let circle_x = lerp((rect.left() + radius)..=(rect.right() - radius), how_on);
let system_x = rect.width() / 2. + rect.left();
let system_position = pos2(system_x, rect.center().y);
let light_position = pos2(rect.left() + 1.1 * radius, rect.center().y - radius / 10.);
let dark_position = pos2(rect.right() - 1.1 * radius, rect.center().y - radius / 10.);
let circle_position = pos2(circle_x, rect.center().y);
ui.painter()
.rect(rect, radius, rect_visuals.bg_fill, rect_visuals.bg_stroke);
let light_rect = ui.painter().text(
light_position,
Align2::CENTER_CENTER,
egui_material_icons::icons::ICON_LIGHT_MODE,
font.clone(),
Color32::WHITE,
);
let system_rect = ui.painter().text(
system_position,
Align2::CENTER_CENTER,
egui_material_icons::icons::ICON_SETTINGS,
font.clone(),
Color32::WHITE,
);
let dark_rect = ui.painter().text(
dark_position,
Align2::CENTER_CENTER,
egui_material_icons::icons::ICON_DARK_MODE,
font,
Color32::WHITE,
);
if response.clicked() {
response.mark_changed(); let interaction = response.interact_pointer_pos().unwrap();
if light_rect.contains(interaction) {
*TOGGLE_STORAGE.write().unwrap() = ThemePreference::Light;
} else if dark_rect.contains(interaction) {
*TOGGLE_STORAGE.write().unwrap() = ThemePreference::Dark;
} else if system_rect.contains(interaction) {
*TOGGLE_STORAGE.write().unwrap() = ThemePreference::System;
}
}
ui.painter().circle(
circle_position,
1. * radius,
circle_visuals.bg_fill,
circle_visuals.fg_stroke,
);
}
response
}
pub fn theme_switcher_with_dimension(dim: Dimension) -> impl Widget {
move |ui: &mut Ui| theme_switcher_ui(ui, dim)
}
pub fn theme_switcher() -> impl Widget {
move |ui: &mut Ui| theme_switcher_ui(ui, Dimension::default())
}