Skip to main content

pacenotes_installer\ui/
forward_button.rs

1//! The buttons that carry the normal flow forward, painted light green so
2//! the eye finds them. A themed push button ignores the colours its parent
3//! offers, so these are drawn whole from the button's `NM_CUSTOMDRAW`.
4
5use windows_sys::Win32::Graphics::Gdi as gdi;
6use windows_sys::Win32::UI::Controls as ctl;
7use windows_sys::Win32::UI::WindowsAndMessaging as wm;
8
9const FILL: u32 = rgb(204, 240, 204);
10const FILL_HOT: u32 = rgb(182, 230, 182);
11const FILL_PRESSED: u32 = rgb(156, 214, 156);
12const BORDER: u32 = rgb(46, 139, 87);
13
14/// Paints a forward button and answers what its parent returns for the
15/// notification. A disabled one is left to the theme: grey is what "not
16/// yet" looks like.
17pub unsafe fn paint_forward_button(draw: &ctl::NMCUSTOMDRAW) -> isize {
18    let state = draw.uItemState;
19    if draw.dwDrawStage != ctl::CDDS_PREPAINT || state & ctl::CDIS_DISABLED != 0 {
20        return ctl::CDRF_DODEFAULT as isize;
21    }
22    let fill = if state & ctl::CDIS_SELECTED != 0 {
23        FILL_PRESSED
24    } else if state & ctl::CDIS_HOT != 0 {
25        FILL_HOT
26    } else {
27        FILL
28    };
29    let mut rect = draw.rc;
30    let brush = gdi::CreateSolidBrush(fill);
31    gdi::FillRect(draw.hdc, &rect, brush);
32    gdi::DeleteObject(brush);
33
34    // Focus thickens the border, as the theme's own focused button does.
35    let border = gdi::CreateSolidBrush(BORDER);
36    gdi::FrameRect(draw.hdc, &rect, border);
37    if state & ctl::CDIS_FOCUS != 0 {
38        gdi::InflateRect(&mut rect, -1, -1);
39        gdi::FrameRect(draw.hdc, &rect, border);
40    }
41    gdi::DeleteObject(border);
42
43    let cues = state & ctl::CDIS_SHOWKEYBOARDCUES != 0;
44    draw_label(draw.hdr.hwndFrom, draw.hdc, rect, cues);
45    if cues && state & ctl::CDIS_FOCUS != 0 {
46        gdi::InflateRect(&mut rect, -2, -2);
47        gdi::DrawFocusRect(draw.hdc, &rect);
48    }
49    ctl::CDRF_SKIPDEFAULT as isize
50}
51
52/// The button's own text in its own font, centred; the `&` mnemonic is
53/// underlined only while keyboard cues show, as the theme would.
54unsafe fn draw_label(
55    button: windows_sys::Win32::Foundation::HWND,
56    dc: gdi::HDC,
57    mut rect: windows_sys::Win32::Foundation::RECT,
58    cues: bool,
59) {
60    let mut text = [0u16; 64];
61    let length = wm::GetWindowTextW(button, text.as_mut_ptr(), text.len() as i32);
62    let font = wm::SendMessageW(button, wm::WM_GETFONT, 0, 0);
63    let previous = gdi::SelectObject(dc, font as gdi::HGDIOBJ);
64    gdi::SetBkMode(dc, gdi::TRANSPARENT as i32);
65    gdi::SetTextColor(dc, gdi::GetSysColor(gdi::COLOR_BTNTEXT));
66    let mut format = gdi::DT_CENTER | gdi::DT_VCENTER | gdi::DT_SINGLELINE;
67    if !cues {
68        format |= gdi::DT_HIDEPREFIX;
69    }
70    gdi::DrawTextW(dc, text.as_ptr(), length, &mut rect, format);
71    gdi::SelectObject(dc, previous);
72}
73
74const fn rgb(red: u8, green: u8, blue: u8) -> u32 {
75    red as u32 | (green as u32) << 8 | (blue as u32) << 16
76}