notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

app_style.rs (6165B)


      1 use crate::colors::{
      2     desktop_dark_color_theme, light_color_theme, mobile_dark_color_theme, ColorTheme,
      3 };
      4 use egui::{
      5     epaint::Shadow,
      6     style::{Interaction, Selection, WidgetVisuals, Widgets},
      7     Button, Context, FontFamily, FontId, Rounding, Stroke, Style, TextStyle, Ui, Visuals,
      8 };
      9 use strum::IntoEnumIterator;
     10 use strum_macros::EnumIter;
     11 
     12 const WIDGET_ROUNDING: Rounding = Rounding::same(8.0);
     13 
     14 pub fn light_mode() -> Visuals {
     15     create_themed_visuals(light_color_theme(), Visuals::light())
     16 }
     17 
     18 pub fn dark_mode(mobile: bool) -> Visuals {
     19     create_themed_visuals(
     20         if mobile {
     21             mobile_dark_color_theme()
     22         } else {
     23             desktop_dark_color_theme()
     24         },
     25         Visuals::dark(),
     26     )
     27 }
     28 
     29 pub fn user_requested_visuals_change(
     30     mobile: bool,
     31     cur_darkmode: bool,
     32     ui: &mut Ui,
     33 ) -> Option<Visuals> {
     34     if cur_darkmode {
     35         if ui
     36             .add(Button::new("☀").frame(false))
     37             .on_hover_text("Switch to light mode")
     38             .clicked()
     39         {
     40             return Some(light_mode());
     41         }
     42     } else if ui
     43         .add(Button::new("🌙").frame(false))
     44         .on_hover_text("Switch to dark mode")
     45         .clicked()
     46     {
     47         return Some(dark_mode(mobile));
     48     }
     49     None
     50 }
     51 
     52 /// Create custom text sizes for any FontSizes
     53 pub fn create_custom_style(ctx: &Context, font_size: fn(&NotedeckTextStyle) -> f32) -> Style {
     54     let mut style = (*ctx.style()).clone();
     55 
     56     style.text_styles = NotedeckTextStyle::iter()
     57         .map(|text_style| {
     58             (
     59                 text_style.text_style(),
     60                 FontId::new(font_size(&text_style), text_style.font_family()),
     61             )
     62         })
     63         .collect();
     64 
     65     style.interaction = Interaction {
     66         tooltip_delay: 0.1,
     67         show_tooltips_only_when_still: false,
     68         ..Interaction::default()
     69     };
     70 
     71     style
     72 }
     73 
     74 pub fn desktop_font_size(text_style: &NotedeckTextStyle) -> f32 {
     75     match text_style {
     76         NotedeckTextStyle::Heading => 48.0,
     77         NotedeckTextStyle::Heading2 => 24.0,
     78         NotedeckTextStyle::Heading3 => 20.0,
     79         NotedeckTextStyle::Body => 16.0,
     80         NotedeckTextStyle::Monospace => 13.0,
     81         NotedeckTextStyle::Button => 13.0,
     82         NotedeckTextStyle::Small => 12.0,
     83     }
     84 }
     85 
     86 pub fn mobile_font_size(text_style: &NotedeckTextStyle) -> f32 {
     87     // TODO: tweak text sizes for optimal mobile viewing
     88     match text_style {
     89         NotedeckTextStyle::Heading => 48.0,
     90         NotedeckTextStyle::Heading2 => 24.0,
     91         NotedeckTextStyle::Heading3 => 20.0,
     92         NotedeckTextStyle::Body => 13.0,
     93         NotedeckTextStyle::Monospace => 13.0,
     94         NotedeckTextStyle::Button => 13.0,
     95         NotedeckTextStyle::Small => 12.0,
     96     }
     97 }
     98 
     99 #[derive(EnumIter)]
    100 pub enum NotedeckTextStyle {
    101     Heading,
    102     Heading2,
    103     Heading3,
    104     Body,
    105     Monospace,
    106     Button,
    107     Small,
    108 }
    109 
    110 impl NotedeckTextStyle {
    111     pub fn text_style(&self) -> TextStyle {
    112         match self {
    113             Self::Heading => TextStyle::Heading,
    114             Self::Heading2 => TextStyle::Name("Heading2".into()),
    115             Self::Heading3 => TextStyle::Name("Heading3".into()),
    116             Self::Body => TextStyle::Body,
    117             Self::Monospace => TextStyle::Monospace,
    118             Self::Button => TextStyle::Button,
    119             Self::Small => TextStyle::Small,
    120         }
    121     }
    122 
    123     pub fn font_family(&self) -> FontFamily {
    124         match self {
    125             Self::Heading => FontFamily::Proportional,
    126             Self::Heading2 => FontFamily::Proportional,
    127             Self::Heading3 => FontFamily::Proportional,
    128             Self::Body => FontFamily::Proportional,
    129             Self::Monospace => FontFamily::Monospace,
    130             Self::Button => FontFamily::Proportional,
    131             Self::Small => FontFamily::Proportional,
    132         }
    133     }
    134 }
    135 
    136 pub fn create_themed_visuals(theme: ColorTheme, default: Visuals) -> Visuals {
    137     Visuals {
    138         hyperlink_color: theme.hyperlink_color,
    139         override_text_color: Some(theme.text_color),
    140         panel_fill: theme.panel_fill,
    141         selection: Selection {
    142             bg_fill: theme.selection_color,
    143             stroke: Stroke {
    144                 width: 1.0,
    145                 color: theme.selection_color,
    146             },
    147         },
    148         widgets: Widgets {
    149             noninteractive: WidgetVisuals {
    150                 bg_fill: theme.noninteractive_bg_fill,
    151                 weak_bg_fill: theme.noninteractive_weak_bg_fill,
    152                 bg_stroke: Stroke {
    153                     width: 1.0,
    154                     color: theme.noninteractive_bg_stroke_color,
    155                 },
    156                 fg_stroke: Stroke {
    157                     width: 1.0,
    158                     color: theme.noninteractive_fg_stroke_color,
    159                 },
    160                 rounding: WIDGET_ROUNDING,
    161                 ..default.widgets.noninteractive
    162             },
    163             inactive: WidgetVisuals {
    164                 bg_fill: theme.inactive_bg_fill,
    165                 weak_bg_fill: theme.inactive_weak_bg_fill,
    166                 bg_stroke: Stroke {
    167                     width: 1.0,
    168                     color: theme.inactive_bg_stroke_color,
    169                 },
    170                 rounding: WIDGET_ROUNDING,
    171                 ..default.widgets.inactive
    172             },
    173             hovered: WidgetVisuals {
    174                 rounding: WIDGET_ROUNDING,
    175                 ..default.widgets.hovered
    176             },
    177             active: WidgetVisuals {
    178                 rounding: WIDGET_ROUNDING,
    179                 ..default.widgets.active
    180             },
    181             open: WidgetVisuals {
    182                 ..default.widgets.open
    183             },
    184         },
    185         extreme_bg_color: theme.extreme_bg_color,
    186         error_fg_color: theme.err_fg_color,
    187         window_rounding: Rounding::same(8.0),
    188         window_fill: theme.window_fill,
    189         window_shadow: Shadow {
    190             offset: [0.0, 8.0].into(),
    191             blur: 24.0,
    192             spread: 0.0,
    193             color: egui::Color32::from_rgba_unmultiplied(0x6D, 0x6D, 0x6D, 0x14),
    194         },
    195         window_stroke: Stroke {
    196             width: 1.0,
    197             color: theme.window_stroke_color,
    198         },
    199         ..default
    200     }
    201 }