use crate::ui::viewer::app::App;
use eframe::egui::{Align, Color32, Context, CursorIcon, FontId, Frame, Layout, Pos2, Response, Sense, Shape, Stroke, TopBottomPanel, Ui, Vec2, ViewportCommand};
impl App {
pub(crate) fn draw_title_bar(&mut self, ctx: &Context) {
TopBottomPanel::top("custom_title_bar")
.show_separator_line(false)
.exact_height(30.0)
.frame(Frame { fill: Color32::from_rgba_unmultiplied(8, 32, 44, 200), ..Default::default() })
.show(ctx, |ui| {
let title_bar_rect = ui.max_rect();
let label_text = "blp";
let font = FontId::proportional(16.0);
let galley = ui.fonts_mut(|f| f.layout_no_wrap(label_text.to_string(), font.clone(), Color32::WHITE));
let pad_x = 10.0;
let pad_y = 3.0;
let text_pos = Pos2::new(
title_bar_rect.left() + pad_x, title_bar_rect.center().y - galley.size().y * 0.5,
);
let x0 = text_pos.x - pad_x;
let y0 = text_pos.y - pad_y;
let x1 = text_pos.x + galley.size().x + pad_x;
let y1 = text_pos.y + galley.size().y + pad_y;
let slope = 12.0;
let pts = vec![
Pos2::new(x0, y0), Pos2::new(x1 - slope, y0), Pos2::new(x1, (y0 + y1) * 0.5), Pos2::new(x1 - slope, y1), Pos2::new(x0, y1), ];
ui.painter().add(Shape::convex_polygon(
pts,
Color32::from_rgba_unmultiplied(10, 180, 250, 80), Stroke::NONE,
));
ui.painter()
.galley(text_pos, galley, Color32::WHITE);
let ver_text = env!("CARGO_PKG_VERSION");
let ver_galley = ui.fonts_mut(|f| f.layout_no_wrap(ver_text.to_string(), font.clone(), Color32::WHITE));
let ver_gap = 6.0; let ver_text_pos = Pos2::new(
x1 + ver_gap, title_bar_rect.center().y - ver_galley.size().y * 0.5,
);
ui.painter()
.galley(ver_text_pos, ver_galley, Color32::WHITE);
let (close_resp, zoom_resp, min_resp) = ui
.with_layout(Layout::right_to_left(Align::Center), |ui| {
ui.add_space(6.0);
let close_resp = macos_dot(ui, TrafficKind::Close).on_hover_cursor(CursorIcon::PointingHand);
ui.add_space(6.0);
let zoom_resp = macos_dot_zoom(ui, self.maximized).on_hover_cursor(CursorIcon::PointingHand);
ui.add_space(6.0);
let min_resp = macos_dot(ui, TrafficKind::Minimize).on_hover_cursor(CursorIcon::PointingHand);
(close_resp, zoom_resp, min_resp)
})
.inner;
if min_resp.clicked() {
ctx.send_viewport_cmd(ViewportCommand::Minimized(true));
}
if zoom_resp.clicked() {
self.maximized = !self.maximized;
ctx.send_viewport_cmd(ViewportCommand::Maximized(self.maximized));
}
if close_resp.clicked() {
ctx.send_viewport_cmd(ViewportCommand::Close);
}
if let Some(p) = ui.input(|i| i.pointer.hover_pos()) {
let over_btns = min_resp.rect.contains(p) || zoom_resp.rect.contains(p) || close_resp.rect.contains(p);
if title_bar_rect.contains(p) && !over_btns {
ui.output_mut(|o| o.cursor_icon = CursorIcon::Grab);
}
}
let pointer = ui.input(|i| i.pointer.clone());
if pointer.primary_down() {
if let Some(pos) = pointer.interact_pos() {
let over = min_resp.rect.contains(pos) || zoom_resp.rect.contains(pos) || close_resp.rect.contains(pos);
if title_bar_rect.contains(pos) && !over {
ctx.send_viewport_cmd(ViewportCommand::StartDrag);
}
}
}
});
}
}
#[derive(Copy, Clone)]
enum TrafficKind {
Close,
Minimize,
}
const MACOS_DOT_SIZE: f32 = 18.0;
fn macos_dot(ui: &mut Ui, kind: TrafficKind) -> Response {
let size = Vec2::splat(MACOS_DOT_SIZE);
let (rect, resp) = ui.allocate_exact_size(size, Sense::click());
let center = rect.center();
let (base, hover_stroke) = match kind {
TrafficKind::Close => (Color32::from_rgb(255, 95, 86), Color32::from_rgba_unmultiplied(0, 0, 0, 100)), TrafficKind::Minimize => (Color32::from_rgb(255, 189, 46), Color32::from_rgba_unmultiplied(0, 0, 0, 100)), };
ui.painter()
.circle_filled(center, MACOS_DOT_SIZE * 0.5, base);
if resp.hovered() {
ui.painter()
.circle_stroke(center, MACOS_DOT_SIZE * 0.5, Stroke { width: 1.0, color: hover_stroke });
match kind {
TrafficKind::Close => {
let r = MACOS_DOT_SIZE * 0.28;
ui.painter()
.line_segment([Pos2::new(center.x - r, center.y - r), Pos2::new(center.x + r, center.y + r)], Stroke { width: 1.5, color: Color32::BLACK });
ui.painter()
.line_segment([Pos2::new(center.x - r, center.y + r), Pos2::new(center.x + r, center.y - r)], Stroke { width: 1.5, color: Color32::BLACK });
}
TrafficKind::Minimize => {
let r = MACOS_DOT_SIZE * 0.30;
ui.painter()
.line_segment([Pos2::new(center.x - r, center.y), Pos2::new(center.x + r, center.y)], Stroke { width: 2.0, color: Color32::BLACK });
}
}
}
resp
}
fn macos_dot_zoom(ui: &mut Ui, inward: bool) -> Response {
let (rect, resp) = ui.allocate_exact_size(Vec2::splat(MACOS_DOT_SIZE), Sense::click());
let c = rect.center();
let r = MACOS_DOT_SIZE * 0.5;
ui.painter()
.circle_filled(c, r, Color32::from_rgb(39, 201, 63));
if resp.hovered() {
ui.painter()
.circle_stroke(c, r, Stroke { width: 1.0, color: Color32::from_rgba_unmultiplied(0, 0, 0, 100) });
let u = Vec2::new(1.0, 1.0).normalized();
let n = Vec2::new(-u.y, u.x);
let tip_off = if inward { 1. } else { r * 0.8 }; let height = r * 0.6; let base_w = r * 0.9;
let tri_tip = |tip: Pos2, dir: Vec2| {
let d = dir.normalized();
let base_c = tip - d * height;
let a = base_c + n * (base_w * 0.5);
let b = base_c - n * (base_w * 0.5);
ui.painter()
.add(Shape::convex_polygon(vec![tip, a, b], Color32::BLACK, Stroke::NONE));
};
let tip_tl = c - u * tip_off; let tip_br = c + u * tip_off;
if inward {
tri_tip(tip_tl, u); tri_tip(tip_br, -u); } else {
tri_tip(tip_tl, -u); tri_tip(tip_br, u); }
}
resp
}