use egui::{Color32, Context, Frame, Id, LayerId, Order, Rect, Style};
use std::sync::{Arc, RwLock};
const PANIC_MSG: &str = "Custom theme not supported, use Theme::from_custom() instead";
pub mod editor;
pub mod hsla;
pub mod themes;
pub mod utils;
pub mod visuals;
pub mod window;
pub use editor::ThemeEditor;
use themes::*;
pub use visuals::*;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum ThemeKind {
Dark,
Custom,
}
impl ThemeKind {
pub fn to_str(&self) -> &str {
match self {
ThemeKind::Dark => "Dark",
ThemeKind::Custom => "Custom",
}
}
pub fn to_vec() -> Vec<Self> {
vec![Self::Dark]
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct Theme {
pub dark_mode: bool,
#[cfg_attr(feature = "serde", serde(skip))]
pub overlay_manager: OverlayManager,
pub image_tint_recommended: bool,
pub kind: ThemeKind,
pub style: Style,
pub colors: ThemeColors,
pub text_sizes: TextSizes,
pub window_frame: Frame,
pub frame1: Frame,
pub frame2: Frame,
pub frame1_visuals: FrameVisuals,
pub frame2_visuals: FrameVisuals,
}
impl Theme {
pub fn new(kind: ThemeKind) -> Self {
let theme = match kind {
ThemeKind::Dark => dark::theme(),
ThemeKind::Custom => panic!("{}", PANIC_MSG),
};
theme
}
pub fn set_window_frame_colors(&mut self) {
match self.kind {
ThemeKind::Dark => self.window_frame = dark::window_frame(&self.colors),
ThemeKind::Custom => panic!("{}", PANIC_MSG),
}
}
pub fn set_frame1_colors(&mut self) {
match self.kind {
ThemeKind::Dark => self.frame1 = dark::frame1(&self.colors),
ThemeKind::Custom => panic!("{}", PANIC_MSG),
}
}
pub fn set_frame2_colors(&mut self) {
match self.kind {
ThemeKind::Dark => self.frame2 = dark::frame2(&self.colors),
ThemeKind::Custom => panic!("{}", PANIC_MSG),
}
}
pub fn button_visuals(&self) -> ButtonVisuals {
match self.kind {
ThemeKind::Dark => self.colors.button_visuals,
ThemeKind::Custom => panic!("{}", PANIC_MSG),
}
}
pub fn label_visuals(&self) -> LabelVisuals {
match self.kind {
ThemeKind::Dark => self.colors.label_visuals,
ThemeKind::Custom => panic!("{}", PANIC_MSG),
}
}
pub fn combo_box_visuals(&self) -> ComboBoxVisuals {
match self.kind {
ThemeKind::Dark => self.colors.combo_box_visuals,
ThemeKind::Custom => panic!("{}", PANIC_MSG),
}
}
pub fn text_edit_visuals(&self) -> TextEditVisuals {
match self.kind {
ThemeKind::Dark => self.colors.text_edit_visuals,
ThemeKind::Custom => panic!("{}", PANIC_MSG),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug)]
pub struct ThemeColors {
pub button_visuals: ButtonVisuals,
pub label_visuals: LabelVisuals,
pub combo_box_visuals: ComboBoxVisuals,
pub text_edit_visuals: TextEditVisuals,
pub title_bar: Color32,
pub bg: Color32,
pub widget_bg: Color32,
pub hover: Color32,
pub text: Color32,
pub text_muted: Color32,
pub highlight: Color32,
pub border: Color32,
pub accent: Color32,
pub error: Color32,
pub warning: Color32,
pub success: Color32,
pub info: Color32,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Default, Debug)]
pub struct TextSizes {
pub very_small: f32,
pub small: f32,
pub normal: f32,
pub large: f32,
pub very_large: f32,
pub heading: f32,
}
impl TextSizes {
pub fn new(
very_small: f32,
small: f32,
normal: f32,
large: f32,
very_large: f32,
heading: f32,
) -> Self {
Self {
very_small,
small,
normal,
large,
very_large,
heading,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct OverlayManager(Arc<RwLock<OverlayCounter>>);
impl OverlayManager {
pub fn new() -> Self {
Self(Arc::new(RwLock::new(OverlayCounter::new())))
}
pub fn tint_0(&self) -> Color32 {
Color32::from_black_alpha(40)
}
pub fn tint_1(&self) -> Color32 {
Color32::from_black_alpha(60)
}
pub fn tint_2(&self) -> Color32 {
Color32::from_black_alpha(80)
}
pub fn tint_3(&self) -> Color32 {
Color32::from_black_alpha(100)
}
pub fn counter(&self) -> u8 {
self.0.read().unwrap().counter()
}
pub fn order(&self) -> Order {
self.0.read().unwrap().order()
}
pub fn paint_background(&self) {
self.0.write().unwrap().paint_background()
}
pub fn paint_middle(&self) {
self.0.write().unwrap().paint_middle()
}
pub fn paint_foreground(&self) {
self.0.write().unwrap().paint_foreground()
}
pub fn paint_tooltip(&self) {
self.0.write().unwrap().paint_tooltip()
}
pub fn paint_debug(&self) {
self.0.write().unwrap().paint_debug()
}
pub fn window_opened(&self) {
self.0.write().unwrap().window_opened();
}
pub fn window_closed(&self) {
self.0.write().unwrap().window_closed();
}
pub fn recommended_order(&self) -> Order {
self.0.read().unwrap().recommended_order()
}
pub fn calculate_alpha(&self) -> u8 {
self.0.read().unwrap().calculate_alpha()
}
pub fn overlay_tint(&self) -> Color32 {
self.0.read().unwrap().overlay_tint()
}
pub fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
self.0.read().unwrap().paint_overlay(ctx, recommend_order);
}
pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
self.0.read().unwrap().paint_overlay_at(ctx, rect, order, id, tint);
}
}
#[derive(Clone, Debug)]
struct OverlayCounter {
counter: u8,
order: Order,
}
impl Default for OverlayCounter {
fn default() -> Self {
Self::new()
}
}
impl OverlayCounter {
pub fn new() -> Self {
Self {
counter: 0,
order: Order::Background,
}
}
pub fn counter(&self) -> u8 {
self.counter
}
pub fn order(&self) -> Order {
self.order
}
fn paint_background(&mut self) {
self.order = Order::Background;
}
fn paint_middle(&mut self) {
self.order = Order::Middle;
}
fn paint_foreground(&mut self) {
self.order = Order::Foreground;
}
fn paint_tooltip(&mut self) {
self.order = Order::Tooltip;
}
fn paint_debug(&mut self) {
self.order = Order::Debug;
}
fn window_opened(&mut self) {
self.counter += 1;
}
fn window_closed(&mut self) {
if self.counter > 0 {
self.counter -= 1;
}
}
fn calculate_alpha(&self) -> u8 {
let counter = self.counter;
if counter == 0 {
return 0;
}
let mut a = 60;
for _ in 1..counter {
a += 20;
}
a
}
fn overlay_tint(&self) -> Color32 {
let counter = self.counter();
if counter == 1 {
return Color32::from_black_alpha(60);
}
let alpha = self.calculate_alpha();
Color32::from_black_alpha(alpha)
}
fn recommended_order(&self) -> Order {
if self.counter() == 1 {
Order::Middle
} else if self.counter() == 2 {
Order::Foreground
} else {
Order::Tooltip
}
}
fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
let counter = self.counter();
if counter == 0 {
return;
}
let order = if recommend_order {
if counter == 1 {
Order::Middle
} else if counter == 2 {
Order::Foreground
} else {
Order::Tooltip
}
} else {
self.order()
};
let layer_id = LayerId::new(order, Id::new("darkening_overlay"));
let painter = ctx.layer_painter(layer_id);
painter.rect_filled(ctx.content_rect(), 0.0, self.overlay_tint());
}
pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
let layer_id = LayerId::new(order, id);
let painter = ctx.layer_painter(layer_id);
painter.rect_filled(rect, 0.0, tint);
}
}