mirror of
https://github.com/italicsjenga/muda.git
synced 2025-01-11 04:11:32 +11:00
feat: add common-controls-v6
(#69)
This commit is contained in:
parent
7af4477896
commit
ac14222934
5
.changes/common-controls-v6.md
Normal file
5
.changes/common-controls-v6.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"muda": "minor"
|
||||||
|
---
|
||||||
|
|
||||||
|
Add `common-controls-v6` feature flag, disabled by default, which could be used to enable usage of `TaskDialogIndirect` API from `ComCtl32.dll` v6 on Windows for The predefined `About` menu item.
|
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
|
@ -14,15 +14,15 @@ env:
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: ['windows-latest', 'macos-latest', 'ubuntu-latest']
|
os: ["windows-latest", "macos-latest", "ubuntu-latest"]
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
@ -44,4 +44,9 @@ jobs:
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
|
||||||
|
- name: test common-controls-v6
|
||||||
|
if: matrix.os == "windows-latest"
|
||||||
|
working-directory: examples/windows-common-controls-v6
|
||||||
|
run: cargo run
|
||||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -3,16 +3,17 @@ name = "muda"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
description = "Menu Utilities for Desktop Applications"
|
description = "Menu Utilities for Desktop Applications"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
keywords = [ "windowing", "menu" ]
|
keywords = ["windowing", "menu"]
|
||||||
license = "Apache-2.0 OR MIT"
|
license = "Apache-2.0 OR MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/amrbashir/muda"
|
repository = "https://github.com/amrbashir/muda"
|
||||||
documentation = "https://docs.rs/muda"
|
documentation = "https://docs.rs/muda"
|
||||||
categories = [ "gui" ]
|
categories = ["gui"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "libxdo" ]
|
default = ["libxdo"]
|
||||||
libxdo = [ "dep:libxdo" ]
|
libxdo = ["dep:libxdo"]
|
||||||
|
common-controls-v6 = ["windows-sys/Win32_UI_Controls"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
|
@ -29,7 +30,7 @@ features = [
|
||||||
"Win32_UI_Shell",
|
"Win32_UI_Shell",
|
||||||
"Win32_Globalization",
|
"Win32_Globalization",
|
||||||
"Win32_UI_Input_KeyboardAndMouse",
|
"Win32_UI_Input_KeyboardAndMouse",
|
||||||
"Win32_System_SystemServices"
|
"Win32_System_SystemServices",
|
||||||
]
|
]
|
||||||
|
|
||||||
[target."cfg(target_os = \"linux\")".dependencies]
|
[target."cfg(target_os = \"linux\")".dependencies]
|
||||||
|
|
|
@ -14,7 +14,8 @@ muda is a Menu Utilities library for Desktop Applications.
|
||||||
|
|
||||||
### Cargo Features
|
### Cargo Features
|
||||||
|
|
||||||
- `libxdo` (enabled by default): Enables linking to `libxdo` which is used for the predfined `Copy`, `Cut`, `Paste` and `SelectAll` menu item.
|
- `common-controls-v6`: Use `TaskDialogIndirect` API from `ComCtl32.dll` v6 on Windows for showing the predefined `About` menu item dialog.
|
||||||
|
- `libxdo`: Enables linking to `libxdo` on Linux which is used for the predfined `Copy`, `Cut`, `Paste` and `SelectAll` menu item.
|
||||||
|
|
||||||
## Dependencies (Linux Only)
|
## Dependencies (Linux Only)
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,7 @@ fn main() {
|
||||||
None,
|
None,
|
||||||
Some(AboutMetadata {
|
Some(AboutMetadata {
|
||||||
name: Some("tao".to_string()),
|
name: Some("tao".to_string()),
|
||||||
|
version: Some("1.2.3".to_string()),
|
||||||
copyright: Some("Copyright tao".to_string()),
|
copyright: Some("Copyright tao".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
|
2
examples/windows-common-controls-v6/.gitignore
vendored
Normal file
2
examples/windows-common-controls-v6/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
18
examples/windows-common-controls-v6/Cargo.toml
Normal file
18
examples/windows-common-controls-v6/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "windows-common-controls-v6"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
muda = { path = "../../", features = ["common-controls-v6"] }
|
||||||
|
winit = "0.28"
|
||||||
|
image = "0.24"
|
||||||
|
|
||||||
|
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
|
||||||
|
version = "0.48"
|
||||||
|
features = ["Win32_UI_WindowsAndMessaging", "Win32_Foundation"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
embed-resource = "1.6"
|
15
examples/windows-common-controls-v6/app.exe.manifest
Normal file
15
examples/windows-common-controls-v6/app.exe.manifest
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</assembly>
|
4
examples/windows-common-controls-v6/build.rs
Normal file
4
examples/windows-common-controls-v6/build.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
embed_resource::compile("manifest.rc");
|
||||||
|
}
|
2
examples/windows-common-controls-v6/manifest.rc
Normal file
2
examples/windows-common-controls-v6/manifest.rc
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#define RT_MANIFEST 24
|
||||||
|
1 RT_MANIFEST "app.exe.manifest"
|
207
examples/windows-common-controls-v6/src/main.rs
Normal file
207
examples/windows-common-controls-v6/src/main.rs
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
// Copyright 2022-2022 Tauri Programme within The Commons Conservancy
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#![allow(unused)]
|
||||||
|
use muda::{
|
||||||
|
accelerator::{Accelerator, Code, Modifiers},
|
||||||
|
AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem,
|
||||||
|
PredefinedMenuItem, Submenu,
|
||||||
|
};
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use winit::platform::macos::{EventLoopBuilderExtMacOS, WindowExtMacOS};
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use winit::platform::windows::{EventLoopBuilderExtWindows, WindowExtWindows};
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, MouseButton, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoopBuilder},
|
||||||
|
window::{Window, WindowBuilder},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut event_loop_builder = EventLoopBuilder::new();
|
||||||
|
|
||||||
|
let menu_bar = Menu::new();
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
let menu_bar_c = menu_bar.clone();
|
||||||
|
event_loop_builder.with_msg_hook(move |msg| {
|
||||||
|
use windows_sys::Win32::UI::WindowsAndMessaging::{TranslateAcceleratorW, MSG};
|
||||||
|
unsafe {
|
||||||
|
let msg = msg as *const MSG;
|
||||||
|
let translated = TranslateAcceleratorW((*msg).hwnd, menu_bar_c.haccel(), msg);
|
||||||
|
translated == 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
event_loop_builder.with_default_menu(false);
|
||||||
|
|
||||||
|
let event_loop = event_loop_builder.build();
|
||||||
|
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_title("Window 1")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
let window2 = WindowBuilder::new()
|
||||||
|
.with_title("Window 2")
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
let app_m = Submenu::new("App", true);
|
||||||
|
menu_bar.append(&app_m);
|
||||||
|
app_m.append_items(&[
|
||||||
|
&PredefinedMenuItem::about(None, None),
|
||||||
|
&PredefinedMenuItem::separator(),
|
||||||
|
&PredefinedMenuItem::services(None),
|
||||||
|
&PredefinedMenuItem::separator(),
|
||||||
|
&PredefinedMenuItem::hide(None),
|
||||||
|
&PredefinedMenuItem::hide_others(None),
|
||||||
|
&PredefinedMenuItem::show_all(None),
|
||||||
|
&PredefinedMenuItem::separator(),
|
||||||
|
&PredefinedMenuItem::quit(None),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_m = Submenu::new("&File", true);
|
||||||
|
let edit_m = Submenu::new("&Edit", true);
|
||||||
|
let window_m = Submenu::new("&Window", true);
|
||||||
|
|
||||||
|
menu_bar.append_items(&[&file_m, &edit_m, &window_m]);
|
||||||
|
|
||||||
|
let custom_i_1 = MenuItem::new(
|
||||||
|
"C&ustom 1",
|
||||||
|
true,
|
||||||
|
Some(Accelerator::new(Some(Modifiers::ALT), Code::KeyC)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let path = concat!(env!("CARGO_MANIFEST_DIR"), "../../icon.png");
|
||||||
|
let icon = load_icon(std::path::Path::new(path));
|
||||||
|
let image_item = IconMenuItem::new("Image Custom 1", true, Some(icon), None);
|
||||||
|
|
||||||
|
let check_custom_i_1 = CheckMenuItem::new("Check Custom 1", true, true, None);
|
||||||
|
let check_custom_i_2 = CheckMenuItem::new("Check Custom 2", false, true, None);
|
||||||
|
let check_custom_i_3 = CheckMenuItem::new(
|
||||||
|
"Check Custom 3",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
Some(Accelerator::new(Some(Modifiers::SHIFT), Code::KeyD)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let copy_i = PredefinedMenuItem::copy(None);
|
||||||
|
let cut_i = PredefinedMenuItem::cut(None);
|
||||||
|
let paste_i = PredefinedMenuItem::paste(None);
|
||||||
|
|
||||||
|
file_m.append_items(&[
|
||||||
|
&custom_i_1,
|
||||||
|
&image_item,
|
||||||
|
&window_m,
|
||||||
|
&PredefinedMenuItem::separator(),
|
||||||
|
&check_custom_i_1,
|
||||||
|
&check_custom_i_2,
|
||||||
|
]);
|
||||||
|
|
||||||
|
window_m.append_items(&[
|
||||||
|
&PredefinedMenuItem::minimize(None),
|
||||||
|
&PredefinedMenuItem::maximize(None),
|
||||||
|
&PredefinedMenuItem::close_window(Some("Close")),
|
||||||
|
&PredefinedMenuItem::fullscreen(None),
|
||||||
|
&PredefinedMenuItem::about(
|
||||||
|
None,
|
||||||
|
Some(AboutMetadata {
|
||||||
|
name: Some("winit".to_string()),
|
||||||
|
version: Some("1.2.3".to_string()),
|
||||||
|
copyright: Some("Copyright winit".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
&check_custom_i_3,
|
||||||
|
&image_item,
|
||||||
|
&custom_i_1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
edit_m.append_items(&[©_i, &PredefinedMenuItem::separator(), &paste_i]);
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
menu_bar.init_for_hwnd(window.hwnd() as _);
|
||||||
|
menu_bar.init_for_hwnd(window2.hwnd() as _);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
menu_bar.init_for_nsapp();
|
||||||
|
window_m.set_windows_menu_for_nsapp();
|
||||||
|
}
|
||||||
|
|
||||||
|
let menu_channel = MenuEvent::receiver();
|
||||||
|
|
||||||
|
let mut x = 0_f64;
|
||||||
|
let mut y = 0_f64;
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => *control_flow = ControlFlow::Exit,
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CursorMoved { position, .. },
|
||||||
|
window_id,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if window_id == window2.id() {
|
||||||
|
x = position.x;
|
||||||
|
y = position.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::WindowEvent {
|
||||||
|
event:
|
||||||
|
WindowEvent::MouseInput {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
button: MouseButton::Right,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
window_id,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if window_id == window2.id() {
|
||||||
|
show_context_menu(&window2, &file_m, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::MainEventsCleared => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(event) = menu_channel.try_recv() {
|
||||||
|
if event.id == custom_i_1.id() {
|
||||||
|
file_m.insert(&MenuItem::new("New Menu Item", true, None), 2);
|
||||||
|
}
|
||||||
|
println!("{event:?}");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_context_menu(window: &Window, menu: &dyn ContextMenu, x: f64, y: f64) {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
menu.show_context_menu_for_hwnd(window.hwnd() as _, x, y);
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
menu.show_context_menu_for_nsview(window.ns_view() as _, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_icon(path: &std::path::Path) -> muda::icon::Icon {
|
||||||
|
let (icon_rgba, icon_width, icon_height) = {
|
||||||
|
let image = image::open(path)
|
||||||
|
.expect("Failed to open icon path")
|
||||||
|
.into_rgba8();
|
||||||
|
let (width, height) = image.dimensions();
|
||||||
|
let rgba = image.into_raw();
|
||||||
|
(rgba, width, height)
|
||||||
|
};
|
||||||
|
muda::icon::Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
|
||||||
|
}
|
|
@ -113,6 +113,7 @@ fn main() {
|
||||||
None,
|
None,
|
||||||
Some(AboutMetadata {
|
Some(AboutMetadata {
|
||||||
name: Some("winit".to_string()),
|
name: Some("winit".to_string()),
|
||||||
|
version: Some("1.2.3".to_string()),
|
||||||
copyright: Some("Copyright winit".to_string()),
|
copyright: Some("Copyright winit".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
predefined::PredfinedMenuItemType,
|
predefined::PredfinedMenuItemType,
|
||||||
util::{AddOp, Counter},
|
util::{AddOp, Counter},
|
||||||
MenuEvent, MenuItemType,
|
AboutMetadata, MenuEvent, MenuItemType,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
cell::{RefCell, RefMut},
|
cell::{RefCell, RefMut},
|
||||||
|
@ -31,12 +31,11 @@ use windows_sys::Win32::{
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu,
|
AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu,
|
||||||
DestroyAcceleratorTable, DrawMenuBar, EnableMenuItem, GetMenuItemInfoW, InsertMenuW,
|
DestroyAcceleratorTable, DrawMenuBar, EnableMenuItem, GetMenuItemInfoW, InsertMenuW,
|
||||||
MessageBoxW, PostQuitMessage, RemoveMenu, SendMessageW, SetMenu, SetMenuItemInfoW,
|
PostQuitMessage, RemoveMenu, SendMessageW, SetMenu, SetMenuItemInfoW, ShowWindow,
|
||||||
ShowWindow, TrackPopupMenu, HACCEL, HMENU, MB_ICONINFORMATION, MENUITEMINFOW,
|
TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND,
|
||||||
MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED,
|
MF_BYPOSITION, MF_CHECKED, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR,
|
||||||
MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, MF_UNCHECKED, MIIM_BITMAP,
|
MF_STRING, MF_UNCHECKED, MIIM_BITMAP, MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE,
|
||||||
MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, WM_CLOSE,
|
SW_MINIMIZE, TPM_LEFTALIGN, WM_CLOSE, WM_COMMAND, WM_DESTROY,
|
||||||
WM_COMMAND, WM_DESTROY,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1160,53 +1159,7 @@ unsafe extern "system" fn menu_subclass_proc(
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
}
|
}
|
||||||
PredfinedMenuItemType::About(Some(metadata)) => {
|
PredfinedMenuItemType::About(Some(metadata)) => {
|
||||||
use std::fmt::Write;
|
show_about_dialog(hwnd, metadata)
|
||||||
|
|
||||||
let mut message = String::new();
|
|
||||||
if let Some(name) = &metadata.name {
|
|
||||||
let _ = writeln!(&mut message, "Name: {}", name);
|
|
||||||
}
|
|
||||||
if let Some(version) = &metadata.version {
|
|
||||||
let _ = writeln!(&mut message, "Version: {}", version);
|
|
||||||
}
|
|
||||||
if let Some(authors) = &metadata.authors {
|
|
||||||
let _ = writeln!(&mut message, "Authors: {}", authors.join(", "));
|
|
||||||
}
|
|
||||||
if let Some(license) = &metadata.license {
|
|
||||||
let _ = writeln!(&mut message, "License: {}", license);
|
|
||||||
}
|
|
||||||
match (&metadata.website_label, &metadata.website) {
|
|
||||||
(Some(label), None) => {
|
|
||||||
let _ = writeln!(&mut message, "Website: {}", label);
|
|
||||||
}
|
|
||||||
(None, Some(url)) => {
|
|
||||||
let _ = writeln!(&mut message, "Website: {}", url);
|
|
||||||
}
|
|
||||||
(Some(label), Some(url)) => {
|
|
||||||
let _ = writeln!(&mut message, "Website: {} {}", label, url);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
if let Some(comments) = &metadata.comments {
|
|
||||||
let _ = writeln!(&mut message, "\n{}", comments);
|
|
||||||
}
|
|
||||||
if let Some(copyright) = &metadata.copyright {
|
|
||||||
let _ = writeln!(&mut message, "\n{}", copyright);
|
|
||||||
}
|
|
||||||
|
|
||||||
let message = encode_wide(message);
|
|
||||||
let title = encode_wide(format!(
|
|
||||||
"About {}",
|
|
||||||
metadata.name.as_deref().unwrap_or_default()
|
|
||||||
));
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
MessageBoxW(
|
|
||||||
hwnd,
|
|
||||||
message.as_ptr(),
|
|
||||||
title.as_ptr(),
|
|
||||||
MB_ICONINFORMATION,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -1322,3 +1275,103 @@ fn create_icon_item_info(hbitmap: HBITMAP) -> MENUITEMINFOW {
|
||||||
info.hbmpItem = hbitmap;
|
info.hbmpItem = hbitmap;
|
||||||
info
|
info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_about_dialog(hwnd: HWND, metadata: &AboutMetadata) {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
let mut message = String::new();
|
||||||
|
if let Some(name) = &metadata.name {
|
||||||
|
let _ = writeln!(&mut message, "Name: {}", name);
|
||||||
|
}
|
||||||
|
if let Some(version) = &metadata.version {
|
||||||
|
let _ = writeln!(&mut message, "Version: {}", version);
|
||||||
|
}
|
||||||
|
if let Some(authors) = &metadata.authors {
|
||||||
|
let _ = writeln!(&mut message, "Authors: {}", authors.join(", "));
|
||||||
|
}
|
||||||
|
if let Some(license) = &metadata.license {
|
||||||
|
let _ = writeln!(&mut message, "License: {}", license);
|
||||||
|
}
|
||||||
|
match (&metadata.website_label, &metadata.website) {
|
||||||
|
(Some(label), None) => {
|
||||||
|
let _ = writeln!(&mut message, "Website: {}", label);
|
||||||
|
}
|
||||||
|
(None, Some(url)) => {
|
||||||
|
let _ = writeln!(&mut message, "Website: {}", url);
|
||||||
|
}
|
||||||
|
(Some(label), Some(url)) => {
|
||||||
|
let _ = writeln!(&mut message, "Website: {} {}", label, url);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
if let Some(comments) = &metadata.comments {
|
||||||
|
let _ = writeln!(&mut message, "\n{}", comments);
|
||||||
|
}
|
||||||
|
if let Some(copyright) = &metadata.copyright {
|
||||||
|
let _ = writeln!(&mut message, "\n{}", copyright);
|
||||||
|
}
|
||||||
|
|
||||||
|
let message = encode_wide(message);
|
||||||
|
let title = encode_wide(format!(
|
||||||
|
"About {}",
|
||||||
|
metadata.name.as_deref().unwrap_or_default()
|
||||||
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "common-controls-v6"))]
|
||||||
|
std::thread::spawn(move || unsafe {
|
||||||
|
use windows_sys::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_ICONINFORMATION};
|
||||||
|
MessageBoxW(hwnd, message.as_ptr(), title.as_ptr(), MB_ICONINFORMATION);
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "common-controls-v6")]
|
||||||
|
{
|
||||||
|
use windows_sys::Win32::UI::Controls::{
|
||||||
|
TaskDialogIndirect, TASKDIALOGCONFIG, TASKDIALOGCONFIG_0, TASKDIALOGCONFIG_1,
|
||||||
|
TDCBF_OK_BUTTON, TDF_ALLOW_DIALOG_CANCELLATION, TD_INFORMATION_ICON,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::thread::spawn(move || unsafe {
|
||||||
|
let task_dialog_config = TASKDIALOGCONFIG {
|
||||||
|
cbSize: core::mem::size_of::<TASKDIALOGCONFIG>() as u32,
|
||||||
|
hwndParent: hwnd,
|
||||||
|
dwFlags: TDF_ALLOW_DIALOG_CANCELLATION,
|
||||||
|
pszWindowTitle: title.as_ptr(),
|
||||||
|
pszContent: message.as_ptr(),
|
||||||
|
Anonymous1: TASKDIALOGCONFIG_0 {
|
||||||
|
pszMainIcon: TD_INFORMATION_ICON,
|
||||||
|
},
|
||||||
|
Anonymous2: TASKDIALOGCONFIG_1 {
|
||||||
|
pszFooterIcon: std::ptr::null(),
|
||||||
|
},
|
||||||
|
dwCommonButtons: TDCBF_OK_BUTTON,
|
||||||
|
pButtons: std::ptr::null(),
|
||||||
|
cButtons: 0,
|
||||||
|
pRadioButtons: std::ptr::null(),
|
||||||
|
cRadioButtons: 0,
|
||||||
|
cxWidth: 0,
|
||||||
|
hInstance: 0,
|
||||||
|
pfCallback: None,
|
||||||
|
lpCallbackData: 0,
|
||||||
|
nDefaultButton: 0,
|
||||||
|
nDefaultRadioButton: 0,
|
||||||
|
pszCollapsedControlText: std::ptr::null(),
|
||||||
|
pszExpandedControlText: std::ptr::null(),
|
||||||
|
pszExpandedInformation: std::ptr::null(),
|
||||||
|
pszMainInstruction: std::ptr::null(),
|
||||||
|
pszVerificationText: std::ptr::null(),
|
||||||
|
pszFooter: std::ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut pf_verification_flag_checked = 0;
|
||||||
|
let mut pn_button = 0;
|
||||||
|
let mut pn_radio_button = 0;
|
||||||
|
|
||||||
|
TaskDialogIndirect(
|
||||||
|
&task_dialog_config,
|
||||||
|
&mut pn_button,
|
||||||
|
&mut pn_radio_button,
|
||||||
|
&mut pf_verification_flag_checked,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue