Native Windows GUI: Helpers

Outside of the controls, the resources, and the layouts NWG also has some utility functions. This section will go over them.

Automatic styling

NWG use a hack to implement visual style in applications without having to use a manifest file (see this) This is a good thing because linking a manifest requires a third party library (such as rust-embed-resource)

If automatic styling is not needed, enable the feature no-styling

Message box

In a GUI application, there's no console to display error messages to the end user. In cases like this, a message box is used.
NWG wraps the winapi message box function with the message or the modal_message function.
The modal variant takes a parent window parameter. The parent window is disabled during the message box duration.
use native_windows_gui as nwg;

let p = nwg::MessageParams {
    title: "Hey",
    content: "Cats are cute",
    buttons: nwg::MessageButtons::Ok,
    icons: nwg::MessageIcons::Warning
};

assert!(nwg::message(&p) == nwg::MessageChoice::Ok)

This function might be a little to verbose for most use case, that's why NWG implements six simple interfaces (3 modal and 3 without):
pub fn fatal_message<'a>(title: &'a str, content: &'a str) {}
pub fn error_message<'a>(title: &'a str, content: &'a str) {}
pub fn simple_message<'a>(title: &'a str, content: &'a str) {}

Global Cursor

NWG wraps the system cursor with the GlobalCursor object. Not to be confused with a Cursor object, a cursor resource file (*.cur).

GlobalCursor is hidden behind the cursor feature. Once enabled, it can be used to get/set the mouse position on screen, capture the cursor outside of the application, detect a dragging gesture, and set the image of the cursor globally.

See https://docs.rs/native-windows-gui/1.0.12/native_windows_gui/struct.GlobalCursor.html

The GlobalCursor object cannot be instanced. It must be used as it is:
use native_windows_gui as nwg;
let (x,y) = nwg::GlobalCursor::position();

Monitor

Sometimes you need to know the dimensions of the desktop to position Windows control. The Monitor object handles this. With Monitor, it is possible to query the main screen dimensions, the virtual screen dimensions, and the monitor screen that a top level window is currently in

See https://docs.rs/native-windows-gui/1.0.12/native_windows_gui/struct.Monitor.html

The Monitor object cannot be instanced. It must be used as it is:
// Creating and centering a window in the main monitor

use native_windows_gui as nwg;

fn create_window(width: i32, height: i32) -> nwg::Window {
    let [total_width, total_height] = [nwg::Monitor::width(), nwg::Monitor::height()];
    let mut window = nwg::Window::default();

    let x = (total_width-width)/2;
    let y = (total_height-height)/2;

    nwg::Window::builder()
        .size((width, height))
        .position((x, y))
        .build(&mut window)
        .unwrap();

    window
}

Clipboard

NWG also wraps the system clipboard in order to write or read data from it. The clipboard is a way to copy arbritary data from within an application or even between two different applications.

See https://docs.rs/native-windows-gui/1.0.12/native_windows_gui/struct.Clipboard.html

Note that NWG clipboard intentionally keeps things simple and close to the metal. If you want to more robust API, then I recommend you look into https://github.com/DoumanAsh/clipboard-win

Clipboard is hidden behind the clipboard feature.

Writing/Reading text

Because reading or writing text will probably be the most used feature, NWG includes helpers functions that handles the unsafy bits. One thing to note is that Windows clipboard functions always take a control parameter. This can be any HWND based controls (ex: Window, Button, etc).

use native_windows_gui as nwg;

fn clipboard_text(window: &nwg::Window) {
    nwg::Clipboard::set_data_text(window, "Hello!");

    let text = nwg::Clipboard::data_text(window);
    assert!(text.is_some());
    assert!(&text.unwrap() == &"Hello!");
}

Handling custom data

It's possible to pass data structure around using the low level clipboard functions. Note that those function are unsafe because there is no way to validate the data.

When using the low level functions, the clipboard must be manually opened and then closed. Also, when setting data, it's up to the developer to empty whatever content was in the clipboard. Failure to do so will result in unknown errors.
use native_windows_gui as nwg;

#[repr(C)]
#[derive(Clone, Copy)]
struct Hello {
    foo: usize,
    bar: [u16; 3]
}

fn write_custom_data(window: &nwg::Window) {
    let data = Hello {
        foo: 6529,
        bar: [0, 100, 20]
    };

    nwg::Clipboard::open(window);
    nwg::Clipboard::empty();
    unsafe {
        nwg::Clipboard::set_data(
            nwg::ClipboardFormat::Global("Hello"),
            &data as *const Hello,
            1
        );
    }

    nwg::Clipboard::close();
}
    
fn read_custom_data(window: &nwg::Window) -> Option<Hello> {
    unsafe {
        nwg::Clipboard::open(window);
        let data = nwg::Clipboard::data(nwg::ClipboardFormat::Global("Hello"));
        nwg::Clipboard::close();
        data
    }
}

fn read_custom_data_handle(window: &nwg::Window) -> Option<Hello> {
    unsafe {
        nwg::Clipboard::open(window);
        let handle = nwg::Clipboard::data_handle(nwg::ClipboardFormat::Global("Hello"));
        let data = match handle {
            Some(h) => {
                let data_ptr: *const Hello = h.cast();
                let data = *data_ptr;
                h.release();
                Some(data)
            },
            None => None
        };

        nwg::Clipboard::close();
        data
    }
}