6个稳定版本
1.0.5 | 2022年6月21日 |
---|---|
1.0.4 | 2021年7月5日 |
1.0.3 | 2021年1月7日 |
1.0.2 | 2020年10月4日 |
1.0.0 | 2020年7月19日 |
737 在 进程宏 中排名
1,376 每月下载量
用于 5 crates
61KB
1K SLoC
Native Windows GUI
欢迎使用Native Windows GUI(简称NWG)。一个用于在Microsoft Windows桌面开发本地GUI应用程序的Rust库。
NWG是对WINAPI的一个非常轻量级的包装。它通过提供一个简单、安全和类似Rust的接口,让您作为开发者处理API的怪癖和粗糙边缘。
Native Windows GUI保持简单。这意味着编译时间短,资源使用量最小,查找文档的时间更少,您开发应用程序的时间更多。
这是NWG的第三个也是最后一个版本。它被认为是“成熟的”,或者说,正如我会说的,“待办事项列表为空,它很可能保持这种状态”。这个版本实现了开发Windows应用程序所需的几乎所有功能。请勿使用旧版本,因为它们有“不可调和的设计决策”,并且无法支持一些关键功能。未来的开发将在其他库中进行。
如果您已经阅读了这篇介绍,那么您应该知道我的twitter账号是#gdube_dev,您可以通过GitHub Sponsors来支持这个项目。
任何支持都将非常感激。
安装
要在项目中使用NWG,请将其添加到cargo.toml
[dependencies]
native-windows-gui = "1.0.12"
native-windows-derive = "1.0.3" # Optional. Only if the derive macro is used.
然后,在main.rs或lib.rs中
extern crate native_windows_gui as nwg;
extern crate native_windows_derive as nwd; // Optional. Only if the derive macro is used.
Rust 2018别名
您可以通过在 Cargo.toml
中添加以下代码来跳过源代码中的 extern crate
定义。注意,过程宏仍然需要 extern crate
定义,因此这不会与 native-windows-derive
一起工作。
[dependencies]
nwg = {version = "^1.0.12", package = "native-windows-gui"}
尝试一下
亲自看看。NWG 有许多示例和一个完全交互的测试套件。您需要做的只是
git clone [email protected]:gabdube/native-windows-gui.git
cd native-windows-gui/native-windows-gui # Running the tests from the workspace screws up the features
cargo test everything --features "all" # For the test suite
cargo run --example basic
cargo run --example calculator
cargo run --example message_bank
cargo run --example image_decoder_d --features "extern-canvas"
cargo run --example partials --features "listbox frame combobox"
cargo run --example system_tray --features "tray-notification message-window menu cursor"
cargo run --example dialog_multithreading_d --features "notice"
cargo run --example image_decoder_d --features "image-decoder file-dialog"
cargo run --example month_name_d --features "winnls textbox"
cargo run --example splash_screen_d --features "image-decoder"
cargo run --example drop_files_d --features "textbox"
cd examples/opengl_canvas
cargo run
# The closest thing to a real application in the examples
cd ../examples/sync-draw
cargo run
# Requires the console to be run as Admin because of the embed resource
cd ../examples/embed_resources
cargo run
从 Ubuntu 进行交叉编译
需求:MinGW 编译器
sudo apt install gcc-mingw-w64-x86-64
需求:Rust 支持
rustup target add x86_64-pc-windows-gnu
编译和运行基本示例
cargo build --release --target=x86_64-pc-windows-gnu
cargo build --release --target=x86_64-pc-windows-gnu --example basic
wine target/x86_64-pc-windows-gnu/release/examples/basic.exe
项目结构
这是主项目 git。它分为多个部分
- native-windows-gui
- 基本库。包括交互式测试套件和大量示例
- native-windows-derive
- 一个过程宏,它可以从 Rust 结构生成 GUI 应用程序(我认为这很酷)
- docs/native-windows-docs 在线阅读
- 这是一份详尽的文档,涵盖了您需要了解的关于 NWG 的所有内容
- 展示
- 示例图片。如果您已经制作了 NWG 应用程序并希望在此处分享,请给我发消息或打开一个 PR。这里的空间是免费的。
支持的功能
- 整个 winapi 控件库 (参考)
- 一些非常特殊的控件不受支持:平面滚动条、IP 控件、Rebar 和分页器。
- 菜单和菜单栏
- 图像和字体资源
- BMP
- ICO
- CUR
- PNG*
- GIF*
- JPG*
- TIFF*
- DDS*
- *:使用 Windows Imaging Component (WIC) 扩展的图像格式。
- 本地化支持
- 内部使用 Windows 国家语言支持 (参考)
- 工具提示
- 系统托盘通知
- 光标处理
- 完整的剪贴板包装器
- 部分模板支持
- 将大型应用程序分成块
- 动态控件支持
- 在运行时添加/删除控件
- 在运行时绑定或解绑新事件
- 多线程应用程序支持
- 从另一个线程与 GUI 线程通信
- 在不同的线程上运行多个窗口
- 简单的布局配置
- FlexboxLayout
- GridLayout
- 拖放
- 将文件从桌面拖放到窗口中
- 最常用的对话框
- 文件对话框(保存、打开、打开文件夹)
- 字体对话框
- 颜色对话框
- 可以由外部渲染 API 使用的画布
- 高 DPI 兼容
- 对无障碍功能的支持
- 标签导航
- 对低级别系统消息捕获(HWND、MSG、WPARAM、LPARAM)的支持
- 使用 Wine 和 mingw 从 Linux 到 Windows 的交叉编译和测试。
- 并非所有功能都受支持(但大多数都受支持,感谢 WINE!)
- 有关步骤,请参阅
https://zork.net/~st/jottings/rust-windows-and-debian.html
性能
这是在以下 CPU 上测量的:Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz, 3401 Mhz, 4 Core(s), 8 Logical Processor(s)
在发布模式下,basic
示例在磁盘上占用 163kb,在内存中占用 900kb。启动时间是瞬间的。
该交互式测试套件(包含所有功能和数百个测试)在磁盘上大小为 931 kb,在内存中占用 8MB。启动时间仍然是瞬间的。
基本应用的初始构建时间大约需要 22秒。这主要是因为 winapi-rs
的初始编译时间。后续编译时间大约为 0.7秒。
开发
这个库的开发被认为是“完成”的。这意味着API不会发生任何变化。如果发现bug或者文档中的某些区域不清晰,可以提出问题。如果遗漏了一个非常重要的功能,很可能会被添加。
许可证
NWG 使用 MIT 许可证
代码示例
使用原生 Windows 继承
#![windows_subsystem = "windows"]
/*!
A very simple application that shows your name in a message box.
Unlike `basic_d`, this example uses layout to position the controls in the window
*/
extern crate native_windows_gui as nwg;
extern crate native_windows_derive as nwd;
use nwd::NwgUi;
use nwg::NativeUi;
#[derive(Default, NwgUi)]
pub struct BasicApp {
#[nwg_control(size: (300, 115), position: (300, 300), title: "Basic example", flags: "WINDOW|VISIBLE")]
#[nwg_events( OnWindowClose: [BasicApp::say_goodbye] )]
window: nwg::Window,
#[nwg_layout(parent: window, spacing: 1)]
grid: nwg::GridLayout,
#[nwg_control(text: "Heisenberg", focus: true)]
#[nwg_layout_item(layout: grid, row: 0, col: 0)]
name_edit: nwg::TextInput,
#[nwg_control(text: "Say my name")]
#[nwg_layout_item(layout: grid, col: 0, row: 1, row_span: 2)]
#[nwg_events( OnButtonClick: [BasicApp::say_hello] )]
hello_button: nwg::Button
}
impl BasicApp {
fn say_hello(&self) {
nwg::modal_info_message(&self.window, "Hello", &format!("Hello {}", self.name_edit.text()));
}
fn say_goodbye(&self) {
nwg::modal_info_message(&self.window, "Goodbye", &format!("Goodbye {}", self.name_edit.text()));
nwg::stop_thread_dispatch();
}
}
fn main() {
nwg::init().expect("Failed to init Native Windows GUI");
nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");
let _app = BasicApp::build_ui(Default::default()).expect("Failed to build UI");
nwg::dispatch_thread_events();
}
基本示例。如果只需要简单的静态 UI,则适用
#![windows_subsystem = "windows"]
/**
A very simple application that show your name in a message box.
This demo shows how to use NWG without the NativeUi trait boilerplate.
Note that this way of doing things is alot less extensible and cannot make use of native windows derive.
See `basic` for the NativeUi version and `basic_d` for the derive version
*/
extern crate native_windows_gui as nwg;
use std::rc::Rc;
fn main() {
nwg::init().expect("Failed to init Native Windows GUI");
nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");
let mut window = Default::default();
let mut name_edit = Default::default();
let mut hello_button = Default::default();
let layout = Default::default();
nwg::Window::builder()
.size((300, 115))
.position((300, 300))
.title("Basic example")
.build(&mut window)
.unwrap();
nwg::TextInput::builder()
.text("Heisenberg")
.focus(true)
.parent(&window)
.build(&mut name_edit)
.unwrap();
nwg::Button::builder()
.text("Say my name")
.parent(&window)
.build(&mut hello_button)
.unwrap();
nwg::GridLayout::builder()
.parent(&window)
.spacing(1)
.child(0, 0, &name_edit)
.child_item(nwg::GridLayoutItem::new(&hello_button, 0, 1, 1, 2))
.build(&layout)
.unwrap();
let window = Rc::new(window);
let events_window = window.clone();
let handler = nwg::full_bind_event_handler(&window.handle, move |evt, _evt_data, handle| {
use nwg::Event as E;
match evt {
E::OnWindowClose =>
if &handle == &events_window as &nwg::Window {
nwg::modal_info_message(&events_window.handle, "Goodbye", &format!("Goodbye {}", name_edit.text()));
nwg::stop_thread_dispatch();
},
E::OnButtonClick =>
if &handle == &hello_button {
nwg::modal_info_message(&events_window.handle, "Hello", &format!("Hello {}", name_edit.text()));
},
_ => {}
}
});
nwg::dispatch_thread_events();
nwg::unbind_event_handler(&handler);
}
使用 NativeUi 模板
#![windows_subsystem = "windows"]
/*!
A very simple application that shows your name in a message box.
Uses layouts to position the controls in the window
*/
extern crate native_windows_gui as nwg;
use nwg::NativeUi;
#[derive(Default)]
pub struct BasicApp {
window: nwg::Window,
layout: nwg::GridLayout,
name_edit: nwg::TextInput,
hello_button: nwg::Button
}
impl BasicApp {
fn say_hello(&self) {
nwg::modal_info_message(&self.window, "Hello", &format!("Hello {}", self.name_edit.text()));
}
fn say_goodbye(&self) {
nwg::modal_info_message(&self.window, "Goodbye", &format!("Goodbye {}", self.name_edit.text()));
nwg::stop_thread_dispatch();
}
}
//
// ALL of this stuff is handled by native-windows-derive
//
mod basic_app_ui {
use native_windows_gui as nwg;
use super::*;
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
pub struct BasicAppUi {
inner: Rc<BasicApp>,
default_handler: RefCell<Option<nwg::EventHandler>>
}
impl nwg::NativeUi<BasicAppUi> for BasicApp {
fn build_ui(mut data: BasicApp) -> Result<BasicAppUi, nwg::NwgError> {
use nwg::Event as E;
// Controls
nwg::Window::builder()
.flags(nwg::WindowFlags::WINDOW | nwg::WindowFlags::VISIBLE)
.size((300, 115))
.position((300, 300))
.title("Basic example")
.build(&mut data.window)?;
nwg::TextInput::builder()
.text("Heisenberg")
.parent(&data.window)
.focus(true)
.build(&mut data.name_edit)?;
nwg::Button::builder()
.text("Say my name")
.parent(&data.window)
.build(&mut data.hello_button)?;
// Wrap-up
let ui = BasicAppUi {
inner: Rc::new(data),
default_handler: Default::default(),
};
// Events
let evt_ui = Rc::downgrade(&ui.inner);
let handle_events = move |evt, _evt_data, handle| {
if let Some(ui) = evt_ui.upgrade() {
match evt {
E::OnButtonClick =>
if &handle == &ui.hello_button {
BasicApp::say_hello(&ui);
},
E::OnWindowClose =>
if &handle == &ui.window {
BasicApp::say_goodbye(&ui);
},
_ => {}
}
}
};
*ui.default_handler.borrow_mut() = Some(nwg::full_bind_event_handler(&ui.window.handle, handle_events));
// Layouts
nwg::GridLayout::builder()
.parent(&ui.window)
.spacing(1)
.child(0, 0, &ui.name_edit)
.child_item(nwg::GridLayoutItem::new(&ui.hello_button, 0, 1, 1, 2))
.build(&ui.layout)?;
return Ok(ui);
}
}
impl Drop for BasicAppUi {
/// To make sure that everything is freed without issues, the default handler must be unbound.
fn drop(&mut self) {
let handler = self.default_handler.borrow();
if handler.is_some() {
nwg::unbind_event_handler(handler.as_ref().unwrap());
}
}
}
impl Deref for BasicAppUi {
type Target = BasicApp;
fn deref(&self) -> &BasicApp {
&self.inner
}
}
}
fn main() {
nwg::init().expect("Failed to init Native Windows GUI");
nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");
let _ui = BasicApp::build_ui(Default::default()).expect("Failed to build UI");
nwg::dispatch_thread_events();
}
归属
用于测试套件(仅限那里)中的图标
- love.ico 由 Smashicons 制作,来源 www.flaticon.com
- popcorn.bmp 由 Freepik 制作,来源 www.flaticon.com
- ball.bmp 由 Freepik 制作,来源 www.flaticon.com
- cat.jpg 由 Freepik 制作,来源 www.flaticon.com
- weird_cat.png 由 Freepik 制作,来源 www.flaticon.com
- list_0.png、list_1.png、list_2.png、list_3.png 由 Smashicons 制作,来源 www.flaticon.com
- ice.cur 由 nrox653 制作,来源 rw-designer
依赖项
约 ~2MB
约 ~44K SLoC