gabdube / native-windows-gui

A light windows GUI toolkit for rust
https://gabdube.github.io/native-windows-gui/
MIT License
1.97k stars 131 forks source link

Checkboxes in `ListView` #249

Open mbartlett21 opened 2 years ago

mbartlett21 commented 2 years ago

I would like an option to have checkboxes in the list view.

See https://docs.microsoft.com/en-us/windows/win32/controls/extended-list-view-styles

https://docs.rs/winapi/latest/winapi/um/commctrl/constant.LVS_EX_CHECKBOXES.html

mbartlett21 commented 2 years ago

Here's how you can get it working with windows-rs if anyone is interested:

When creating the ListView, include unsafe { nwg::ListViewExFlags::from_bits_unchecked(0x4) } as one of the flags. (This is LVS_EX_CHECKBOXES (see here)).

You can then use the following trait for adding set_checked and get_checked functions to the ListView:

use windows::Win32::{
    Foundation::{HWND, LPARAM, WPARAM},
    UI::{
        Controls::{LVIS_STATEIMAGEMASK, LVITEMA, LVM_GETITEMSTATE, LVM_SETITEMSTATE},
        WindowsAndMessaging::SendMessageW,
    },
};

trait ListViewCheckboxes {
    fn set_checked(&self, id: usize, checked: bool);
    fn get_checked(&self, id: usize) -> bool;
}

impl ListViewCheckboxes for nwg::ListView {
    fn set_checked(&self, id: usize, checked: bool) {
        let mut macro_lvi = LVITEMA {
            stateMask: LVIS_STATEIMAGEMASK,
            state: if checked { 2 << 12 } else { 1 << 12 },
            ..default()
        };

        unsafe {
            SendMessageW(
                HWND(self.handle.hwnd().unwrap() as isize),
                LVM_SETITEMSTATE,
                WPARAM(id),
                LPARAM(&mut macro_lvi as *mut _ as isize),
            );
        }
    }

    fn get_checked(&self, id: usize) -> bool {
        let res = (unsafe {
            SendMessageW(
                HWND(self.handle.hwnd().unwrap() as isize),
                LVM_GETITEMSTATE,
                WPARAM(id),
                LPARAM(LVIS_STATEIMAGEMASK as isize),
            )
        }
        .0 >> 12)
            - 1;

        res & 1 == 1
    }
}

You will also need to include windows in your dependencies:

[dependencies.windows]
version = "0.37"
features = [
    "Win32_Foundation",
    "Win32_UI_Controls",
    "Win32_UI_WindowsAndMessaging",
]