ui: revamp overlay styling with modern sleek glass theme and custom font

- Bundle and embed Adwaita Sans font for proportional typography across menus, titles, and buttons
- Update HUD action cards to modern sleek glass containers with refined rounded corners and subtle hover glow
- Smooth dropdown menus, action cards, toasts, and settings dialogs with rounded corner radius hierarchy
- Maintain monospace formatting strictly for numerical counters and keybind badges
This commit is contained in:
katR 2026-09-11 22:01:17 +05:30
parent d5687bb884
commit f190f331dd
2 changed files with 149 additions and 117 deletions

Binary file not shown.

View file

@ -23,6 +23,19 @@ pub enum KeybindAction {
ToggleCursor, ToggleCursor,
} }
pub fn setup_custom_fonts(ctx: &egui::Context) {
let mut fonts = egui::FontDefinitions::default();
fonts.font_data.insert(
"modern_sans".to_owned(),
Arc::new(egui::FontData::from_static(include_bytes!("../assets/fonts/AdwaitaSans-Regular.ttf"))),
);
fonts.families
.entry(egui::FontFamily::Proportional)
.or_default()
.insert(0, "modern_sans".to_owned());
ctx.set_fonts(fonts);
}
fn format_egui_key(key: egui::Key) -> Option<String> { fn format_egui_key(key: egui::Key) -> Option<String> {
match key { match key {
egui::Key::Escape => None, egui::Key::Escape => None,
@ -151,7 +164,7 @@ fn render_vu_meter(ui: &mut egui::Ui, level: f32, width: f32, height: f32, label
let bg_color = Color32::from_rgb(14, 14, 16); let bg_color = Color32::from_rgb(14, 14, 16);
let border_stroke = Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 20)); let border_stroke = Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 20));
ui.painter().rect(rect, CornerRadius::ZERO, bg_color, border_stroke, egui::StrokeKind::Inside); ui.painter().rect(rect, CornerRadius::same(3), bg_color, border_stroke, egui::StrokeKind::Inside);
let fill_w = (rect.width() * clamped).max(0.0); let fill_w = (rect.width() * clamped).max(0.0);
if fill_w > 0.5 { if fill_w > 0.5 {
@ -163,7 +176,7 @@ fn render_vu_meter(ui: &mut egui::Ui, level: f32, width: f32, height: f32, label
} else { } else {
Color32::from_rgb(34, 197, 94) Color32::from_rgb(34, 197, 94)
}; };
ui.painter().rect_filled(fill_rect, CornerRadius::ZERO, fill_color); ui.painter().rect_filled(fill_rect, CornerRadius::same(3), fill_color);
} }
if clamped > 0.05 { if clamped > 0.05 {
@ -179,7 +192,7 @@ fn render_vu_meter(ui: &mut egui::Ui, level: f32, width: f32, height: f32, label
rect.left_center() + Vec2::new(3.0, 0.0), rect.left_center() + Vec2::new(3.0, 0.0),
egui::Align2::LEFT_CENTER, egui::Align2::LEFT_CENTER,
label, label,
FontId::monospace(8.0), FontId::proportional(8.5),
Color32::from_rgba_unmultiplied(255, 255, 255, 200), Color32::from_rgba_unmultiplied(255, 255, 255, 200),
); );
} }
@ -382,16 +395,16 @@ fn async_send_command(cmd: Command) {
#[allow(dead_code)] #[allow(dead_code)]
fn render_keycap(ui: &mut egui::Ui, text: &str) { fn render_keycap(ui: &mut egui::Ui, text: &str) {
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgb(15, 15, 17)) .fill(Color32::from_rgb(18, 20, 26))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 28))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 26)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(5))
.inner_margin(Margin::symmetric(6_i8, 2_i8)) .inner_margin(Margin::symmetric(7_i8, 3_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.label( ui.label(
egui::RichText::new(text) egui::RichText::new(text)
.font(FontId::monospace(9.5)) .font(FontId::monospace(9.5))
.strong() .strong()
.color(Color32::from_rgb(215, 215, 220)), .color(Color32::from_rgb(220, 222, 228)),
); );
}); });
} }
@ -417,16 +430,16 @@ fn render_keycap_button(
) -> bool { ) -> bool {
let (fill, stroke, text_color, label_text) = if listening { let (fill, stroke, text_color, label_text) = if listening {
( (
Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 28), Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 32),
Stroke::new(1.5_f32, accent), Stroke::new(1.5_f32, accent),
accent, accent,
"PRESS KEYS...".to_string(), "PRESS KEYS...".to_string(),
) )
} else { } else {
( (
Color32::from_rgba_unmultiplied(18, 20, 26, 195), Color32::from_rgba_unmultiplied(20, 22, 30, 210),
Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 28)), Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 24)),
Color32::from_rgb(220, 220, 225), Color32::from_rgb(220, 220, 228),
text.to_string(), text.to_string(),
) )
}; };
@ -439,33 +452,33 @@ fn render_keycap_button(
) )
.fill(fill) .fill(fill)
.stroke(stroke) .stroke(stroke)
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(6))
.min_size(Vec2::new(130.0, 26.0)); .min_size(Vec2::new(130.0, 26.0));
ui.add(btn).clicked() ui.add(btn).clicked()
} }
// Minimal squared button selector // Modern sleek rounded button
fn squared_button(ui: &mut egui::Ui, text: &str, active: bool, accent: Color32) -> bool { fn squared_button(ui: &mut egui::Ui, text: &str, active: bool, accent: Color32) -> bool {
let fill = if active { let fill = if active {
accent accent
} else { } else {
Color32::from_rgba_unmultiplied(255, 255, 255, 10) Color32::from_rgba_unmultiplied(255, 255, 255, 12)
}; };
let stroke = if active { let stroke = if active {
Stroke::new(1.0_f32, accent) Stroke::new(1.0_f32, accent)
} else { } else {
Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 22)) Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 24))
}; };
let text_color = if active { let text_color = if active {
Color32::from_rgb(10, 10, 12) Color32::from_rgb(10, 10, 12)
} else { } else {
Color32::from_rgb(200, 200, 205) Color32::from_rgb(215, 215, 220)
}; };
let btn = egui::Button::new(egui::RichText::new(text).size(11.0).strong().color(text_color)) let btn = egui::Button::new(egui::RichText::new(text).size(11.5).strong().color(text_color))
.fill(fill) .fill(fill)
.stroke(stroke) .stroke(stroke)
.corner_radius(CornerRadius::ZERO); .corner_radius(CornerRadius::same(6));
ui.add(btn).clicked() ui.add(btn).clicked()
} }
@ -474,7 +487,7 @@ fn pill_button(ui: &mut egui::Ui, text: &str, active: bool, accent: Color32) ->
squared_button(ui, text, active, accent) squared_button(ui, text, active, accent)
} }
// Sleek Squared Switch Toggle // Sleek Modern Switch Toggle
fn toggle_switch(ui: &mut egui::Ui, on: &mut bool, accent: Color32) -> egui::Response { fn toggle_switch(ui: &mut egui::Ui, on: &mut bool, accent: Color32) -> egui::Response {
let desired_size = egui::vec2(38.0, 20.0); let desired_size = egui::vec2(38.0, 20.0);
let (rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click()); let (rect, mut response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
@ -489,14 +502,14 @@ fn toggle_switch(ui: &mut egui::Ui, on: &mut bool, accent: Color32) -> egui::Res
let bg_color = if *on { let bg_color = if *on {
accent accent
} else { } else {
Color32::from_rgb(25, 25, 28) Color32::from_rgb(26, 28, 34)
}; };
let stroke = Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 26)); let stroke = Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 28));
ui.painter().rect(rect, CornerRadius::ZERO, bg_color, stroke, egui::StrokeKind::Inside); ui.painter().rect(rect, CornerRadius::same(10), bg_color, stroke, egui::StrokeKind::Inside);
let knob_w = rect.height() - 4.0; let knob_w = rect.height() - 4.0;
let knob_x = egui::lerp((rect.left() + 2.0)..=(rect.right() - knob_w - 2.0), how_on); let knob_x = egui::lerp((rect.left() + 2.0)..=(rect.right() - knob_w - 2.0), how_on);
let knob_rect = egui::Rect::from_min_size(egui::pos2(knob_x, rect.top() + 2.0), egui::vec2(knob_w, knob_w)); let knob_rect = egui::Rect::from_min_size(egui::pos2(knob_x, rect.top() + 2.0), egui::vec2(knob_w, knob_w));
ui.painter().rect_filled(knob_rect, CornerRadius::ZERO, Color32::WHITE); ui.painter().rect_filled(knob_rect, CornerRadius::same(8), Color32::WHITE);
} }
response response
} }
@ -629,11 +642,11 @@ fn draw_settings_icon(painter: &egui::Painter, center: egui::Pos2, radius: f32,
center + Vec2::new(x_off, y_off), center + Vec2::new(x_off, y_off),
Vec2::new(knob_w, knob_h), Vec2::new(knob_w, knob_h),
); );
painter.rect_filled(knob_rect, CornerRadius::ZERO, color); painter.rect_filled(knob_rect, CornerRadius::same(2), color);
} }
} }
// Minimal Squared Action Card Renderer // Modern Sleek Action Card Renderer
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn render_action_card( fn render_action_card(
ui: &mut egui::Ui, ui: &mut egui::Ui,
@ -656,36 +669,36 @@ fn render_action_card(
let hovered = response.hovered(); let hovered = response.hovered();
let bg = if dropdown_open { let bg = if dropdown_open {
Color32::from_rgba_unmultiplied(22, 24, 30, 220) Color32::from_rgba_unmultiplied(26, 30, 44, 245)
} else if hovered { } else if hovered {
Color32::from_rgba_unmultiplied(18, 20, 26, 210) Color32::from_rgba_unmultiplied(22, 26, 38, 235)
} else { } else {
Color32::from_rgba_unmultiplied(12, 13, 17, 195) Color32::from_rgba_unmultiplied(16, 18, 25, 220)
}; };
let border = if dropdown_open { let border = if dropdown_open {
accent accent
} else if hovered { } else if hovered {
Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 190) Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 180)
} else { } else {
Color32::from_rgba_unmultiplied(255, 255, 255, 25) Color32::from_rgba_unmultiplied(255, 255, 255, 24)
}; };
let painter = ui.painter(); let painter = ui.painter();
// Card background - SQUARED // Card background - Modern Sleek Glass with rounded corners
painter.rect(rect, CornerRadius::ZERO, bg, Stroke::new(1.0_f32, border), egui::StrokeKind::Inside); painter.rect(rect, CornerRadius::same(12), bg, Stroke::new(1.0_f32, border), egui::StrokeKind::Inside);
// Card Title // Card Title (clean modern sans typography)
painter.text( painter.text(
egui::pos2(rect.center().x, rect.top() + 24.0), egui::pos2(rect.center().x, rect.top() + 25.0),
egui::Align2::CENTER_CENTER, egui::Align2::CENTER_CENTER,
title, title,
FontId::monospace(14.5), FontId::proportional(14.0),
if is_active { accent } else { Color32::WHITE }, if is_active { accent } else { Color32::WHITE },
); );
// Centered vector icon // Centered vector icon
let icon_center = egui::pos2(rect.center().x, rect.top() + 84.0); let icon_center = egui::pos2(rect.center().x, rect.top() + 85.0);
draw_icon(painter, icon_center); draw_icon(painter, icon_center);
// Status subtitle (e.g. "01:23" or "Not recording" / "Buffer 60s") // Status subtitle (e.g. "01:23" or "Not recording" / "Buffer 60s")
@ -703,13 +716,13 @@ fn render_action_card(
egui::Align2::CENTER_CENTER, egui::Align2::CENTER_CENTER,
sub_text, sub_text,
FontId::proportional(10.0), FontId::proportional(10.0),
Color32::from_rgb(120, 120, 126), Color32::from_rgb(130, 135, 145),
); );
response.clicked() response.clicked()
} }
// Sleek Squared Dropdown Action Menu Container // Modern Sleek Dropdown Action Menu Container
fn render_dropdown_menu( fn render_dropdown_menu(
ui: &mut egui::Ui, ui: &mut egui::Ui,
card_width: f32, card_width: f32,
@ -718,10 +731,10 @@ fn render_dropdown_menu(
) { ) {
ui.add_space(6.0); ui.add_space(6.0);
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgba_unmultiplied(12, 13, 17, 235)) .fill(Color32::from_rgba_unmultiplied(16, 19, 27, 245))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 180))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 160)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(10))
.inner_margin(Margin::ZERO) .inner_margin(Margin::symmetric(4_i8, 4_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.spacing_mut().item_spacing = Vec2::ZERO; ui.spacing_mut().item_spacing = Vec2::ZERO;
ui.set_width(card_width); ui.set_width(card_width);
@ -731,7 +744,7 @@ fn render_dropdown_menu(
}); });
} }
// Sleek Squared Dropdown Action Menu Item (Completely fills container, zero bottom dead space) // Modern Sleek Dropdown Action Menu Item
fn render_menu_item(ui: &mut egui::Ui, label: &str, accent: Color32, is_last: bool) -> bool { fn render_menu_item(ui: &mut egui::Ui, label: &str, accent: Color32, is_last: bool) -> bool {
let (raw_rect, response) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 35.0), egui::Sense::click()); let (raw_rect, response) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 35.0), egui::Sense::click());
let rect = egui::Rect::from_min_size( let rect = egui::Rect::from_min_size(
@ -741,17 +754,17 @@ fn render_menu_item(ui: &mut egui::Ui, label: &str, accent: Color32, is_last: bo
let hovered = response.hovered(); let hovered = response.hovered();
let bg = if hovered { let bg = if hovered {
Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 28) Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 32)
} else { } else {
Color32::TRANSPARENT Color32::TRANSPARENT
}; };
ui.painter().rect_filled(rect, CornerRadius::ZERO, bg); ui.painter().rect_filled(rect, CornerRadius::same(6), bg);
// Clean 1px divider between items (not drawn on last item so it stays flush) // Clean subtle 1px divider between items (not drawn on last item so it stays flush)
if !is_last { if !is_last && !hovered {
ui.painter().line_segment( ui.painter().line_segment(
[egui::pos2(rect.left(), rect.bottom()), egui::pos2(rect.right(), rect.bottom())], [egui::pos2(rect.left() + 8.0, rect.bottom()), egui::pos2(rect.right() - 8.0, rect.bottom())],
Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 14)), Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 14)),
); );
} }
@ -759,7 +772,7 @@ fn render_menu_item(ui: &mut egui::Ui, label: &str, accent: Color32, is_last: bo
let text_color = if hovered { let text_color = if hovered {
accent accent
} else { } else {
Color32::from_rgb(220, 220, 225) Color32::from_rgb(220, 222, 228)
}; };
ui.painter().text( ui.painter().text(
@ -776,9 +789,9 @@ fn render_menu_item(ui: &mut egui::Ui, label: &str, accent: Color32, is_last: bo
// Section card helper for Settings view // Section card helper for Settings view
fn render_section_card(ui: &mut egui::Ui, header: &str, accent: Color32, add_contents: impl FnOnce(&mut egui::Ui)) { fn render_section_card(ui: &mut egui::Ui, header: &str, accent: Color32, add_contents: impl FnOnce(&mut egui::Ui)) {
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgba_unmultiplied(16, 18, 24, 185)) .fill(Color32::from_rgba_unmultiplied(18, 21, 28, 190))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 16))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 18)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(8))
.inner_margin(Margin::symmetric(16_i8, 12_i8)) .inner_margin(Margin::symmetric(16_i8, 12_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.set_width(ui.available_width()); ui.set_width(ui.available_width());
@ -1036,15 +1049,15 @@ impl ScytheOverlayApp {
if let crate::updater::UpdateStatus::Available(ref info) = cur_update if let crate::updater::UpdateStatus::Available(ref info) = cur_update
&& !self.update_dismissed { && !self.update_dismissed {
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgba_unmultiplied(13, 14, 18, 210)) .fill(Color32::from_rgba_unmultiplied(16, 19, 27, 220))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 170))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 160)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(8))
.inner_margin(Margin::symmetric(14_i8, 7_i8)) .inner_margin(Margin::symmetric(14_i8, 7_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label( ui.label(
egui::RichText::new(format!("UPDATE: v{}", info.version)) egui::RichText::new(format!("UPDATE: v{}", info.version))
.font(FontId::monospace(11.0)) .size(11.5)
.strong() .strong()
.color(accent), .color(accent),
); );
@ -1205,12 +1218,19 @@ impl ScytheOverlayApp {
}); });
}); });
ui.add_space(14.0); ui.add_space(16.0);
ui.label( egui::Frame::NONE
egui::RichText::new("ESC or click outside to close") .fill(Color32::from_rgba_unmultiplied(12, 14, 20, 180))
.font(FontId::monospace(10.0)) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 18)))
.color(Color32::from_rgba_unmultiplied(203, 213, 225, 120)), .corner_radius(CornerRadius::same(12))
); .inner_margin(Margin::symmetric(14_i8, 5_i8))
.show(ui, |ui| {
ui.label(
egui::RichText::new("ESC or click outside to close")
.size(10.5)
.color(Color32::from_rgba_unmultiplied(203, 213, 225, 150)),
);
});
}); });
}); });
} }
@ -1228,9 +1248,9 @@ impl ScytheOverlayApp {
ui.allocate_new_ui(egui::UiBuilder::new().max_rect(modal_rect), |ui| { ui.allocate_new_ui(egui::UiBuilder::new().max_rect(modal_rect), |ui| {
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgba_unmultiplied(12, 13, 17, 210)) .fill(Color32::from_rgba_unmultiplied(14, 16, 22, 240))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 160))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 140)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(12))
.inner_margin(Margin::symmetric(24_i8, 20_i8)) .inner_margin(Margin::symmetric(24_i8, 20_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.set_width(modal_w - 48.0); ui.set_width(modal_w - 48.0);
@ -1239,13 +1259,13 @@ impl ScytheOverlayApp {
ui.horizontal(|ui| { ui.horizontal(|ui| {
let back_btn = egui::Button::new( let back_btn = egui::Button::new(
egui::RichText::new("< BACK") egui::RichText::new("< BACK")
.font(FontId::monospace(12.0)) .size(11.5)
.strong() .strong()
.color(accent), .color(accent),
) )
.fill(Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 22)) .fill(Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 24))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 180))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 160)))
.corner_radius(CornerRadius::ZERO); .corner_radius(CornerRadius::same(6));
if ui.add(back_btn).clicked() { if ui.add(back_btn).clicked() {
self.switch_view(ShadowPlayView::MainHud, ctx); self.switch_view(ShadowPlayView::MainHud, ctx);
@ -1255,7 +1275,7 @@ impl ScytheOverlayApp {
ui.add_space(14.0); ui.add_space(14.0);
ui.label( ui.label(
egui::RichText::new("SETTINGS") egui::RichText::new("SETTINGS")
.font(FontId::proportional(16.0)) .font(FontId::proportional(15.0))
.strong() .strong()
.color(Color32::WHITE), .color(Color32::WHITE),
); );
@ -1263,15 +1283,15 @@ impl ScytheOverlayApp {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgba_unmultiplied(255, 255, 255, 12)) .fill(Color32::from_rgba_unmultiplied(255, 255, 255, 12))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 25))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 22)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(6))
.inner_margin(Margin::symmetric(8_i8, 4_i8)) .inner_margin(Margin::symmetric(9_i8, 4_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.label( ui.label(
egui::RichText::new("ESC TO CLOSE") egui::RichText::new("ESC TO CLOSE")
.font(FontId::monospace(10.0)) .size(10.5)
.strong() .strong()
.color(Color32::from_rgb(150, 150, 155)), .color(Color32::from_rgb(160, 160, 168)),
); );
}); });
}); });
@ -1660,7 +1680,7 @@ impl ScytheOverlayApp {
) )
.fill(bg) .fill(bg)
.stroke(stroke) .stroke(stroke)
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(6))
.min_size(Vec2::new(140.0, 30.0)); .min_size(Vec2::new(140.0, 30.0));
if ui.add(btn).clicked() { if ui.add(btn).clicked() {
@ -1754,12 +1774,12 @@ impl ScytheOverlayApp {
ui.horizontal(|ui| { ui.horizontal(|ui| {
let reset_btn = egui::Button::new( let reset_btn = egui::Button::new(
egui::RichText::new("RESET DEFAULTS") egui::RichText::new("RESET DEFAULTS")
.font(FontId::monospace(11.5)) .font(FontId::proportional(11.5))
.color(Color32::from_rgb(180, 180, 190)), .color(Color32::from_rgb(180, 180, 190)),
) )
.fill(Color32::from_rgba_unmultiplied(255, 255, 255, 14)) .fill(Color32::from_rgba_unmultiplied(255, 255, 255, 14))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 28))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 28)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(6))
.min_size(Vec2::new(135.0, 38.0)); .min_size(Vec2::new(135.0, 38.0));
if ui.add(reset_btn).clicked() { if ui.add(reset_btn).clicked() {
@ -1783,13 +1803,13 @@ impl ScytheOverlayApp {
let apply_btn = egui::Button::new( let apply_btn = egui::Button::new(
egui::RichText::new("APPLY & SAVE SETTINGS") egui::RichText::new("APPLY & SAVE SETTINGS")
.font(FontId::monospace(12.5)) .font(FontId::proportional(12.5))
.strong() .strong()
.color(Color32::from_rgb(11, 18, 4)), .color(Color32::from_rgb(11, 18, 4)),
) )
.fill(accent) .fill(accent)
.stroke(Stroke::NONE) .stroke(Stroke::NONE)
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(6))
.min_size(Vec2::new(ui.available_width(), 38.0)); .min_size(Vec2::new(ui.available_width(), 38.0));
if ui.add(apply_btn).clicked() { if ui.add(apply_btn).clicked() {
@ -1837,9 +1857,9 @@ impl ScytheOverlayApp {
ui.allocate_new_ui(egui::UiBuilder::new().max_rect(modal_rect), |ui| { ui.allocate_new_ui(egui::UiBuilder::new().max_rect(modal_rect), |ui| {
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgba_unmultiplied(12, 13, 17, 210)) .fill(Color32::from_rgba_unmultiplied(14, 16, 22, 240))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 160))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 140)))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(12))
.inner_margin(Margin::symmetric(20_i8, 16_i8)) .inner_margin(Margin::symmetric(20_i8, 16_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.set_width(modal_w - 40.0); ui.set_width(modal_w - 40.0);
@ -1848,13 +1868,13 @@ impl ScytheOverlayApp {
ui.horizontal(|ui| { ui.horizontal(|ui| {
let back_btn = egui::Button::new( let back_btn = egui::Button::new(
egui::RichText::new("< BACK TO SETTINGS") egui::RichText::new("< BACK TO SETTINGS")
.font(FontId::monospace(11.0)) .size(11.5)
.strong() .strong()
.color(accent), .color(accent),
) )
.fill(Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 22)) .fill(Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 24))
.stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 180))) .stroke(Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 160)))
.corner_radius(CornerRadius::ZERO); .corner_radius(CornerRadius::same(6));
if ui.add(back_btn).clicked() { if ui.add(back_btn).clicked() {
self.switch_view(ShadowPlayView::Settings, ctx); self.switch_view(ShadowPlayView::Settings, ctx);
@ -1864,7 +1884,7 @@ impl ScytheOverlayApp {
ui.add_space(12.0); ui.add_space(12.0);
ui.label( ui.label(
egui::RichText::new("GALLERY & CLIP TRIMMER") egui::RichText::new("GALLERY & CLIP TRIMMER")
.font(FontId::proportional(14.0)) .font(FontId::proportional(15.0))
.strong() .strong()
.color(Color32::WHITE), .color(Color32::WHITE),
); );
@ -1887,7 +1907,7 @@ impl ScytheOverlayApp {
egui::Frame::NONE egui::Frame::NONE
.fill(Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 25)) .fill(Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 25))
.stroke(Stroke::new(1.0_f32, accent)) .stroke(Stroke::new(1.0_f32, accent))
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(6))
.inner_margin(Margin::symmetric(10_i8, 6_i8)) .inner_margin(Margin::symmetric(10_i8, 6_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.label( ui.label(
@ -1931,7 +1951,7 @@ impl ScytheOverlayApp {
let resp = egui::Frame::NONE let resp = egui::Frame::NONE
.fill(card_bg) .fill(card_bg)
.stroke(border) .stroke(border)
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(8))
.inner_margin(Margin::symmetric(8_i8, 6_i8)) .inner_margin(Margin::symmetric(8_i8, 6_i8))
.show(ui, |ui| { .show(ui, |ui| {
ui.set_width(260.0); ui.set_width(260.0);
@ -1939,7 +1959,7 @@ impl ScytheOverlayApp {
let badge_col = if clip.is_replay { accent } else { Color32::from_rgb(239, 68, 68) }; let badge_col = if clip.is_replay { accent } else { Color32::from_rgb(239, 68, 68) };
let badge_txt = if clip.is_replay { "REPLAY" } else { "REC" }; let badge_txt = if clip.is_replay { "REPLAY" } else { "REC" };
let (b_rect, _) = ui.allocate_exact_size(Vec2::new(44.0, 16.0), egui::Sense::hover()); let (b_rect, _) = ui.allocate_exact_size(Vec2::new(44.0, 16.0), egui::Sense::hover());
ui.painter().rect_filled(b_rect, CornerRadius::ZERO, badge_col); ui.painter().rect_filled(b_rect, CornerRadius::same(4), badge_col);
ui.painter().text(b_rect.center(), egui::Align2::CENTER_CENTER, badge_txt, FontId::monospace(8.5), Color32::from_rgb(11, 18, 4)); ui.painter().text(b_rect.center(), egui::Align2::CENTER_CENTER, badge_txt, FontId::monospace(8.5), Color32::from_rgb(11, 18, 4));
ui.add_space(4.0); ui.add_space(4.0);
@ -1995,7 +2015,7 @@ impl ScytheOverlayApp {
let play_btn = egui::Button::new(egui::RichText::new("PLAY VIDEO").size(11.0).strong().color(Color32::from_rgb(11, 18, 4))) let play_btn = egui::Button::new(egui::RichText::new("PLAY VIDEO").size(11.0).strong().color(Color32::from_rgb(11, 18, 4)))
.fill(accent) .fill(accent)
.stroke(Stroke::NONE) .stroke(Stroke::NONE)
.corner_radius(CornerRadius::ZERO); .corner_radius(CornerRadius::same(6));
if ui.add(play_btn).clicked() { if ui.add(play_btn).clicked() {
play_clip(&clip.path); play_clip(&clip.path);
} }
@ -2007,7 +2027,7 @@ impl ScytheOverlayApp {
let del_btn = egui::Button::new(egui::RichText::new("DELETE").size(11.0).strong().color(Color32::from_rgb(239, 68, 68))) let del_btn = egui::Button::new(egui::RichText::new("DELETE").size(11.0).strong().color(Color32::from_rgb(239, 68, 68)))
.fill(Color32::from_rgba_unmultiplied(239, 68, 68, 20)) .fill(Color32::from_rgba_unmultiplied(239, 68, 68, 20))
.stroke(Stroke::new(1.0_f32, Color32::from_rgb(239, 68, 68))) .stroke(Stroke::new(1.0_f32, Color32::from_rgb(239, 68, 68)))
.corner_radius(CornerRadius::ZERO); .corner_radius(CornerRadius::same(6));
if ui.add(del_btn).clicked() { if ui.add(del_btn).clicked() {
let _ = std::fs::remove_file(&clip.path); let _ = std::fs::remove_file(&clip.path);
self.trim_status_msg = Some((format!("Deleted {}", clip.filename), Instant::now())); self.trim_status_msg = Some((format!("Deleted {}", clip.filename), Instant::now()));
@ -2049,7 +2069,7 @@ impl ScytheOverlayApp {
let trim_btn = egui::Button::new(egui::RichText::new("TRIM & EXPORT COPY").size(11.5).strong().color(Color32::from_rgb(11, 18, 4))) let trim_btn = egui::Button::new(egui::RichText::new("TRIM & EXPORT COPY").size(11.5).strong().color(Color32::from_rgb(11, 18, 4)))
.fill(accent) .fill(accent)
.stroke(Stroke::NONE) .stroke(Stroke::NONE)
.corner_radius(CornerRadius::ZERO) .corner_radius(CornerRadius::same(6))
.min_size(Vec2::new(ui.available_width(), 32.0)); .min_size(Vec2::new(ui.available_width(), 32.0));
if ui.add(trim_btn).clicked() { if ui.add(trim_btn).clicked() {
@ -2115,16 +2135,16 @@ impl ScytheOverlayApp {
// Drop shadow // Drop shadow
painter.rect_filled( painter.rect_filled(
toast_rect.translate(Vec2::new(0.0, 4.0)), toast_rect.translate(Vec2::new(0.0, 4.0)),
CornerRadius::ZERO, CornerRadius::same(10),
Color32::from_rgba_unmultiplied(0, 0, 0, 120), Color32::from_rgba_unmultiplied(0, 0, 0, 140),
); );
// Solid obsidian dark slate toast card // Modern sleek dark glass toast card
painter.rect( painter.rect(
toast_rect, toast_rect,
CornerRadius::ZERO, CornerRadius::same(10),
Color32::from_rgba_unmultiplied(14, 16, 21, 245), Color32::from_rgba_unmultiplied(16, 19, 27, 245),
Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 36)), Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 30)),
egui::StrokeKind::Inside, egui::StrokeKind::Inside,
); );
@ -2134,8 +2154,8 @@ impl ScytheOverlayApp {
} else { } else {
accent accent
}; };
let accent_line = egui::Rect::from_min_size(toast_rect.min, Vec2::new(3.5, toast_rect.height())); let accent_line = egui::Rect::from_min_size(toast_rect.min + Vec2::new(1.0, 1.0), Vec2::new(3.5, toast_rect.height() - 2.0));
painter.rect_filled(accent_line, CornerRadius::ZERO, line_color); painter.rect_filled(accent_line, CornerRadius::same(2), line_color);
// Left icon area (32x32 at center-left) // Left icon area (32x32 at center-left)
let icon_center = egui::pos2(toast_rect.left() + 24.0, toast_rect.center().y); let icon_center = egui::pos2(toast_rect.left() + 24.0, toast_rect.center().y);
@ -2176,14 +2196,14 @@ impl ScytheOverlayApp {
egui::pos2(text_left, toast_rect.top() + 19.0), egui::pos2(text_left, toast_rect.top() + 19.0),
egui::Align2::LEFT_CENTER, egui::Align2::LEFT_CENTER,
&notif.title, &notif.title,
FontId::monospace(12.0), FontId::proportional(12.5),
Color32::WHITE, Color32::WHITE,
); );
painter.text( painter.text(
egui::pos2(text_left, toast_rect.top() + 37.0), egui::pos2(text_left, toast_rect.top() + 37.0),
egui::Align2::LEFT_CENTER, egui::Align2::LEFT_CENTER,
&notif.subtitle, &notif.subtitle,
FontId::proportional(10.5), FontId::proportional(11.0),
Color32::from_rgb(160, 160, 168), Color32::from_rgb(160, 160, 168),
); );
} else { } else {
@ -2324,6 +2344,12 @@ impl eframe::App for ScytheOverlayApp {
let mut visuals = egui::Visuals::dark(); let mut visuals = egui::Visuals::dark();
visuals.panel_fill = Color32::TRANSPARENT; visuals.panel_fill = Color32::TRANSPARENT;
visuals.window_fill = Color32::TRANSPARENT; visuals.window_fill = Color32::TRANSPARENT;
visuals.widgets.noninteractive.corner_radius = CornerRadius::same(6);
visuals.widgets.inactive.corner_radius = CornerRadius::same(6);
visuals.widgets.hovered.corner_radius = CornerRadius::same(6);
visuals.widgets.active.corner_radius = CornerRadius::same(6);
visuals.widgets.open.corner_radius = CornerRadius::same(6);
visuals.selection.bg_fill = self.accent_color();
ctx.set_visuals(visuals); ctx.set_visuals(visuals);
// Background screen darkening scrim (rgba 0, 0, 0, 115 gives ~45% dimming of background) // Background screen darkening scrim (rgba 0, 0, 0, 115 gives ~45% dimming of background)
@ -2517,7 +2543,10 @@ pub fn run_egui_overlay() {
let _ = eframe::run_native( let _ = eframe::run_native(
"scythe-overlay", "scythe-overlay",
options, options,
Box::new(|_cc| Ok(Box::new(ScytheOverlayApp::new()))), Box::new(|cc| {
setup_custom_fonts(&cc.egui_ctx);
Ok(Box::new(ScytheOverlayApp::new()))
}),
); );
crate::ipc::clean_overlay_pid(); crate::ipc::clean_overlay_pid();
@ -2591,16 +2620,16 @@ impl eframe::App for ShadowPlayToastApp {
// Drop shadow // Drop shadow
painter.rect_filled( painter.rect_filled(
rect.translate(Vec2::new(0.0, 4.0)), rect.translate(Vec2::new(0.0, 4.0)),
CornerRadius::ZERO, CornerRadius::same(10),
Color32::from_rgba_unmultiplied(0, 0, 0, 120), Color32::from_rgba_unmultiplied(0, 0, 0, 140),
); );
// Solid obsidian dark slate toast card // Modern sleek dark glass toast card
painter.rect( painter.rect(
rect, rect,
CornerRadius::ZERO, CornerRadius::same(10),
Color32::from_rgba_unmultiplied(14, 16, 21, 245), Color32::from_rgba_unmultiplied(16, 19, 27, 245),
Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 36)), Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 30)),
egui::StrokeKind::Inside, egui::StrokeKind::Inside,
); );
@ -2610,8 +2639,8 @@ impl eframe::App for ShadowPlayToastApp {
} else { } else {
self.accent self.accent
}; };
let accent_line = egui::Rect::from_min_size(rect.min, Vec2::new(3.5, rect.height())); let accent_line = egui::Rect::from_min_size(rect.min + Vec2::new(1.0, 1.0), Vec2::new(3.5, rect.height() - 2.0));
painter.rect_filled(accent_line, CornerRadius::ZERO, line_color); painter.rect_filled(accent_line, CornerRadius::same(2), line_color);
// Left icon area (32x32 at center-left) // Left icon area (32x32 at center-left)
let icon_center = egui::pos2(rect.left() + 24.0, rect.center().y); let icon_center = egui::pos2(rect.left() + 24.0, rect.center().y);
@ -2652,14 +2681,14 @@ impl eframe::App for ShadowPlayToastApp {
egui::pos2(text_left, rect.top() + 19.0), egui::pos2(text_left, rect.top() + 19.0),
egui::Align2::LEFT_CENTER, egui::Align2::LEFT_CENTER,
&self.title, &self.title,
FontId::monospace(12.0), FontId::proportional(12.5),
Color32::WHITE, Color32::WHITE,
); );
painter.text( painter.text(
egui::pos2(text_left, rect.top() + 37.0), egui::pos2(text_left, rect.top() + 37.0),
egui::Align2::LEFT_CENTER, egui::Align2::LEFT_CENTER,
&self.subtitle, &self.subtitle,
FontId::proportional(10.5), FontId::proportional(11.0),
Color32::from_rgb(160, 160, 168), Color32::from_rgb(160, 160, 168),
); );
}); });
@ -2740,7 +2769,10 @@ pub fn run_egui_toast(title: &str, subtitle: &str, icon: crate::overlay::ToastIc
let _ = eframe::run_native( let _ = eframe::run_native(
"scythe-toast", "scythe-toast",
options, options,
Box::new(|_cc| Ok(Box::new(app))), Box::new(|cc| {
setup_custom_fonts(&cc.egui_ctx);
Ok(Box::new(app))
}),
); );
crate::ipc::clean_toast_pid(); crate::ipc::clean_toast_pid();