{"id":13632580,"url":"https://github.com/gabdube/native-windows-gui","last_synced_at":"2025-05-13T22:00:16.045Z","repository":{"id":37664844,"uuid":"68837427","full_name":"gabdube/native-windows-gui","owner":"gabdube","description":"A light windows GUI toolkit for rust","archived":false,"fork":false,"pushed_at":"2024-05-16T10:00:30.000Z","size":5488,"stargazers_count":2009,"open_issues_count":71,"forks_count":139,"subscribers_count":37,"default_branch":"master","last_synced_at":"2025-04-28T17:11:56.385Z","etag":null,"topics":["gui","native","native-windows-gui","rust"],"latest_commit_sha":null,"homepage":"https://gabdube.github.io/native-windows-gui/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gabdube.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"gabdube"}},"created_at":"2016-09-21T16:41:38.000Z","updated_at":"2025-04-25T09:05:34.000Z","dependencies_parsed_at":"2024-01-14T07:10:51.367Z","dependency_job_id":"751cf244-537b-4887-a7f4-0f4278e80bc1","html_url":"https://github.com/gabdube/native-windows-gui","commit_stats":{"total_commits":838,"total_committers":40,"mean_commits":20.95,"dds":"0.11813842482100234","last_synced_commit":"a6c96e8de5d01fe7bb566d737622dfead3cd1aed"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabdube%2Fnative-windows-gui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabdube%2Fnative-windows-gui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabdube%2Fnative-windows-gui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gabdube%2Fnative-windows-gui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gabdube","download_url":"https://codeload.github.com/gabdube/native-windows-gui/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254036806,"owners_count":22003651,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gui","native","native-windows-gui","rust"],"created_at":"2024-08-01T22:03:07.604Z","updated_at":"2025-05-13T22:00:15.650Z","avatar_url":"https://github.com/gabdube.png","language":"Rust","funding_links":["https://github.com/sponsors/gabdube"],"categories":["Rust"],"sub_categories":[],"readme":"# Native Windows GUI\n\nWelcome to Native Windows GUI (aka NWG). A rust library to develop native GUI applications on the desktop for Microsoft Windows.\n\nNWG is a very light wrapper over WINAPI. It allows you, the developer, to handle\nthe quirks and rough edges of the API by providing a simple, safe and rust-like interface.\n\nNative Windows GUI keeps things simple. This means small compile times, minimal resource usage,\nless time searching the documentation and more time for you to develop your application.\n\nOf course, you don't have to take my word for it, check out the [showcase](showcase) and the \n[examples](native-windows-gui/examples).\n\nThis is the 3rd and final version of NWG. It is considered \"mature\" or, as I would say\n\"the backlog is empty, and it will most likely stay that way\". This version implements pretty much\neverything required to develop applications on Windows. Don't bother using the older versions as they\nhave \"irreconcilable design decisions\" and cannot support some key features. Future development will be done\nin other libraries.\n\nIf you've managed to read through this introduction, you should know that my twitter handle\nis [#gdube_dev](https://twitter.com/gdube_dev) and you can support this project with [*GitHub Sponsors*](https://github.com/sponsors/gabdube).\n\nAny support is greatly appreciated.\n\n## Installation\n\nTo use NWG in your project add it to cargo.toml:\n\n```toml\n[dependencies]\nnative-windows-gui = \"1.0.12\"\nnative-windows-derive = \"1.0.3\" # Optional. Only if the derive macro is used.\n```\n\nAnd then, in main.rs or lib.rs :\n\n```rust\nextern crate native_windows_gui as nwg;\nextern crate native_windows_derive as nwd;  // Optional. Only if the derive macro is used.\n```\n\n### Rust 2018 aliasing\n\nYou can skip the `extern crate` define in your source code by adding the following code in `Cargo.toml`\nNote that procedural macros still require an `extern crate` definition, so this wont work with `native-windows-derive`\n\n```toml\n[dependencies]\nnwg = {version = \"^1.0.12\", package = \"native-windows-gui\"}\n```\n\n\n## Trying it out\n\nSee it for yourself. NWG has plenty of examples and a fully interactive test suite. The only thing you need to do is:\n\n```bash\ngit clone git@github.com:gabdube/native-windows-gui.git\n\ncd native-windows-gui/native-windows-gui # Running the tests from the workspace screws up the features\n\ncargo test everything --features \"all\"  # For the test suite\ncargo run --example basic\ncargo run --example calculator\ncargo run --example message_bank\ncargo run --example image_decoder_d --features \"extern-canvas\"\ncargo run --example partials --features \"listbox frame combobox\"\ncargo run --example system_tray --features \"tray-notification message-window menu cursor\"\ncargo run --example dialog_multithreading_d --features \"notice\"\ncargo run --example image_decoder_d --features \"image-decoder file-dialog\"\ncargo run --example month_name_d --features \"winnls textbox\"\ncargo run --example splash_screen_d --features \"image-decoder\"\ncargo run --example drop_files_d --features \"textbox\"\n\ncd examples/opengl_canvas\ncargo run\n\n# The closest thing to a real application in the examples\ncd ../examples/sync-draw\ncargo run\n\n# Requires the console to be run as Admin because of the embed resource\ncd ../examples/embed_resources\ncargo run\n```\n\n### Cross-compiling from Ubuntu\n\nRequirement: MinGW compiler\n\n    sudo apt install gcc-mingw-w64-x86-64\n\nRequirement: Rust support\n\n    rustup target add x86_64-pc-windows-gnu\n\nCompiling and running basic example:\n\n    cargo build --release --target=x86_64-pc-windows-gnu\n    cargo build --release --target=x86_64-pc-windows-gnu --example basic\n    wine target/x86_64-pc-windows-gnu/release/examples/basic.exe\n\n## Project structure\n\nThis is the main project git. It is separated in multiple sections\n\n- native-windows-gui\n  - The base library. Includes an interactive test suite and plenty of examples\n- native-windows-derive\n  - A procedural macro that generates the GUI application from rust structure (pretty cool stuff IMO)\n- docs/native-windows-docs [read it online](https://gabdube.github.io/native-windows-gui/native-windows-docs/index.html)\n  - Hefty documentation that goes over everything you need to know about NWG\n- [showcase](showcase)\n  - Images of the examples. If you've made an NWG application and want\n  to share it here, send me a message or open a PR. It's free real estate.\n\n## Supported features\n\n- The WHOLE winapi control library [(reference)](https://docs.microsoft.com/en-us/windows/win32/controls/individual-control-info)\n  - Some very niche controls are not supported: flat scroll bar, ip control, rebar, and pager.\n- Menus and menu bar\n- Image and font resource\n  - BMP\n  - ICO\n  - CUR\n  - PNG*\n  - GIF*\n  - JPG*\n  - TIFF*\n  - DDS*\n  - *: Extended image formats with the Windows Imaging Component (WIC).\n- Localization support\n  - Uses Windows National Language Support internally [(reference)](https://docs.microsoft.com/en-us/windows/win32/intl/national-language-support)\n- Tooltip\n- System tray notification\n- Cursor handling\n- A full clipboard wrapper\n- Partial templates support\n  - Split large application into chunks\n- Dynamic controls support\n  - Add/Remove controls at runtime\n  - Bind or unbind new events at runtime\n- Multithreaded application support\n  - Communicate to the GUI thread from another thread\n  - Run multiple windows on different threads\n- Simple layout configurations\n  - FlexboxLayout\n  - GridLayout\n- Drag and drop\n  - Drop files from the desktop to a window\n- The most common dialog boxes\n  - File dialog (save, open, open folder)\n  - Font dialog\n  - Color dialog\n- A canvas that can be used by external rendering APIs\n- High-DPI aware\n- Support for accessibility functions\n  - Tab navigation\n- Support for low level system message capture (HWND, MSG, WPARAM, LPARAM)\n- Cross compiling and testing from Linux to Windows with Wine and mingw.\n  - Not all features are supported (but the majority are, thanks WINE!)\n  - See `https://zork.net/~st/jottings/rust-windows-and-debian.html` for the steps to follow\n\n## Performance\n\nThis was measured on a `Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz, 3401 Mhz, 4 Core(s), 8 Logical Processor(s)`\n\nIn release mode, the `basic` example weighs **163kb** on disk and takes **900kb** in memory. Launch time is instantaneous.\n\nThe interactive test suite (with every feature and 100s of tests) weighs **931 kb** on disk and takes **8MB** in memory. Launch time is still instantaneous.\n\nInitial build time takes around **22 seconds** for a basic application. This is mainly due to `winapi-rs` initial compile time. Subsequent compile time takes around **0.7 seconds**.\n\n## Development\n\nThe development of this library is considered \"done\". By that, I mean that\nthere won't be any change to the API. Issues can be raised if a bug is found or\nif some area in the documentation is unclear. If I overlooked a very important feature,\nit will most likely be added.\n\n## License\n\nNWG uses the MIT license\n\n## Code example\n\n### With native windows derive\n\n```rust\n#![windows_subsystem = \"windows\"]\n/*!\n    A very simple application that shows your name in a message box.\n    Unlike `basic_d`, this example uses layout to position the controls in the window\n*/\n\n\nextern crate native_windows_gui as nwg;\nextern crate native_windows_derive as nwd;\n\nuse nwd::NwgUi;\nuse nwg::NativeUi;\n\n\n#[derive(Default, NwgUi)]\npub struct BasicApp {\n    #[nwg_control(size: (300, 115), position: (300, 300), title: \"Basic example\", flags: \"WINDOW|VISIBLE\")]\n    #[nwg_events( OnWindowClose: [BasicApp::say_goodbye] )]\n    window: nwg::Window,\n\n    #[nwg_layout(parent: window, spacing: 1)]\n    grid: nwg::GridLayout,\n\n    #[nwg_control(text: \"Heisenberg\", focus: true)]\n    #[nwg_layout_item(layout: grid, row: 0, col: 0)]\n    name_edit: nwg::TextInput,\n\n    #[nwg_control(text: \"Say my name\")]\n    #[nwg_layout_item(layout: grid, col: 0, row: 1, row_span: 2)]\n    #[nwg_events( OnButtonClick: [BasicApp::say_hello] )]\n    hello_button: nwg::Button\n}\n\nimpl BasicApp {\n\n    fn say_hello(\u0026self) {\n        nwg::modal_info_message(\u0026self.window, \"Hello\", \u0026format!(\"Hello {}\", self.name_edit.text()));\n    }\n    \n    fn say_goodbye(\u0026self) {\n        nwg::modal_info_message(\u0026self.window, \"Goodbye\", \u0026format!(\"Goodbye {}\", self.name_edit.text()));\n        nwg::stop_thread_dispatch();\n    }\n\n}\n\nfn main() {\n    nwg::init().expect(\"Failed to init Native Windows GUI\");\n    nwg::Font::set_global_family(\"Segoe UI\").expect(\"Failed to set default font\");\n    let _app = BasicApp::build_ui(Default::default()).expect(\"Failed to build UI\");\n    nwg::dispatch_thread_events();\n}\n```\n\n### Barebone example. Suitable if you only need a simple static UI\n\n```rust\n#![windows_subsystem = \"windows\"]\n/**\n    A very simple application that show your name in a message box.\n\n    This demo shows how to use NWG without the NativeUi trait boilerplate.\n    Note that this way of doing things is alot less extensible and cannot make use of native windows derive.\n\n    See `basic` for the NativeUi version and `basic_d` for the derive version\n*/\nextern crate native_windows_gui as nwg;\nuse std::rc::Rc;\n\nfn main() {\n    nwg::init().expect(\"Failed to init Native Windows GUI\");\n    nwg::Font::set_global_family(\"Segoe UI\").expect(\"Failed to set default font\");\n\n    let mut window = Default::default();\n    let mut name_edit = Default::default();\n    let mut hello_button = Default::default();\n    let layout = Default::default();\n\n    nwg::Window::builder()\n        .size((300, 115))\n        .position((300, 300))\n        .title(\"Basic example\")\n        .build(\u0026mut window)\n        .unwrap();\n\n    nwg::TextInput::builder()\n        .text(\"Heisenberg\")\n        .focus(true)\n        .parent(\u0026window)\n        .build(\u0026mut name_edit)\n        .unwrap();\n\n    nwg::Button::builder()\n        .text(\"Say my name\")\n        .parent(\u0026window)\n        .build(\u0026mut hello_button)\n        .unwrap();\n\n    nwg::GridLayout::builder()\n        .parent(\u0026window)\n        .spacing(1)\n        .child(0, 0, \u0026name_edit)\n        .child_item(nwg::GridLayoutItem::new(\u0026hello_button, 0, 1, 1, 2))\n        .build(\u0026layout)\n        .unwrap();\n\n    let window = Rc::new(window);\n    let events_window = window.clone();\n\n    let handler = nwg::full_bind_event_handler(\u0026window.handle, move |evt, _evt_data, handle| {\n        use nwg::Event as E;\n\n        match evt {\n            E::OnWindowClose =\u003e \n                if \u0026handle == \u0026events_window as \u0026nwg::Window {\n                    nwg::modal_info_message(\u0026events_window.handle, \"Goodbye\", \u0026format!(\"Goodbye {}\", name_edit.text()));\n                    nwg::stop_thread_dispatch();\n                },\n            E::OnButtonClick =\u003e \n                if \u0026handle == \u0026hello_button {\n                    nwg::modal_info_message(\u0026events_window.handle, \"Hello\", \u0026format!(\"Hello {}\", name_edit.text()));\n                },\n            _ =\u003e {}\n        }\n    });\n\n    nwg::dispatch_thread_events();\n    nwg::unbind_event_handler(\u0026handler);\n}\n```\n\n### With the NativeUi boilerplate\n\n```rust\n#![windows_subsystem = \"windows\"]\n/*!\n    A very simple application that shows your name in a message box.\n    Uses layouts to position the controls in the window\n*/\n\nextern crate native_windows_gui as nwg;\nuse nwg::NativeUi;\n\n\n#[derive(Default)]\npub struct BasicApp {\n    window: nwg::Window,\n    layout: nwg::GridLayout,\n    name_edit: nwg::TextInput,\n    hello_button: nwg::Button\n}\n\nimpl BasicApp {\n\n    fn say_hello(\u0026self) {\n        nwg::modal_info_message(\u0026self.window, \"Hello\", \u0026format!(\"Hello {}\", self.name_edit.text()));\n    }\n    \n    fn say_goodbye(\u0026self) {\n        nwg::modal_info_message(\u0026self.window, \"Goodbye\", \u0026format!(\"Goodbye {}\", self.name_edit.text()));\n        nwg::stop_thread_dispatch();\n    }\n\n}\n\n//\n// ALL of this stuff is handled by native-windows-derive\n//\nmod basic_app_ui {\n    use native_windows_gui as nwg;\n    use super::*;\n    use std::rc::Rc;\n    use std::cell::RefCell;\n    use std::ops::Deref;\n\n    pub struct BasicAppUi {\n        inner: Rc\u003cBasicApp\u003e,\n        default_handler: RefCell\u003cOption\u003cnwg::EventHandler\u003e\u003e\n    }\n\n    impl nwg::NativeUi\u003cBasicAppUi\u003e for BasicApp {\n        fn build_ui(mut data: BasicApp) -\u003e Result\u003cBasicAppUi, nwg::NwgError\u003e {\n            use nwg::Event as E;\n            \n            // Controls\n            nwg::Window::builder()\n                .flags(nwg::WindowFlags::WINDOW | nwg::WindowFlags::VISIBLE)\n                .size((300, 115))\n                .position((300, 300))\n                .title(\"Basic example\")\n                .build(\u0026mut data.window)?;\n\n            nwg::TextInput::builder()\n                .text(\"Heisenberg\")\n                .parent(\u0026data.window)\n                .focus(true)\n                .build(\u0026mut data.name_edit)?;\n\n            nwg::Button::builder()\n                .text(\"Say my name\")\n                .parent(\u0026data.window)\n                .build(\u0026mut data.hello_button)?;\n\n            // Wrap-up\n            let ui = BasicAppUi {\n                inner: Rc::new(data),\n                default_handler: Default::default(),\n            };\n\n            // Events\n            let evt_ui = Rc::downgrade(\u0026ui.inner);\n            let handle_events = move |evt, _evt_data, handle| {\n                if let Some(ui) = evt_ui.upgrade() {\n                    match evt {\n                        E::OnButtonClick =\u003e \n                            if \u0026handle == \u0026ui.hello_button {\n                                BasicApp::say_hello(\u0026ui);\n                            },\n                        E::OnWindowClose =\u003e \n                            if \u0026handle == \u0026ui.window {\n                                BasicApp::say_goodbye(\u0026ui);\n                            },\n                        _ =\u003e {}\n                    }\n                }\n            };\n\n           *ui.default_handler.borrow_mut() = Some(nwg::full_bind_event_handler(\u0026ui.window.handle, handle_events));\n\n           // Layouts\n           nwg::GridLayout::builder()\n            .parent(\u0026ui.window)\n            .spacing(1)\n            .child(0, 0, \u0026ui.name_edit)\n            .child_item(nwg::GridLayoutItem::new(\u0026ui.hello_button, 0, 1, 1, 2))\n            .build(\u0026ui.layout)?;\n\n            return Ok(ui);\n        }\n    }\n\n    impl Drop for BasicAppUi {\n        /// To make sure that everything is freed without issues, the default handler must be unbound.\n        fn drop(\u0026mut self) {\n            let handler = self.default_handler.borrow();\n            if handler.is_some() {\n                nwg::unbind_event_handler(handler.as_ref().unwrap());\n            }\n        }\n    }\n\n    impl Deref for BasicAppUi {\n        type Target = BasicApp;\n\n        fn deref(\u0026self) -\u003e \u0026BasicApp {\n            \u0026self.inner\n        }\n    }\n}\n\nfn main() {\n    nwg::init().expect(\"Failed to init Native Windows GUI\");\n    nwg::Font::set_global_family(\"Segoe UI\").expect(\"Failed to set default font\");\n    let _ui = BasicApp::build_ui(Default::default()).expect(\"Failed to build UI\");\n    nwg::dispatch_thread_events();\n}\n```\n\n## Attributions\n\nFor the icons used in the test suite (and only there):\n\n- **love.ico** is made by [Smashicons](https://smashicons.com/) from [www.flaticon.com](https://www.flaticon.com/)\n- **popcorn.bmp** is made by [Freepik](https://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/)\n- **ball.bmp** is made by [Freepik](https://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/)\n- **cat.jpg** is made by [Freepik](https://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/)\n- **weird_cat.png** is made by [Freepik](https://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/)\n- **list_0.png**, **list_1.png**, **list_2.png**, **list_3.png** are made by [Smashicons](https://smashicons.com/) from [www.flaticon.com](https://www.flaticon.com/)\n- **ice.cur** is made by nrox653 from [rw-designer](http://www.rw-designer.com/cursor-set/icepackpro)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabdube%2Fnative-windows-gui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgabdube%2Fnative-windows-gui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgabdube%2Fnative-windows-gui/lists"}