ui: align notifications flush to screen edge with translucent cards and modern icons
This commit is contained in:
parent
f0117451e7
commit
a341565c5a
3 changed files with 192 additions and 166 deletions
|
|
@ -158,6 +158,7 @@ pub fn register_hyprland_binds(config: &ScytheConfig) {
|
|||
"float, class:^(scythe-toast)$",
|
||||
"pin, class:^(scythe-toast)$",
|
||||
"noborder, class:^(scythe-toast)$",
|
||||
"move 100%-w 16, class:^(scythe-toast)$",
|
||||
"float, class:^(vrec-overlay)$",
|
||||
"move 0 0, class:^(vrec-overlay)$",
|
||||
"size 100% 100%, class:^(vrec-overlay)$",
|
||||
|
|
|
|||
153
src/overlay.rs
153
src/overlay.rs
|
|
@ -199,7 +199,7 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
_ => ("#38bdf8", (0.220, 0.741, 0.973)),
|
||||
};
|
||||
|
||||
let (active_accent_hex, active_accent) = if icon == ToastIcon::Record {
|
||||
let (_active_accent_hex, active_accent) = if icon == ToastIcon::Record {
|
||||
("#ef4444", (0.937, 0.267, 0.267))
|
||||
} else {
|
||||
(accent_hex, accent_rgb)
|
||||
|
|
@ -229,8 +229,8 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
app.connect_activate(move |app| {
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.default_width(360)
|
||||
.default_height(64)
|
||||
.default_width(320)
|
||||
.default_height(56)
|
||||
.build();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
|
|
@ -250,8 +250,8 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
}
|
||||
window.set_anchor(gtk_layer_shell::Edge::Top, true);
|
||||
window.set_anchor(gtk_layer_shell::Edge::Right, true);
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, 28);
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Right, 28);
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, 16);
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Right, 0); // flush on screen right edge
|
||||
window.set_keyboard_interactivity(false);
|
||||
} else {
|
||||
window.set_decorated(false);
|
||||
|
|
@ -260,8 +260,8 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
window.set_accept_focus(false);
|
||||
if let Some(ref mon) = active_monitor {
|
||||
let geom = mon.geometry();
|
||||
let x = geom.x() + geom.width() - 360 - 28;
|
||||
let y = geom.y() + 28;
|
||||
let x = geom.x() + geom.width() - 320;
|
||||
let y = geom.y() + 16;
|
||||
window.move_(x, y);
|
||||
}
|
||||
}
|
||||
|
|
@ -289,14 +289,12 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
box-shadow: none;
|
||||
}}
|
||||
.toast-card {{
|
||||
background-color: rgba(14, 16, 21, 0.96);
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
border-left: 4px solid {accent};
|
||||
background-color: rgba(10, 10, 10, 0.65);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 0px;
|
||||
box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: none;
|
||||
}}
|
||||
"#,
|
||||
accent = active_accent_hex
|
||||
);
|
||||
let _ = css_provider.load_from_data(css.as_bytes());
|
||||
if let Some(screen) = gdk::Screen::default() {
|
||||
|
|
@ -316,31 +314,20 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
hbox.connect_draw(move |widget, cr| {
|
||||
let w = widget.allocated_width() as f64;
|
||||
let h = widget.allocated_height() as f64;
|
||||
let card_h = (h - 4.0).max(10.0);
|
||||
|
||||
cr.set_operator(gtk::cairo::Operator::Over);
|
||||
|
||||
// 1. Drop shadow (soft dark shadow offset down by 4px)
|
||||
cr.set_source_rgba(0.0, 0.0, 0.0, 0.60);
|
||||
cr.rectangle(0.0, 4.0, w, card_h);
|
||||
// Sleek translucent neutral dark glass background (matching HUD cards)
|
||||
cr.set_source_rgba(10.0 / 255.0, 10.0 / 255.0, 10.0 / 255.0, 0.65);
|
||||
cr.rectangle(0.0, 0.0, w, h);
|
||||
let _ = cr.fill();
|
||||
|
||||
// 2. Solid obsidian dark slate background (matching egui rgba(14, 16, 21, 0.96))
|
||||
cr.set_source_rgba(14.0 / 255.0, 16.0 / 255.0, 21.0 / 255.0, 0.96);
|
||||
cr.rectangle(0.0, 0.0, w, card_h);
|
||||
let _ = cr.fill();
|
||||
|
||||
// 3. Subtle white border (1px inside)
|
||||
cr.set_source_rgba(1.0, 1.0, 1.0, 0.14);
|
||||
// Refined subtle accent stroke framing the card
|
||||
cr.set_source_rgba(active_accent.0, active_accent.1, active_accent.2, 0.55);
|
||||
cr.set_line_width(1.0);
|
||||
cr.rectangle(0.5, 0.5, w - 1.0, card_h - 1.0);
|
||||
cr.rectangle(0.5, 0.5, w - 1.0, h - 1.0);
|
||||
let _ = cr.stroke();
|
||||
|
||||
// 4. Left accent bar (4.0px width)
|
||||
cr.set_source_rgb(active_accent.0, active_accent.1, active_accent.2);
|
||||
cr.rectangle(0.0, 0.0, 4.0, card_h);
|
||||
let _ = cr.fill();
|
||||
|
||||
gtk::glib::Propagation::Proceed
|
||||
});
|
||||
|
||||
|
|
@ -355,75 +342,75 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
let cy = 16.0;
|
||||
match icon_type {
|
||||
ToastIcon::Replay => {
|
||||
let r = 10.0;
|
||||
cr.set_source_rgb(accent_rgb.0, accent_rgb.1, accent_rgb.2);
|
||||
cr.set_line_width(2.2);
|
||||
cr.arc(cx, cy, r, 0.25 * PI, 1.80 * PI);
|
||||
let _ = cr.stroke();
|
||||
|
||||
let a_x = cx + r * (0.25 * PI).cos();
|
||||
let a_y = cy + r * (0.25 * PI).sin();
|
||||
cr.move_to(a_x, a_y);
|
||||
cr.line_to(a_x - 4.5, a_y + 0.5);
|
||||
cr.line_to(a_x - 0.5, a_y - 4.5);
|
||||
cr.close_path();
|
||||
let _ = cr.fill();
|
||||
|
||||
let tri_r = 4.0;
|
||||
cr.move_to(cx + tri_r + 0.5, cy);
|
||||
cr.line_to(cx - tri_r * 0.6 + 0.5, cy - tri_r * 0.86);
|
||||
cr.line_to(cx - tri_r * 0.6 + 0.5, cy + tri_r * 0.86);
|
||||
cr.close_path();
|
||||
let _ = cr.fill();
|
||||
cr.set_line_width(1.8);
|
||||
cr.set_line_cap(gtk::cairo::LineCap::Round);
|
||||
cr.set_line_join(gtk::cairo::LineJoin::Round);
|
||||
let ch_h = 7.0;
|
||||
let ch_w = 4.4;
|
||||
let gap = 4.2;
|
||||
let start_x = cx - (ch_w + gap) * 0.5;
|
||||
for i in 0..2 {
|
||||
let tip_x = start_x + (i as f64) * gap;
|
||||
let base_x = tip_x + ch_w;
|
||||
cr.move_to(base_x, cy - ch_h);
|
||||
cr.line_to(tip_x, cy);
|
||||
cr.line_to(base_x, cy + ch_h);
|
||||
let _ = cr.stroke();
|
||||
}
|
||||
}
|
||||
ToastIcon::Record => {
|
||||
cr.set_source_rgba(0.937, 0.267, 0.267, 0.25);
|
||||
cr.arc(cx, cy, 13.0, 0.0, PI * 2.0);
|
||||
let _ = cr.fill();
|
||||
|
||||
cr.set_source_rgb(0.937, 0.267, 0.267);
|
||||
cr.set_line_width(1.8);
|
||||
cr.arc(cx, cy, 10.0, 0.0, PI * 2.0);
|
||||
let red = (0.937, 0.267, 0.267);
|
||||
cr.set_source_rgb(red.0, red.1, red.2);
|
||||
cr.set_line_width(1.6);
|
||||
let half = 8.5;
|
||||
let arm = 3.2;
|
||||
cr.move_to(cx - half + arm, cy - half); cr.line_to(cx - half, cy - half); cr.line_to(cx - half, cy - half + arm);
|
||||
cr.move_to(cx + half - arm, cy - half); cr.line_to(cx + half, cy - half); cr.line_to(cx + half, cy - half + arm);
|
||||
cr.move_to(cx - half + arm, cy + half); cr.line_to(cx - half, cy + half); cr.line_to(cx - half, cy + half - arm);
|
||||
cr.move_to(cx + half - arm, cy + half); cr.line_to(cx + half, cy + half); cr.line_to(cx + half, cy + half - arm);
|
||||
let _ = cr.stroke();
|
||||
|
||||
cr.arc(cx, cy, 5.0, 0.0, PI * 2.0);
|
||||
cr.arc(cx, cy, 3.5, 0.0, PI * 2.0);
|
||||
let _ = cr.fill();
|
||||
}
|
||||
ToastIcon::Save => {
|
||||
cr.set_source_rgb(accent_rgb.0, accent_rgb.1, accent_rgb.2);
|
||||
cr.set_line_width(2.5);
|
||||
cr.set_line_width(2.0);
|
||||
cr.set_line_cap(gtk::cairo::LineCap::Round);
|
||||
cr.set_line_join(gtk::cairo::LineJoin::Round);
|
||||
cr.move_to(cx - 7.0, cy);
|
||||
cr.line_to(cx - 2.0, cy + 5.0);
|
||||
cr.line_to(cx + 7.0, cy - 5.0);
|
||||
cr.move_to(cx - 6.0, cy + 0.5);
|
||||
cr.line_to(cx - 2.0, cy + 4.5);
|
||||
cr.line_to(cx + 6.0, cy - 4.5);
|
||||
let _ = cr.stroke();
|
||||
}
|
||||
ToastIcon::Cursor => {
|
||||
cr.set_source_rgb(accent_rgb.0, accent_rgb.1, accent_rgb.2);
|
||||
cr.set_line_width(1.8);
|
||||
cr.move_to(cx - 6.0, cy - 8.0);
|
||||
cr.line_to(cx + 6.0, cy - 1.0);
|
||||
cr.line_to(cx, cy + 1.0);
|
||||
cr.line_to(cx + 2.0, cy + 7.0);
|
||||
cr.line_to(cx - 1.0, cy + 8.0);
|
||||
cr.line_to(cx - 3.0, cy + 2.0);
|
||||
cr.line_to(cx - 6.0, cy + 4.0);
|
||||
cr.set_line_cap(gtk::cairo::LineCap::Round);
|
||||
cr.set_line_join(gtk::cairo::LineJoin::Round);
|
||||
cr.move_to(cx - 5.0, cy - 7.0);
|
||||
cr.line_to(cx - 5.0, cy + 5.0);
|
||||
cr.line_to(cx - 1.5, cy + 2.0);
|
||||
cr.line_to(cx + 1.5, cy + 7.0);
|
||||
cr.line_to(cx + 3.5, cy + 6.0);
|
||||
cr.line_to(cx + 0.5, cy + 1.0);
|
||||
cr.line_to(cx + 5.0, cy + 1.0);
|
||||
cr.close_path();
|
||||
let _ = cr.fill();
|
||||
let _ = cr.stroke();
|
||||
}
|
||||
ToastIcon::Error => {
|
||||
cr.set_source_rgb(0.937, 0.267, 0.267);
|
||||
cr.set_line_width(2.2);
|
||||
cr.move_to(cx - 6.0, cy - 6.0);
|
||||
cr.line_to(cx + 6.0, cy + 6.0);
|
||||
cr.move_to(cx + 6.0, cy - 6.0);
|
||||
cr.line_to(cx - 6.0, cy + 6.0);
|
||||
cr.set_line_width(2.0);
|
||||
cr.set_line_cap(gtk::cairo::LineCap::Round);
|
||||
cr.move_to(cx - 5.0, cy - 5.0);
|
||||
cr.line_to(cx + 5.0, cy + 5.0);
|
||||
cr.move_to(cx + 5.0, cy - 5.0);
|
||||
cr.line_to(cx - 5.0, cy + 5.0);
|
||||
let _ = cr.stroke();
|
||||
}
|
||||
ToastIcon::Info => {
|
||||
cr.set_source_rgb(accent_rgb.0, accent_rgb.1, accent_rgb.2);
|
||||
cr.arc(cx, cy, 6.0, 0.0, PI * 2.0);
|
||||
cr.arc(cx, cy, 4.5, 0.0, PI * 2.0);
|
||||
let _ = cr.fill();
|
||||
}
|
||||
}
|
||||
|
|
@ -437,14 +424,14 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
|
||||
let title_lbl = Label::new(None);
|
||||
title_lbl.set_markup(&format!(
|
||||
"<span font_desc='monospace bold 10.5' color='#ffffff'>{}</span>",
|
||||
"<span font_desc='sans bold 10.5' color='#ffffff'>{}</span>",
|
||||
gtk::glib::markup_escape_text(&title_text)
|
||||
));
|
||||
title_lbl.set_halign(gtk::Align::Start);
|
||||
|
||||
let sub_lbl = Label::new(None);
|
||||
sub_lbl.set_markup(&format!(
|
||||
"<span font_desc='sans 9' color='#a1a1aa'>{}</span>",
|
||||
"<span font_desc='sans 9.5' color='#b4b9c4'>{}</span>",
|
||||
gtk::glib::markup_escape_text(&sub_text)
|
||||
));
|
||||
sub_lbl.set_halign(gtk::Align::Start);
|
||||
|
|
@ -456,8 +443,8 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
hbox.pack_start(&vbox, true, true, 0);
|
||||
|
||||
let fixed = Fixed::new();
|
||||
fixed.set_size_request(360, 64);
|
||||
fixed.put(&hbox, 360, 4);
|
||||
fixed.set_size_request(320, 56);
|
||||
fixed.put(&hbox, 320, 0);
|
||||
window.add(&fixed);
|
||||
window.show_all();
|
||||
|
||||
|
|
@ -476,17 +463,17 @@ pub fn show_shadowplay_toast(title: &str, subtitle: &str, icon: ToastIcon) {
|
|||
let slide_x = if elapsed < 0.35 {
|
||||
let t = (elapsed / 0.35).min(1.0);
|
||||
let ease = 1.0 - (1.0 - t).powi(3);
|
||||
(1.0 - ease) * 360.0
|
||||
(1.0 - ease) * 320.0
|
||||
} else if elapsed < 2.40 {
|
||||
0.0
|
||||
} else {
|
||||
let t = ((elapsed - 2.40) / 0.40).min(1.0);
|
||||
let ease = t.powi(3);
|
||||
ease * 360.0
|
||||
ease * 320.0
|
||||
};
|
||||
|
||||
let target_x = (slide_x + 14.0).round() as i32;
|
||||
fixed_clone.move_(&hbox_clone, target_x, 4);
|
||||
let target_x = slide_x.round() as i32;
|
||||
fixed_clone.move_(&hbox_clone, target_x, 0);
|
||||
window_clone.queue_draw();
|
||||
gtk::glib::ControlFlow::Continue
|
||||
});
|
||||
|
|
|
|||
|
|
@ -525,7 +525,7 @@ fn draw_replay_icon(painter: &egui::Painter, center: egui::Pos2, radius: f32, is
|
|||
};
|
||||
|
||||
// Minimalist Line Chevrons (≪) - Crisp, high-tech stroked rewind glyph
|
||||
let stroke_w = 2.4_f32;
|
||||
let stroke_w = (radius * 0.10).clamp(1.7, 2.4);
|
||||
let stroke = Stroke::new(stroke_w, color);
|
||||
let chevron_h = radius * 0.62;
|
||||
let chevron_w = radius * 0.38;
|
||||
|
|
@ -2173,72 +2173,91 @@ impl ScytheOverlayApp {
|
|||
let card_w = 320.0;
|
||||
let card_h = 56.0;
|
||||
let toast_rect = egui::Rect::from_min_size(
|
||||
egui::pos2(screen_w - card_w - 28.0 + slide_x, 28.0),
|
||||
egui::pos2(screen_w - card_w + slide_x, 16.0),
|
||||
egui::vec2(card_w, card_h),
|
||||
);
|
||||
|
||||
let painter = ui.painter();
|
||||
|
||||
// Drop shadow
|
||||
// Sleek translucent neutral dark glass toast card (matching HUD cards)
|
||||
painter.rect_filled(
|
||||
toast_rect.translate(Vec2::new(0.0, 4.0)),
|
||||
CornerRadius::ZERO,
|
||||
Color32::from_rgba_unmultiplied(0, 0, 0, 140),
|
||||
);
|
||||
|
||||
// Modern sleek dark glass toast card
|
||||
painter.rect(
|
||||
toast_rect,
|
||||
CornerRadius::ZERO,
|
||||
Color32::from_rgba_unmultiplied(12, 13, 16, 252),
|
||||
Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 30)),
|
||||
Color32::from_rgba_unmultiplied(10, 10, 10, 165),
|
||||
);
|
||||
|
||||
// Refined subtle accent stroke framing the card
|
||||
let stroke_color = if notif.icon == crate::overlay::ToastIcon::Record || notif.icon == crate::overlay::ToastIcon::Error {
|
||||
Color32::from_rgba_unmultiplied(239, 68, 68, 140)
|
||||
} else {
|
||||
Color32::from_rgba_unmultiplied(accent.r(), accent.g(), accent.b(), 140)
|
||||
};
|
||||
painter.rect_stroke(
|
||||
toast_rect,
|
||||
CornerRadius::ZERO,
|
||||
Stroke::new(1.0_f32, stroke_color),
|
||||
egui::StrokeKind::Inside,
|
||||
);
|
||||
|
||||
// Left vertical accent bar
|
||||
let line_color = if notif.icon == crate::overlay::ToastIcon::Record {
|
||||
Color32::from_rgb(239, 68, 68)
|
||||
} else {
|
||||
accent
|
||||
};
|
||||
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);
|
||||
|
||||
// Left icon area (32x32 at center-left)
|
||||
// Left icon area (centered vertically at x = 24.0)
|
||||
let icon_center = egui::pos2(toast_rect.left() + 24.0, toast_rect.center().y);
|
||||
match notif.icon {
|
||||
crate::overlay::ToastIcon::Replay => {
|
||||
draw_replay_icon(painter, icon_center, 9.5, true, accent);
|
||||
draw_replay_icon(painter, icon_center, 12.0, true, accent);
|
||||
}
|
||||
crate::overlay::ToastIcon::Record => {
|
||||
painter.circle_filled(icon_center, 5.5, Color32::from_rgb(239, 68, 68));
|
||||
painter.circle_stroke(icon_center, 9.5, Stroke::new(1.2_f32, Color32::from_rgba_unmultiplied(239, 68, 68, 120)));
|
||||
let red_bright = Color32::from_rgb(239, 68, 68);
|
||||
let half = 8.5;
|
||||
let arm = 3.2;
|
||||
let stroke = Stroke::new(1.6_f32, red_bright);
|
||||
painter.line_segment([icon_center + Vec2::new(-half + arm, -half), icon_center + Vec2::new(-half, -half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(-half, -half), icon_center + Vec2::new(-half, -half + arm)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half - arm, -half), icon_center + Vec2::new(half, -half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half, -half), icon_center + Vec2::new(half, -half + arm)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(-half + arm, half), icon_center + Vec2::new(-half, half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(-half, half), icon_center + Vec2::new(-half, half - arm)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half - arm, half), icon_center + Vec2::new(half, half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half, half), icon_center + Vec2::new(half, half - arm)], stroke);
|
||||
painter.circle_filled(icon_center, 3.5, red_bright);
|
||||
}
|
||||
crate::overlay::ToastIcon::Save => {
|
||||
let c = icon_center;
|
||||
let p1 = c + Vec2::new(-5.5, 0.0);
|
||||
let p2 = c + Vec2::new(-1.5, 4.0);
|
||||
let p3 = c + Vec2::new(5.5, -4.0);
|
||||
painter.line_segment([p1, p2], Stroke::new(2.2_f32, accent));
|
||||
painter.line_segment([p2, p3], Stroke::new(2.2_f32, accent));
|
||||
painter.add(egui::epaint::PathShape::line(
|
||||
vec![
|
||||
icon_center + Vec2::new(-6.0, 0.5),
|
||||
icon_center + Vec2::new(-2.0, 4.5),
|
||||
icon_center + Vec2::new(6.0, -4.5),
|
||||
],
|
||||
Stroke::new(2.0_f32, accent),
|
||||
));
|
||||
}
|
||||
crate::overlay::ToastIcon::Cursor => {
|
||||
let c = icon_center;
|
||||
painter.text(c, egui::Align2::CENTER_CENTER, "⯈", FontId::proportional(14.0), accent);
|
||||
painter.add(egui::epaint::PathShape::line(
|
||||
vec![
|
||||
icon_center + Vec2::new(-5.0, -7.0),
|
||||
icon_center + Vec2::new(-5.0, 5.0),
|
||||
icon_center + Vec2::new(-1.5, 2.0),
|
||||
icon_center + Vec2::new(1.5, 7.0),
|
||||
icon_center + Vec2::new(3.5, 6.0),
|
||||
icon_center + Vec2::new(0.5, 1.0),
|
||||
icon_center + Vec2::new(5.0, 1.0),
|
||||
icon_center + Vec2::new(-5.0, -7.0),
|
||||
],
|
||||
Stroke::new(1.8_f32, accent),
|
||||
));
|
||||
}
|
||||
crate::overlay::ToastIcon::Error => {
|
||||
let c = icon_center;
|
||||
let red = Color32::from_rgb(239, 68, 68);
|
||||
painter.line_segment([c + Vec2::new(-4.0, -4.0), c + Vec2::new(4.0, 4.0)], Stroke::new(2.0_f32, red));
|
||||
painter.line_segment([c + Vec2::new(4.0, -4.0), c + Vec2::new(-4.0, 4.0)], Stroke::new(2.0_f32, red));
|
||||
let stroke = Stroke::new(2.0_f32, red);
|
||||
painter.line_segment([icon_center + Vec2::new(-5.0, -5.0), icon_center + Vec2::new(5.0, 5.0)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(5.0, -5.0), icon_center + Vec2::new(-5.0, 5.0)], stroke);
|
||||
}
|
||||
crate::overlay::ToastIcon::Info => {
|
||||
painter.circle_filled(icon_center, 5.0, accent);
|
||||
painter.circle_filled(icon_center, 4.5, accent);
|
||||
}
|
||||
}
|
||||
|
||||
// Text stack (Title + Subtitle)
|
||||
let text_left = toast_rect.left() + 48.0;
|
||||
let text_left = toast_rect.left() + 46.0;
|
||||
painter.text(
|
||||
egui::pos2(text_left, toast_rect.top() + 19.0),
|
||||
egui::Align2::LEFT_CENTER,
|
||||
|
|
@ -2251,7 +2270,7 @@ impl ScytheOverlayApp {
|
|||
egui::Align2::LEFT_CENTER,
|
||||
¬if.subtitle,
|
||||
FontId::proportional(11.0),
|
||||
Color32::from_rgb(160, 160, 168),
|
||||
Color32::from_rgb(175, 180, 190),
|
||||
);
|
||||
} else {
|
||||
self.hud_notification = None;
|
||||
|
|
@ -2641,13 +2660,13 @@ impl eframe::App for ShadowPlayToastApp {
|
|||
let slide_x = if elapsed < 0.35 {
|
||||
let t = (elapsed / 0.35).min(1.0);
|
||||
let ease = 1.0 - (1.0 - t).powi(3);
|
||||
(1.0 - ease) * 340.0
|
||||
(1.0 - ease) * 320.0
|
||||
} else if elapsed < total_dur - 0.40 {
|
||||
0.0
|
||||
} else {
|
||||
let t = ((elapsed - (total_dur - 0.40)) / 0.40).min(1.0);
|
||||
let ease = t.powi(3);
|
||||
ease * 340.0
|
||||
ease * 320.0
|
||||
};
|
||||
|
||||
let mut visuals = egui::Visuals::dark();
|
||||
|
|
@ -2659,71 +2678,90 @@ impl eframe::App for ShadowPlayToastApp {
|
|||
.frame(egui::Frame::NONE.fill(Color32::TRANSPARENT))
|
||||
.show(ctx, |ui| {
|
||||
let rect = egui::Rect::from_min_size(
|
||||
egui::pos2(slide_x + 10.0, 4.0),
|
||||
egui::pos2(slide_x, 0.0),
|
||||
Vec2::new(320.0, 56.0),
|
||||
);
|
||||
let painter = ui.painter();
|
||||
|
||||
// Drop shadow
|
||||
// Sleek translucent neutral dark glass toast card (matching HUD cards)
|
||||
painter.rect_filled(
|
||||
rect.translate(Vec2::new(0.0, 4.0)),
|
||||
CornerRadius::ZERO,
|
||||
Color32::from_rgba_unmultiplied(0, 0, 0, 140),
|
||||
);
|
||||
|
||||
// Modern sleek dark glass toast card
|
||||
painter.rect(
|
||||
rect,
|
||||
CornerRadius::ZERO,
|
||||
Color32::from_rgba_unmultiplied(12, 13, 16, 252),
|
||||
Stroke::new(1.0_f32, Color32::from_rgba_unmultiplied(255, 255, 255, 30)),
|
||||
Color32::from_rgba_unmultiplied(10, 10, 10, 165),
|
||||
);
|
||||
|
||||
// Refined subtle accent stroke framing the card
|
||||
let stroke_color = if self.icon == crate::overlay::ToastIcon::Record || self.icon == crate::overlay::ToastIcon::Error {
|
||||
Color32::from_rgba_unmultiplied(239, 68, 68, 140)
|
||||
} else {
|
||||
Color32::from_rgba_unmultiplied(self.accent.r(), self.accent.g(), self.accent.b(), 140)
|
||||
};
|
||||
painter.rect_stroke(
|
||||
rect,
|
||||
CornerRadius::ZERO,
|
||||
Stroke::new(1.0_f32, stroke_color),
|
||||
egui::StrokeKind::Inside,
|
||||
);
|
||||
|
||||
// Left accent line
|
||||
let line_color = if self.icon == crate::overlay::ToastIcon::Record {
|
||||
Color32::from_rgb(239, 68, 68)
|
||||
} else {
|
||||
self.accent
|
||||
};
|
||||
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);
|
||||
|
||||
// Left icon area (32x32 at center-left)
|
||||
// Left icon area (centered vertically at x = 24.0)
|
||||
let icon_center = egui::pos2(rect.left() + 24.0, rect.center().y);
|
||||
match self.icon {
|
||||
crate::overlay::ToastIcon::Replay => {
|
||||
draw_replay_icon(painter, icon_center, 9.5, true, self.accent);
|
||||
draw_replay_icon(painter, icon_center, 12.0, true, self.accent);
|
||||
}
|
||||
crate::overlay::ToastIcon::Record => {
|
||||
painter.circle_filled(icon_center, 5.5, Color32::from_rgb(239, 68, 68));
|
||||
painter.circle_stroke(icon_center, 9.5, Stroke::new(1.2_f32, Color32::from_rgba_unmultiplied(239, 68, 68, 120)));
|
||||
let red_bright = Color32::from_rgb(239, 68, 68);
|
||||
let half = 8.5;
|
||||
let arm = 3.2;
|
||||
let stroke = Stroke::new(1.6_f32, red_bright);
|
||||
painter.line_segment([icon_center + Vec2::new(-half + arm, -half), icon_center + Vec2::new(-half, -half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(-half, -half), icon_center + Vec2::new(-half, -half + arm)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half - arm, -half), icon_center + Vec2::new(half, -half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half, -half), icon_center + Vec2::new(half, -half + arm)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(-half + arm, half), icon_center + Vec2::new(-half, half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(-half, half), icon_center + Vec2::new(-half, half - arm)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half - arm, half), icon_center + Vec2::new(half, half)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(half, half), icon_center + Vec2::new(half, half - arm)], stroke);
|
||||
painter.circle_filled(icon_center, 3.5, red_bright);
|
||||
}
|
||||
crate::overlay::ToastIcon::Save => {
|
||||
let c = icon_center;
|
||||
let p1 = c + Vec2::new(-5.5, 0.0);
|
||||
let p2 = c + Vec2::new(-1.5, 4.0);
|
||||
let p3 = c + Vec2::new(5.5, -4.0);
|
||||
painter.line_segment([p1, p2], Stroke::new(2.2_f32, self.accent));
|
||||
painter.line_segment([p2, p3], Stroke::new(2.2_f32, self.accent));
|
||||
painter.add(egui::epaint::PathShape::line(
|
||||
vec![
|
||||
icon_center + Vec2::new(-6.0, 0.5),
|
||||
icon_center + Vec2::new(-2.0, 4.5),
|
||||
icon_center + Vec2::new(6.0, -4.5),
|
||||
],
|
||||
Stroke::new(2.0_f32, self.accent),
|
||||
));
|
||||
}
|
||||
crate::overlay::ToastIcon::Cursor => {
|
||||
let c = icon_center;
|
||||
painter.text(c, egui::Align2::CENTER_CENTER, "⯈", FontId::proportional(14.0), self.accent);
|
||||
painter.add(egui::epaint::PathShape::line(
|
||||
vec![
|
||||
icon_center + Vec2::new(-5.0, -7.0),
|
||||
icon_center + Vec2::new(-5.0, 5.0),
|
||||
icon_center + Vec2::new(-1.5, 2.0),
|
||||
icon_center + Vec2::new(1.5, 7.0),
|
||||
icon_center + Vec2::new(3.5, 6.0),
|
||||
icon_center + Vec2::new(0.5, 1.0),
|
||||
icon_center + Vec2::new(5.0, 1.0),
|
||||
icon_center + Vec2::new(-5.0, -7.0),
|
||||
],
|
||||
Stroke::new(1.8_f32, self.accent),
|
||||
));
|
||||
}
|
||||
crate::overlay::ToastIcon::Error => {
|
||||
let c = icon_center;
|
||||
let red = Color32::from_rgb(239, 68, 68);
|
||||
painter.line_segment([c + Vec2::new(-4.0, -4.0), c + Vec2::new(4.0, 4.0)], Stroke::new(2.0_f32, red));
|
||||
painter.line_segment([c + Vec2::new(4.0, -4.0), c + Vec2::new(-4.0, 4.0)], Stroke::new(2.0_f32, red));
|
||||
let stroke = Stroke::new(2.0_f32, red);
|
||||
painter.line_segment([icon_center + Vec2::new(-5.0, -5.0), icon_center + Vec2::new(5.0, 5.0)], stroke);
|
||||
painter.line_segment([icon_center + Vec2::new(5.0, -5.0), icon_center + Vec2::new(-5.0, 5.0)], stroke);
|
||||
}
|
||||
crate::overlay::ToastIcon::Info => {
|
||||
painter.circle_filled(icon_center, 5.0, self.accent);
|
||||
painter.circle_filled(icon_center, 4.5, self.accent);
|
||||
}
|
||||
}
|
||||
|
||||
// Text stack (Title + Subtitle)
|
||||
let text_left = rect.left() + 48.0;
|
||||
let text_left = rect.left() + 46.0;
|
||||
painter.text(
|
||||
egui::pos2(text_left, rect.top() + 19.0),
|
||||
egui::Align2::LEFT_CENTER,
|
||||
|
|
@ -2736,7 +2774,7 @@ impl eframe::App for ShadowPlayToastApp {
|
|||
egui::Align2::LEFT_CENTER,
|
||||
&self.subtitle,
|
||||
FontId::proportional(11.0),
|
||||
Color32::from_rgb(160, 160, 168),
|
||||
Color32::from_rgb(175, 180, 190),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -2746,8 +2784,8 @@ pub fn run_egui_toast(title: &str, subtitle: &str, icon: crate::overlay::ToastIc
|
|||
let cfg = ScytheConfig::load();
|
||||
let accent = resolve_accent_color(&cfg.accent_color);
|
||||
|
||||
let toast_w: f32 = 340.0;
|
||||
let toast_h: f32 = 64.0;
|
||||
let toast_w: f32 = 320.0;
|
||||
let toast_h: f32 = 56.0;
|
||||
|
||||
let toast_pid_path = crate::ipc::get_toast_pid_path();
|
||||
if let Ok(prev_pid_str) = std::fs::read_to_string(&toast_pid_path)
|
||||
|
|
@ -2778,8 +2816,8 @@ pub fn run_egui_toast(title: &str, subtitle: &str, icon: crate::overlay::ToastIc
|
|||
#[cfg(not(target_os = "windows"))]
|
||||
let (mon_x, _mon_y, screen_w, _screen_h) = query_focused_monitor_rect();
|
||||
|
||||
let pos_x = (mon_x + screen_w - toast_w - 28.0).max(10.0);
|
||||
let pos_y = 28.0;
|
||||
let pos_x = (mon_x + screen_w - toast_w).max(0.0);
|
||||
let pos_y = 16.0;
|
||||
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue