use crate::theme::Palette;
use windows_sys::Win32::Foundation::RECT;
use windows_sys::Win32::Graphics::Gdi::{
CreatePen, CreateSolidBrush, DeleteObject, Ellipse, GetStockObject, RoundRect, DT_CENTER,
DT_END_ELLIPSIS, DT_LEFT, DT_SINGLELINE, DT_VCENTER, HBRUSH, HDC, HFONT, HGDIOBJ, HPEN,
NULL_BRUSH, PS_SOLID,
};
use winspaces_win32::gdi::draw::{draw_glyph_in, draw_text_in, fill_round};
use winspaces_win32::gdi::font::{create_font, FACE_DISPLAY, FACE_ICONS, FACE_TEXT};
use winspaces_win32::gdi::guard::{GdiObject, SelectGuard};
use winspaces_win32::glyphs::GLYPH_CHEVRON_DOWN;
pub struct Fonts {
pub title: HFONT,
pub subtitle: HFONT,
pub body: HFONT,
pub body_strong: HFONT,
pub caption: HFONT,
pub glyph: HFONT,
pub glyph_small: HFONT,
}
pub unsafe fn create_fonts(scale: f32) -> Fonts {
let px = |v: i32| (v as f32 * scale).round() as i32;
Fonts {
title: create_font(FACE_DISPLAY, -px(28), 600),
subtitle: create_font(FACE_DISPLAY, -px(20), 600),
body: create_font(FACE_TEXT, -px(14), 400),
body_strong: create_font(FACE_TEXT, -px(14), 600),
caption: create_font(FACE_TEXT, -px(12), 400),
glyph: create_font(FACE_ICONS, -px(18), 400),
glyph_small: create_font(FACE_ICONS, -px(12), 400),
}
}
pub unsafe fn delete_fonts(fonts: &Fonts) {
for f in [
fonts.title,
fonts.subtitle,
fonts.body,
fonts.body_strong,
fonts.caption,
fonts.glyph,
fonts.glyph_small,
] {
DeleteObject(f as _);
}
}
#[derive(Clone, Copy, Default)]
pub struct Vis {
pub hover: bool,
pub pressed: bool,
pub focus: bool,
}
pub unsafe fn draw_focus_ring(hdc: HDC, r: &RECT, pal: &Palette, px: i32) {
let pen =
GdiObject::<HPEN>::from_raw(CreatePen(PS_SOLID, (2 * px).max(2), pal.focus) as HGDIOBJ);
let pad = 2 * px;
{
let _pen_guard = SelectGuard::new(hdc, pen.as_raw());
let hollow = GetStockObject(NULL_BRUSH);
let _brush_guard = SelectGuard::new(hdc, hollow);
RoundRect(
hdc,
r.left - pad,
r.top - pad,
r.right + pad,
r.bottom + pad,
6 * px,
6 * px,
);
} }
pub unsafe fn draw_toggle(hdc: HDC, r: &RECT, on: bool, vis: Vis, pal: &Palette, px: i32) {
let h = r.bottom - r.top;
let radius = h;
if on {
fill_round(hdc, r, radius, pal.accent, pal.accent);
} else {
let fill = if vis.hover || vis.pressed {
pal.ctl_hover
} else {
pal.ctl
};
fill_round(hdc, r, radius, fill, pal.text_dim);
}
let knob_d = if vis.hover || vis.pressed {
14 * px
} else {
12 * px
};
let margin = (h - knob_d) / 2;
let (kl, kr) = if on {
(r.right - margin - knob_d, r.right - margin)
} else {
(r.left + margin, r.left + margin + knob_d)
};
let knob_color = if on { pal.accent_text } else { pal.text_dim };
let brush = GdiObject::<HBRUSH>::from_raw(CreateSolidBrush(knob_color) as HGDIOBJ);
let pen = GdiObject::<HPEN>::from_raw(CreatePen(PS_SOLID, 1, knob_color) as HGDIOBJ);
{
let _brush_guard = SelectGuard::new(hdc, brush.as_raw());
let _pen_guard = SelectGuard::new(hdc, pen.as_raw());
Ellipse(hdc, kl, r.top + margin, kr, r.bottom - margin);
}
if vis.focus {
draw_focus_ring(hdc, r, pal, px);
}
}
#[allow(clippy::too_many_arguments)]
pub unsafe fn draw_button(
hdc: HDC,
r: &RECT,
label: &str,
accent: bool,
vis: Vis,
pal: &Palette,
fonts: &Fonts,
px: i32,
) {
let (fill, stroke, text) = if accent {
(pal.accent, pal.accent, pal.accent_text)
} else if vis.pressed {
(pal.ctl_pressed, pal.ctl_stroke, pal.text_dim)
} else if vis.hover {
(pal.ctl_hover, pal.ctl_stroke, pal.text)
} else {
(pal.ctl, pal.ctl_stroke, pal.text)
};
fill_round(hdc, r, 4 * px, fill, stroke);
draw_text_in(
hdc,
fonts.body,
text,
r,
label,
DT_SINGLELINE | DT_VCENTER | DT_CENTER | DT_END_ELLIPSIS,
);
if vis.focus {
draw_focus_ring(hdc, r, pal, px);
}
}
#[allow(clippy::too_many_arguments)]
pub unsafe fn draw_field(
hdc: HDC,
r: &RECT,
label: &str,
capturing: bool,
chevron: bool,
vis: Vis,
pal: &Palette,
fonts: &Fonts,
px: i32,
) {
let (fill, stroke) = if capturing {
(pal.ctl, pal.accent)
} else if vis.pressed {
(pal.ctl_pressed, pal.ctl_stroke)
} else if vis.hover {
(pal.ctl_hover, pal.ctl_stroke)
} else {
(pal.ctl, pal.ctl_stroke)
};
fill_round(hdc, r, 4 * px, fill, stroke);
let text_color = if capturing { pal.text_dim } else { pal.text };
let mut text_rect = RECT {
left: r.left + 12 * px,
top: r.top,
right: r.right - if chevron { 28 * px } else { 12 * px },
bottom: r.bottom,
};
draw_text_in(
hdc,
fonts.body,
text_color,
&text_rect,
label,
DT_SINGLELINE | DT_VCENTER | DT_LEFT | DT_END_ELLIPSIS,
);
if chevron {
text_rect.left = r.right - 26 * px;
text_rect.right = r.right - 8 * px;
draw_glyph_in(
hdc,
fonts.glyph_small,
pal.text_dim,
&text_rect,
GLYPH_CHEVRON_DOWN,
DT_SINGLELINE | DT_VCENTER | DT_CENTER,
);
}
if vis.focus {
draw_focus_ring(hdc, r, pal, px);
}
}
#[allow(clippy::too_many_arguments)]
pub unsafe fn draw_pill(
hdc: HDC,
r: &RECT,
text: &str,
dot: u32,
bg: u32,
pal: &Palette,
fonts: &Fonts,
px: i32,
) {
let h = r.bottom - r.top;
fill_round(hdc, r, h, bg, bg);
let dot_d = 8 * px;
let dot_x = r.left + 12 * px;
let dot_y = r.top + (h - dot_d) / 2;
let brush = GdiObject::<HBRUSH>::from_raw(CreateSolidBrush(dot) as HGDIOBJ);
let pen = GdiObject::<HPEN>::from_raw(CreatePen(PS_SOLID, 1, dot) as HGDIOBJ);
{
let _brush_guard = SelectGuard::new(hdc, brush.as_raw());
let _pen_guard = SelectGuard::new(hdc, pen.as_raw());
Ellipse(hdc, dot_x, dot_y, dot_x + dot_d, dot_y + dot_d);
} let text_rect = RECT {
left: dot_x + dot_d + 8 * px,
top: r.top,
right: r.right - 12 * px,
bottom: r.bottom,
};
draw_text_in(
hdc,
fonts.caption,
pal.text,
&text_rect,
text,
DT_SINGLELINE | DT_VCENTER | DT_LEFT | DT_END_ELLIPSIS,
);
}