use super::{MenuEntry, MenuState, MenuWindow};
use super::{BG_ALPHA, CHEVRON_W, HOVER_INSET, HOVER_RADIUS, ICON_X, RIGHT_PAD, TEXT_X};
use windows_sys::Win32::Foundation::RECT;
use windows_sys::Win32::Graphics::Gdi::{
FillRect, SelectObject, SetTextColor, DT_CENTER, DT_END_ELLIPSIS, DT_NOPREFIX, DT_RIGHT,
DT_SINGLELINE, DT_VCENTER, HDC,
};
use winspaces_win32::dpi::px;
use winspaces_win32::gdi::color::Tint;
use winspaces_win32::gdi::draw::{draw_text_raw, round_rect_with};
use winspaces_win32::gdi::guard::SelectGuard;
use winspaces_win32::gdi::surface::paint_surface_clipped;
use winspaces_win32::glyphs::{GLYPH_CHECK, GLYPH_CHEVRON};
unsafe fn draw_text(hdc: HDC, text: &str, rect: &mut RECT, flags: u32) {
draw_text_raw(hdc, text, rect, flags | DT_NOPREFIX);
}
unsafe fn draw_glyph(hdc: HDC, glyph: u16, rect: &mut RECT, flags: u32) {
let mut ch = String::with_capacity(1);
ch.push(char::from_u32(glyph as u32).unwrap_or('\u{FFFD}'));
draw_text_raw(hdc, &ch, rect, flags | DT_NOPREFIX);
}
pub(crate) unsafe fn render_window(win: &MenuWindow, state: &MenuState, target: HDC, rc: &RECT) {
let w = win.width;
let px = |v: i32| px(state.scale, v);
let theme = &state.theme;
let tint = Tint {
r: theme.bg_r,
g: theme.bg_g,
b: theme.bg_b,
alpha: if state.acrylic { BG_ALPHA } else { 255 },
};
paint_surface_clipped(target, rc, &tint, |hdc_mem| {
let hover_brush = state.paints.hover_brush;
let hover_pen = state.paints.hover_pen;
let sep_brush = state.paints.sep_brush;
let any_chevron = win
.entries
.iter()
.any(|e| matches!(e, MenuEntry::Item(it) if it.submenu.is_some()));
for (idx, entry) in win.entries.iter().enumerate() {
let row = win.rows[idx];
match entry {
MenuEntry::Header(text) => {
SelectObject(hdc_mem, state.fonts.small as _);
SetTextColor(hdc_mem, theme.dim);
let mut r = RECT {
left: px(ICON_X),
top: row.top,
right: w - px(RIGHT_PAD),
bottom: row.top + row.height,
};
draw_text(
hdc_mem,
text,
&mut r,
DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS,
);
}
MenuEntry::Separator => {
let line_y = row.top + row.height / 2;
let r = RECT {
left: px(12),
top: line_y,
right: w - px(12),
bottom: line_y + px(1).max(1),
};
FillRect(hdc_mem, &r, sep_brush);
}
MenuEntry::Item(it) => {
let highlighted = win.hover == Some(idx) || win.sel == Some(idx);
if highlighted {
let hover_r = RECT {
left: px(HOVER_INSET),
top: row.top + px(2),
right: w - px(HOVER_INSET),
bottom: row.top + row.height - px(2),
};
let _brush_guard = SelectGuard::new(hdc_mem, hover_brush as _);
let _pen_guard = SelectGuard::new(hdc_mem, hover_pen as _);
round_rect_with(
hdc_mem,
&hover_r,
hover_brush,
hover_pen,
px(HOVER_RADIUS),
);
}
let icon_glyph = if it.checked {
Some(GLYPH_CHECK)
} else {
it.glyph
};
if let Some(g) = icon_glyph {
SelectObject(hdc_mem, state.fonts.glyph as _);
SetTextColor(hdc_mem, theme.text);
let mut r = RECT {
left: px(ICON_X),
top: row.top,
right: px(TEXT_X) - px(6),
bottom: row.top + row.height,
};
draw_glyph(hdc_mem, g, &mut r, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
let chevron_space = if any_chevron { px(CHEVRON_W) } else { 0 };
let right_edge = w - px(RIGHT_PAD) - chevron_space;
SelectObject(hdc_mem, state.fonts.text as _);
SetTextColor(hdc_mem, theme.text);
let mut r = RECT {
left: px(TEXT_X),
top: row.top,
right: right_edge,
bottom: row.top + row.height,
};
draw_text(
hdc_mem,
&it.label,
&mut r,
DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS,
);
if let Some(shortcut) = &it.shortcut {
SelectObject(hdc_mem, state.fonts.small as _);
SetTextColor(hdc_mem, theme.dim);
let mut r = RECT {
left: px(TEXT_X),
top: row.top,
right: right_edge,
bottom: row.top + row.height,
};
draw_text(
hdc_mem,
shortcut,
&mut r,
DT_SINGLELINE | DT_VCENTER | DT_RIGHT,
);
}
if it.submenu.is_some() {
SelectObject(hdc_mem, state.fonts.glyph_small as _);
SetTextColor(hdc_mem, theme.dim);
let mut r = RECT {
left: w - px(RIGHT_PAD) - px(16),
top: row.top,
right: w - px(RIGHT_PAD) + px(4),
bottom: row.top + row.height,
};
draw_glyph(
hdc_mem,
GLYPH_CHEVRON,
&mut r,
DT_SINGLELINE | DT_VCENTER | DT_CENTER,
);
}
}
}
}
});
}