Skip to main content

pacenotes_installer\ui/
control_layout.rs

1const EM_SETSEL: u32 = 0x00B1;
2const EM_SCROLLCARET: u32 = 0x00B7;
3
4/// Scrolls a multiline edit to its last line, so a growing log never looks stalled.
5pub unsafe fn scroll_to_end(dialog: windows_sys::Win32::Foundation::HWND, control: u16) {
6    let edit = windows_sys::Win32::UI::WindowsAndMessaging::GetDlgItem(dialog, control as i32);
7    let end = windows_sys::Win32::UI::WindowsAndMessaging::SendMessageW(
8        edit,
9        windows_sys::Win32::UI::WindowsAndMessaging::WM_GETTEXTLENGTH,
10        0,
11        0,
12    );
13    // The caret has to sit at the end; `EM_SETSEL(-1, -1)` only clears the selection.
14    windows_sys::Win32::UI::WindowsAndMessaging::SendMessageW(
15        edit,
16        EM_SETSEL,
17        end as windows_sys::Win32::Foundation::WPARAM,
18        end,
19    );
20    windows_sys::Win32::UI::WindowsAndMessaging::SendMessageW(edit, EM_SCROLLCARET, 0, 0);
21}
22
23pub unsafe fn show(dialog: windows_sys::Win32::Foundation::HWND, control: u16, visible: bool) {
24    let mode = if visible {
25        windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOW
26    } else {
27        windows_sys::Win32::UI::WindowsAndMessaging::SW_HIDE
28    };
29    windows_sys::Win32::UI::WindowsAndMessaging::ShowWindow(
30        windows_sys::Win32::UI::WindowsAndMessaging::GetDlgItem(dialog, control as i32),
31        mode,
32    );
33}
34
35/// Grows `control` down to the bottom edge of `over`, which takes the room of a hidden control.
36pub unsafe fn stretch_to_bottom_of(
37    dialog: windows_sys::Win32::Foundation::HWND,
38    control: u16,
39    over: u16,
40) {
41    let (Some(grown), Some(covered)) = (
42        rect_in_dialog(dialog, control),
43        rect_in_dialog(dialog, over),
44    ) else {
45        return;
46    };
47    windows_sys::Win32::UI::WindowsAndMessaging::SetWindowPos(
48        windows_sys::Win32::UI::WindowsAndMessaging::GetDlgItem(dialog, control as i32),
49        core::ptr::null_mut(),
50        0,
51        0,
52        grown.right - grown.left,
53        covered.bottom - grown.top,
54        windows_sys::Win32::UI::WindowsAndMessaging::SWP_NOMOVE
55            | windows_sys::Win32::UI::WindowsAndMessaging::SWP_NOZORDER
56            | windows_sys::Win32::UI::WindowsAndMessaging::SWP_NOACTIVATE,
57    );
58}
59
60/// A control's rectangle in its dialog's client coordinates, which `SetWindowPos` takes.
61unsafe fn rect_in_dialog(
62    dialog: windows_sys::Win32::Foundation::HWND,
63    control: u16,
64) -> Option<windows_sys::Win32::Foundation::RECT> {
65    let mut rect: windows_sys::Win32::Foundation::RECT = core::mem::zeroed();
66    let window = windows_sys::Win32::UI::WindowsAndMessaging::GetDlgItem(dialog, control as i32);
67    if windows_sys::Win32::UI::WindowsAndMessaging::GetWindowRect(window, &mut rect) == 0 {
68        return None;
69    }
70    let corner_count = 2;
71    windows_sys::Win32::Graphics::Gdi::MapWindowPoints(
72        core::ptr::null_mut(),
73        dialog,
74        &mut rect as *mut _ as *mut windows_sys::Win32::Foundation::POINT,
75        corner_count,
76    );
77    Some(rect)
78}