use crate::assets::{FONT_ICONS_BRANDS, FONT_ICONS_REGULAR, FONT_ICONS_SOLID};
use crate::util::f32_with_precision;
use eframe::egui;
use eframe::egui::{FontId, TextStyle};
use egui::{Color32, FontData, FontDefinitions, FontFamily, Rgba, Rounding, Stroke, Vec2};
use std::convert::Into;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
pub const SCREEN_SIZE: Vec2 = Vec2::new(1920.0, 1080.0);
pub const TEXT_SIZE_HEADING: f32 = 42.0;
pub const TEXT_SIZE_BODY: f32 = 34.0;
pub const TEXT_SIZE_MONOSPACE: f32 = 28.0;
pub const TEXT_SIZE_BUTTON1: f32 = 38.0;
pub const TEXT_SIZE_BUTTON2: f32 = 34.0;
pub const TEXT_SIZE_TOOLTIP: f32 = 20.0;
pub const TEXT_SIZE_ICON: f32 = 32.0;
pub const TEXT_SIZE_DIALOGUE_TITLE: f32 = 30.0;
pub const TEXT_SIZE_DIALOGUE_BODY: f32 = 26.0;
pub const HOVERED: Rgba = Rgba::from_rgb(
0x67 as f32 / 255.0,
0x7B as f32 / 255.0,
0xC4 as f32 / 255.0,
);
pub const FOREST_GREEN: Rgba = Rgba::from_rgb(
0x22 as f32 / 255.0,
0x8B as f32 / 255.0,
0x22 as f32 / 255.0,
);
pub const CUSTOM_RED: Rgba = Rgba::from_rgb(
0xC0 as f32 / 255.0,
0x1C as f32 / 255.0,
0x1C as f32 / 255.0,
);
pub const CUSTOM_ORANGE: Rgba = Rgba::from_rgb(
0xFF as f32 / 255.0,
0x6D as f32 / 255.0,
0x0A as f32 / 255.0,
);
pub const CUSTOM_BLUE: Rgba = Rgba::from_rgb(
0x00 as f32 / 255.0,
0x33 as f32 / 255.0,
0x66 as f32 / 255.0,
);
pub const ACTIVE_BLUE: Rgba = Rgba::from_rgb(
0x72 as f32 / 255.0,
0x89 as f32 / 255.0,
0xDA as f32 / 255.0,
);
pub enum Style {
IconControls,
SelectButton,
CancelButton,
SubmitButton,
TodoButton,
DoneButton,
InterruptedButton,
FailedButton,
SoftFailedButton,
SingleLineTextEdit,
}
pub fn style_ui(ui: &mut egui::Ui, style: Style) {
match style {
Style::IconControls => {
let rounding = Rounding::same(30.0);
ui.spacing_mut().item_spacing = Vec2::splat(10.0);
ui.spacing_mut().button_padding = Vec2::splat(10.0);
ui.visuals_mut().widgets.inactive.bg_fill = Color32::TRANSPARENT;
ui.visuals_mut().widgets.inactive.bg_stroke = Stroke::none();
ui.visuals_mut().widgets.inactive.rounding = rounding;
ui.visuals_mut().widgets.hovered.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.2).into();
ui.visuals_mut().widgets.hovered.bg_stroke = Stroke::new(1.5, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.hovered.rounding = rounding;
ui.visuals_mut().widgets.active.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.4).into();
ui.visuals_mut().widgets.active.bg_stroke = Stroke::new(3.0, Color32::DARK_GRAY);
ui.visuals_mut().widgets.active.rounding = rounding;
ui.visuals_mut().widgets.noninteractive.bg_fill = Color32::TRANSPARENT;
ui.visuals_mut().widgets.noninteractive.bg_stroke = Stroke::none();
ui.visuals_mut().widgets.noninteractive.rounding = rounding;
}
Style::SelectButton => {
let rounding = Rounding::same(40.0);
ui.spacing_mut().item_spacing = Vec2::splat(10.0);
ui.spacing_mut().button_padding = Vec2::new(20.0, 8.0);
ui.visuals_mut().widgets.inactive.rounding = rounding;
ui.visuals_mut().widgets.inactive.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.2).into();
ui.visuals_mut().widgets.inactive.bg_stroke = Stroke::new(2.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.inactive.fg_stroke = Stroke::new(2.0, Color32::BLACK);
ui.visuals_mut().widgets.hovered.rounding = rounding;
ui.visuals_mut().widgets.hovered.bg_fill = HOVERED.multiply(0.2).into();
ui.visuals_mut().widgets.hovered.bg_stroke = Stroke::new(3.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.hovered.fg_stroke = Stroke::new(2.0, Color32::BLACK);
ui.visuals_mut().widgets.active.rounding = rounding;
ui.visuals_mut().widgets.active.bg_fill = HOVERED.multiply(0.2).into();
ui.visuals_mut().widgets.active.bg_stroke = Stroke::new(4.0, Color32::DARK_GRAY);
ui.visuals_mut().widgets.active.fg_stroke = Stroke::new(2.0, Color32::WHITE);
ui.visuals_mut().widgets.noninteractive.rounding = rounding;
}
Style::CancelButton => {
let rounding = Rounding::same(50.0);
ui.spacing_mut().button_padding = Vec2::new(60.0, 20.0);
ui.visuals_mut().widgets.inactive.rounding = rounding;
ui.visuals_mut().widgets.inactive.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.2).into();
ui.visuals_mut().widgets.inactive.bg_stroke = Stroke::new(2.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.inactive.fg_stroke = Stroke::new(2.0, CUSTOM_RED);
ui.visuals_mut().widgets.hovered.rounding = rounding;
ui.visuals_mut().widgets.hovered.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.4).into();
ui.visuals_mut().widgets.hovered.bg_stroke = Stroke::new(3.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.hovered.fg_stroke = Stroke::new(2.0, CUSTOM_RED);
ui.visuals_mut().widgets.active.rounding = rounding;
ui.visuals_mut().widgets.active.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.4).into();
ui.visuals_mut().widgets.active.bg_stroke = Stroke::new(4.0, CUSTOM_RED);
ui.visuals_mut().widgets.active.fg_stroke = Stroke::new(2.0, CUSTOM_RED);
ui.visuals_mut().widgets.noninteractive.rounding = rounding;
ui.visuals_mut().override_text_color = Some(CUSTOM_RED.into());
}
Style::SubmitButton => {
let rounding = Rounding::same(50.0);
ui.spacing_mut().button_padding = Vec2::new(60.0, 20.0);
ui.visuals_mut().widgets.inactive.rounding = rounding;
ui.visuals_mut().widgets.inactive.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.2).into();
ui.visuals_mut().widgets.inactive.bg_stroke = Stroke::new(2.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.inactive.fg_stroke = Stroke::new(2.0, FOREST_GREEN);
ui.visuals_mut().widgets.hovered.rounding = rounding;
ui.visuals_mut().widgets.hovered.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.4).into();
ui.visuals_mut().widgets.hovered.bg_stroke = Stroke::new(3.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.hovered.fg_stroke = Stroke::new(2.0, FOREST_GREEN);
ui.visuals_mut().widgets.active.rounding = rounding;
ui.visuals_mut().widgets.active.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.4).into();
ui.visuals_mut().widgets.active.bg_stroke = Stroke::new(4.0, FOREST_GREEN);
ui.visuals_mut().widgets.active.fg_stroke = Stroke::new(2.0, FOREST_GREEN);
ui.visuals_mut().widgets.noninteractive.rounding = rounding;
ui.visuals_mut().override_text_color = Some(FOREST_GREEN.into());
}
Style::SingleLineTextEdit => {
let rounding = Rounding::same(8.0);
ui.visuals_mut().widgets.inactive.rounding = rounding;
ui.visuals_mut().widgets.hovered.rounding = rounding;
ui.visuals_mut().widgets.active.rounding = rounding;
ui.visuals_mut().widgets.noninteractive.rounding = rounding;
}
Style::TodoButton => {
let rounding = Rounding::same(40.0);
ui.spacing_mut().button_padding = Vec2::new(30.0, 15.0);
ui.visuals_mut().widgets.inactive.rounding = rounding;
ui.visuals_mut().widgets.inactive.bg_fill = Color32::TRANSPARENT;
ui.visuals_mut().widgets.inactive.bg_stroke = Stroke::new(2.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.hovered.rounding = rounding;
ui.visuals_mut().widgets.hovered.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.2).into();
ui.visuals_mut().widgets.hovered.bg_stroke = Stroke::new(3.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.active.rounding = rounding;
ui.visuals_mut().widgets.active.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.4).into();
ui.visuals_mut().widgets.active.bg_stroke = Stroke::new(4.0, ACTIVE_BLUE);
ui.visuals_mut().widgets.noninteractive.rounding = rounding;
}
Style::DoneButton => {
let rounding = Rounding::same(40.0);
ui.spacing_mut().button_padding = Vec2::new(30.0, 15.0);
ui.visuals_mut().widgets.inactive.rounding = rounding;
ui.visuals_mut().widgets.inactive.bg_fill = Color32::TRANSPARENT;
ui.visuals_mut().widgets.inactive.bg_stroke = Stroke::none();
ui.visuals_mut().widgets.inactive.fg_stroke = Stroke::new(2.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.hovered.rounding = rounding;
ui.visuals_mut().widgets.hovered.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.2).into();
ui.visuals_mut().widgets.hovered.bg_stroke = Stroke::none();
ui.visuals_mut().widgets.hovered.fg_stroke = Stroke::new(4.0, Color32::LIGHT_GRAY);
ui.visuals_mut().widgets.active.rounding = rounding;
ui.visuals_mut().widgets.active.bg_fill =
Rgba::from(Color32::LIGHT_GRAY).multiply(0.4).into();
ui.visuals_mut().widgets.active.bg_stroke = Stroke::new(4.0, ACTIVE_BLUE);
ui.visuals_mut().widgets.noninteractive.rounding = rounding;
ui.visuals_mut().override_text_color = Some(FOREST_GREEN.into());
}
Style::InterruptedButton => {
style_ui(ui, Style::TodoButton);
ui.visuals_mut().override_text_color = Some(CUSTOM_BLUE.into());
}
Style::FailedButton => {
style_ui(ui, Style::TodoButton);
ui.visuals_mut().override_text_color = Some(CUSTOM_RED.into());
}
Style::SoftFailedButton => {
style_ui(ui, Style::TodoButton);
ui.visuals_mut().override_text_color = Some(CUSTOM_ORANGE.into());
}
}
}
pub fn init(ctx: &egui::Context) {
let mut fonts = FontDefinitions::default();
fonts.font_data.insert(
"fa_brands_regular".to_owned(),
FontData::from_static(FONT_ICONS_BRANDS),
);
fonts.font_data.insert(
"fa_free_regular".to_owned(),
FontData::from_static(FONT_ICONS_REGULAR),
);
fonts.font_data.insert(
"fa_free_solid".to_owned(),
FontData::from_static(FONT_ICONS_SOLID),
);
fonts
.families
.entry(FontFamily::Name("fa_free".into()))
.or_default()
.extend(vec![
"fa_free_regular".to_owned(),
"fa_free_solid".to_owned(),
"fa_brands_regular".to_owned(),
]);
ctx.set_fonts(fonts);
let mut style = (*ctx.style()).clone();
style.text_styles = [
(
TextStyle::Heading,
FontId::new(TEXT_SIZE_HEADING, FontFamily::Proportional),
),
(
TextStyle::Body,
FontId::new(TEXT_SIZE_BODY, FontFamily::Proportional),
),
(
TextStyle::Monospace,
FontId::new(TEXT_SIZE_MONOSPACE, FontFamily::Proportional),
),
(
TextStyle::Button,
FontId::new(TEXT_SIZE_BUTTON2, FontFamily::Proportional),
),
(
TextStyle::Small,
FontId::new(TEXT_SIZE_TOOLTIP, FontFamily::Proportional),
),
]
.into();
ctx.set_style(style);
let mut visuals = egui::Visuals::light();
visuals.override_text_color = Some(Color32::BLACK);
ctx.set_visuals(visuals);
}
pub fn set_fullscreen_scale(ctx: &egui::Context, scale: f32) {
static mut RESCALE_TIMER: Option<Arc<Mutex<Instant>>> = None;
let curr = ctx.pixels_per_point();
let size = ctx.input().screen_rect().size();
let mut scale = (size.x / SCREEN_SIZE.x).min(size.y / SCREEN_SIZE.y) * scale;
scale = f32_with_precision(scale, 6);
if (scale - 1.0).abs() > 1e-6 {
let now = Instant::now();
unsafe {
if RESCALE_TIMER.is_none() {
RESCALE_TIMER = Some(Arc::new(Mutex::new(Instant::now())));
}
let mut timer = RESCALE_TIMER.as_ref().unwrap().lock().unwrap();
if timer.elapsed() > Duration::from_millis(500) {
println!("Rescaling UI {curr}*{scale}={}", curr * scale);
*timer = now;
} else {
scale = 1.0;
}
}
} else {
scale = 1.0;
}
ctx.set_pixels_per_point(curr * scale);
}
pub mod text {
use super::*;
use eframe::egui::{Color32, RichText};
#[inline(always)]
pub fn heading(text: impl Into<String>) -> RichText {
RichText::new(text).heading()
}
#[inline(always)]
pub fn body(text: impl Into<String>) -> RichText {
RichText::new(text).size(TEXT_SIZE_BODY)
}
#[inline(always)]
pub fn inactive(text: impl Into<String>) -> RichText {
RichText::new(text)
.size(TEXT_SIZE_BODY)
.color(Color32::LIGHT_GRAY)
}
#[inline(always)]
pub fn button1(text: impl Into<String>) -> RichText {
RichText::new(text).size(TEXT_SIZE_BUTTON1)
}
#[inline(always)]
pub fn button2(text: impl Into<String>) -> RichText {
RichText::new(text).size(TEXT_SIZE_BUTTON2)
}
#[inline(always)]
pub fn tooltip(text: impl Into<String>) -> RichText {
RichText::new(text).size(TEXT_SIZE_TOOLTIP)
}
#[inline(always)]
pub fn icon(text: impl Into<String>) -> RichText {
RichText::new(text).size(TEXT_SIZE_ICON)
}
}