From e8adcda807e3e6c8a8a2a45e72ea12f8acabab9d Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 01:31:13 +0500 Subject: [PATCH 01/31] feat(macos): add icon setting for menu items Implement functionality to set icons for menu items in the macOS platform. This enhancement allows for improved visual representation of menu items by utilizing the `menuitem_set_icon` function. --- src/platform_impl/macos/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 494e2c48..6b3eefce 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -708,6 +708,8 @@ impl MenuChild { ns_submenu.setAutoenablesItems(false); ns_menu_item.setEnabled(self.enabled); + + menuitem_set_icon(&ns_menu_item, self.icon.as_ref()); } let id = COUNTER.next(); From 872ad2551e5ced81573d3c184a7ebe21eca433d7 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 01:31:18 +0500 Subject: [PATCH 02/31] feat(submenu): add set_icon method to change menu item icons Added a `set_icon` method to the `Submenu` struct, allowing for changing or removing the icon of a menu item. This enhances the flexibility of menu customization in the user interface. --- src/items/submenu.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/items/submenu.rs b/src/items/submenu.rs index 3c21c382..16cb472c 100644 --- a/src/items/submenu.rs +++ b/src/items/submenu.rs @@ -4,10 +4,7 @@ use std::{cell::RefCell, mem, rc::Rc}; -use crate::{ - dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, IsMenuItem, MenuId, - MenuItemKind, -}; +use crate::{dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, Icon, IsMenuItem, MenuId, MenuItemKind}; /// A menu that can be added to a [`Menu`] or another [`Submenu`]. /// @@ -208,6 +205,11 @@ impl Submenu { self.id().clone() } } + + /// Change this menu item icon or remove it. + pub fn set_icon(&self, icon: Option) { + self.inner.borrow_mut().set_icon(icon) + } } impl ContextMenu for Submenu { From 365e66fb72374ef145fb98684cedf9510bf46f04 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 01:31:55 +0500 Subject: [PATCH 03/31] chore(example): add submenu with icons to the window menu Introduce a new submenu within the "Window" menu, allowing for additional icon items. This enhances the user interface by providing more options directly accessible from the menu. The default menu option for macOS is commented out to allow for customization. Icons are now properly set for the new submenu and its items. --- .../windows-common-controls-v6/src/main.rs | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index bb811b2c..9c790182 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -10,7 +10,7 @@ use muda::{ PredefinedMenuItem, Submenu, }; #[cfg(target_os = "macos")] -use tao::platform::macos::{EventLoopBuilderExtMacOS, WindowExtMacOS}; +use tao::platform::macos::{WindowExtMacOS}; #[cfg(target_os = "windows")] use tao::platform::windows::{EventLoopBuilderExtWindows, WindowExtWindows}; use tao::{ @@ -36,8 +36,8 @@ fn main() { } }); } - #[cfg(target_os = "macos")] - event_loop_builder.with_default_menu(false); + // #[cfg(target_os = "macos")] + // event_loop_builder.with_default_menu(false); let event_loop = event_loop_builder.build(); @@ -67,10 +67,15 @@ fn main() { ]); } + let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../icon.png"); + let icon = load_icon(std::path::Path::new(path)); + let file_m = Submenu::new("&File", true); let edit_m = Submenu::new("&Edit", true); let window_m = Submenu::new("&Window", true); + window_m.set_icon(Some(icon.clone())); + menu_bar.append_items(&[&file_m, &edit_m, &window_m]); let custom_i_1 = MenuItem::new( @@ -79,9 +84,8 @@ fn main() { 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 image_item = IconMenuItem::new("Image Custom 1", true, Some(icon.clone()), 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); @@ -127,14 +131,34 @@ fn main() { edit_m.append_items(&[©_i, &PredefinedMenuItem::separator(), &paste_i]); + // Создаём ещё одно подменю внутри "Window" (подподменю). + let sub_submenu = Submenu::new("Sub Submenu", true); + + // Если хотите, чтобы у заголовка самого суб-субменю тоже была иконка (на Windows, Linux), + // можете установить иконку так: + sub_submenu.set_icon(Some(icon.clone())); + + // Добавляем в подподменю пару пунктов с иконками (IconMenuItem) + let icon_item_1 = IconMenuItem::new("Icon Item 1", true, Some(icon.clone()), None); + let icon_item_2 = IconMenuItem::new("Icon Item 2", true, Some(icon.clone()), None); + + // Добавляем эти пункты в sub_submenu + sub_submenu.append_items(&[&icon_item_1, &icon_item_2]); + + // Теперь в "Window" (window_m) добавляем это подменю + window_m.append(&sub_submenu); + + #[cfg(target_os = "windows")] { use tao::rwh_06::*; - if let RawWindowHandle::Win32(handle) = window.window_handle().unwrap().as_raw() { - menu_bar.init_for_hwnd(handle.hwnd.get()); - } - if let RawWindowHandle::Win32(handle) = window2.window_handle().unwrap().as_raw() { - menu_bar.init_for_hwnd(handle.hwnd.get()); + unsafe { + if let RawWindowHandle::Win32(handle) = window.window_handle().unwrap().as_raw() { + menu_bar.init_for_hwnd(handle.hwnd.get()).unwrap(); + } + if let RawWindowHandle::Win32(handle) = window2.window_handle().unwrap().as_raw() { + menu_bar.init_for_hwnd(handle.hwnd.get()).unwrap(); + } } } #[cfg(target_os = "macos")] @@ -206,7 +230,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option

Date: Thu, 27 Feb 2025 01:40:00 +0500 Subject: [PATCH 04/31] feat(macos): add support for native icons in menu items Enhances the menu item functionality by integrating native icons. The `menuitem_set_native_icon` function is now called to set the native icon for the menu item, improving the visual consistency with platform standards. This change enables better user experience on macOS applications. --- src/platform_impl/macos/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 6b3eefce..a1165f82 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -710,6 +710,8 @@ impl MenuChild { ns_menu_item.setEnabled(self.enabled); menuitem_set_icon(&ns_menu_item, self.icon.as_ref()); + + menuitem_set_native_icon(&ns_menu_item, self.native_icon); } let id = COUNTER.next(); From b1adc8132a197eadbb0bfde7869cdfdcde4d6d3a Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 01:40:04 +0500 Subject: [PATCH 05/31] feat(tui): simplify import statements and update submenu icon handling Consolidate import statements for better readability and maintainability. Update submenu icon setting to use `NativeIcon::Add` for improved consistency across platforms. This change enhances the user interface by ensuring the correct icon is displayed for the submenu. --- examples/windows-common-controls-v6/src/main.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index 9c790182..cb37c3fe 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -3,12 +3,7 @@ // SPDX-License-Identifier: MIT #![allow(unused)] -use muda::{ - accelerator::{Accelerator, Code, Modifiers}, - dpi::{PhysicalPosition, Position}, - AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem, - PredefinedMenuItem, Submenu, -}; +use muda::{accelerator::{Accelerator, Code, Modifiers}, dpi::{PhysicalPosition, Position}, AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem, NativeIcon, PredefinedMenuItem, Submenu}; #[cfg(target_os = "macos")] use tao::platform::macos::{WindowExtMacOS}; #[cfg(target_os = "windows")] @@ -136,7 +131,7 @@ fn main() { // Если хотите, чтобы у заголовка самого суб-субменю тоже была иконка (на Windows, Linux), // можете установить иконку так: - sub_submenu.set_icon(Some(icon.clone())); + sub_submenu.set_native_icon(Some(NativeIcon::Add)); // Добавляем в подподменю пару пунктов с иконками (IconMenuItem) let icon_item_1 = IconMenuItem::new("Icon Item 1", true, Some(icon.clone()), None); From 959aa7b401b023a663b9b3ac7d151eb385d93915 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 01:40:29 +0500 Subject: [PATCH 06/31] chore(example): add support for native icons in submenu items Introduce a new method `set_native_icon` to allow changing the menu item icon to a native image. This method currently supports macOS, while remaining unsupported on Windows and Linux platforms. The addition enhances the customization options for submenu items. --- src/items/submenu.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/items/submenu.rs b/src/items/submenu.rs index 16cb472c..0f9f212c 100644 --- a/src/items/submenu.rs +++ b/src/items/submenu.rs @@ -4,7 +4,7 @@ use std::{cell::RefCell, mem, rc::Rc}; -use crate::{dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, Icon, IsMenuItem, MenuId, MenuItemKind}; +use crate::{dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, Icon, IsMenuItem, MenuId, MenuItemKind, NativeIcon}; /// A menu that can be added to a [`Menu`] or another [`Submenu`]. /// @@ -210,6 +210,16 @@ impl Submenu { pub fn set_icon(&self, icon: Option) { self.inner.borrow_mut().set_icon(icon) } + + /// Change this menu item icon to a native image or remove it. + /// + /// ## Platform-specific: + /// + /// - **Windows / Linux**: Unsupported. + pub fn set_native_icon(&self, _icon: Option) { + #[cfg(target_os = "macos")] + self.inner.borrow_mut().set_native_icon(_icon) + } } impl ContextMenu for Submenu { From 2bc706ac127ee942cafa2136528004561e09761a Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 01:41:58 +0500 Subject: [PATCH 07/31] fix(submenu): format import statements for better readability Reorganized the import statements in `src/items/submenu.rs` to enhance code readability. Each import is now on a separate line, following Rust's style guidelines for clarity and maintainability. --- src/items/submenu.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/items/submenu.rs b/src/items/submenu.rs index 0f9f212c..c36c7eb5 100644 --- a/src/items/submenu.rs +++ b/src/items/submenu.rs @@ -4,7 +4,10 @@ use std::{cell::RefCell, mem, rc::Rc}; -use crate::{dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, Icon, IsMenuItem, MenuId, MenuItemKind, NativeIcon}; +use crate::{ + dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, Icon, IsMenuItem, MenuId, + MenuItemKind, NativeIcon, +}; /// A menu that can be added to a [`Menu`] or another [`Submenu`]. /// From e074079ac518e6c7f99c07a8685e4259b879b547 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 12:18:02 +0500 Subject: [PATCH 08/31] feat(windows): add support for submenu icons in the menu system Enhances the menu system by allowing submenu items to display icons. This change introduces checks for `MenuItemType::Submenu`, and if an icon is present, it retrieves the bitmap and updates the menu item info accordingly. This provides a more visually consistent and informative user interface. --- src/platform_impl/windows/mod.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 67920ce6..a3e7237d 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -36,13 +36,13 @@ use windows_sys::Win32::{ WindowsAndMessaging::{ AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, - GetMenu, GetMenuItemInfoW, InsertMenuW, PostMessageW, PostQuitMessage, RemoveMenu, - SendMessageW, SetForegroundWindow, SetMenu, SetMenuItemInfoW, ShowWindow, - TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, - MF_BYPOSITION, MF_CHECKED, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, - MF_STRING, MF_UNCHECKED, MIIM_BITMAP, MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, - SW_MINIMIZE, TPM_LEFTALIGN, TPM_RETURNCMD, WM_CLOSE, WM_COMMAND, WM_NCACTIVATE, - WM_NCPAINT, + GetMenu, GetMenuItemCount, GetMenuItemInfoW, InsertMenuW, PostMessageW, + PostQuitMessage, RemoveMenu, SendMessageW, SetForegroundWindow, SetMenu, + SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, + MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED, + MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, MF_UNCHECKED, MIIM_BITMAP, + MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, + TPM_RETURNCMD, WM_CLOSE, WM_COMMAND, WM_NCACTIVATE, WM_NCPAINT, }, }, }; @@ -884,11 +884,22 @@ impl MenuChild { .map(|i| unsafe { i.inner.to_hbitmap() }) .unwrap_or(std::ptr::null_mut()); let info = create_icon_item_info(hbitmap); - unsafe { SetMenuItemInfoW(self.hmenu, child_.internal_id, false.into(), &info); SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, false.into(), &info); }; + } else if child_.item_type() == MenuItemType::Submenu { + if let Some(icon) = &child_.icon { + let hbitmap = unsafe { icon.inner.to_hbitmap() }; + let info = create_icon_item_info(hbitmap); + + unsafe { + let pos_main = GetMenuItemCount(self.hmenu).saturating_sub(1); + let pos_popup = GetMenuItemCount(self.hpopupmenu).saturating_sub(1); + SetMenuItemInfoW(self.hmenu, pos_main as u32, true.into(), &info); + SetMenuItemInfoW(self.hpopupmenu, pos_popup as u32, true.into(), &info); + } + } } } From 72feadf6583da2c8d1ec82d3afb8ee0e97a035c7 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 12:25:51 +0500 Subject: [PATCH 09/31] fix(macos): update icon handling for menu items Refactor icon setting logic to ensure that native icons are set properly only if they are provided. This change improves the handling of menu item icons, preventing potential null references and ensuring that both native and regular icons are assigned correctly. --- src/platform_impl/macos/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index a1165f82..141378ad 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -709,9 +709,13 @@ impl MenuChild { ns_menu_item.setEnabled(self.enabled); - menuitem_set_icon(&ns_menu_item, self.icon.as_ref()); + if let Some(native_icon) = self.native_icon { + menuitem_set_native_icon(&ns_menu_item, Some(native_icon)); + } - menuitem_set_native_icon(&ns_menu_item, self.native_icon); + if let Some(icon) = self.icon.as_ref() { + menuitem_set_icon(&ns_menu_item, Some(icon)); + } } let id = COUNTER.next(); From 5a7b57b923e12ff5e9b66dbbea35053f42449796 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 12:30:40 +0500 Subject: [PATCH 10/31] refactor(src/main): improve code formatting and readability Reorganize `use` statements for better clarity and remove unnecessary whitespace. Adjust comments for consistency and clarity. This enhances the overall readability of the code without changing its functionality. --- .../windows-common-controls-v6/src/main.rs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index cb37c3fe..e19b8aaa 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -3,9 +3,14 @@ // SPDX-License-Identifier: MIT #![allow(unused)] -use muda::{accelerator::{Accelerator, Code, Modifiers}, dpi::{PhysicalPosition, Position}, AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem, NativeIcon, PredefinedMenuItem, Submenu}; +use muda::{ + accelerator::{Accelerator, Code, Modifiers}, + dpi::{PhysicalPosition, Position}, + AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem, NativeIcon, + PredefinedMenuItem, Submenu, +}; #[cfg(target_os = "macos")] -use tao::platform::macos::{WindowExtMacOS}; +use tao::platform::macos::WindowExtMacOS; #[cfg(target_os = "windows")] use tao::platform::windows::{EventLoopBuilderExtWindows, WindowExtWindows}; use tao::{ @@ -79,7 +84,6 @@ fn main() { Some(Accelerator::new(Some(Modifiers::ALT), Code::KeyC)), ); - let image_item = IconMenuItem::new("Image Custom 1", true, Some(icon.clone()), None); let check_custom_i_1 = CheckMenuItem::new("Check Custom 1", true, true, None); @@ -126,24 +130,18 @@ fn main() { edit_m.append_items(&[©_i, &PredefinedMenuItem::separator(), &paste_i]); - // Создаём ещё одно подменю внутри "Window" (подподменю). let sub_submenu = Submenu::new("Sub Submenu", true); - // Если хотите, чтобы у заголовка самого суб-субменю тоже была иконка (на Windows, Linux), - // можете установить иконку так: - sub_submenu.set_native_icon(Some(NativeIcon::Add)); + // sub_submenu.set_native_icon(Some(NativeIcon::Add)); + sub_submenu.set_icon(Some(icon.clone())); - // Добавляем в подподменю пару пунктов с иконками (IconMenuItem) let icon_item_1 = IconMenuItem::new("Icon Item 1", true, Some(icon.clone()), None); let icon_item_2 = IconMenuItem::new("Icon Item 2", true, Some(icon.clone()), None); - // Добавляем эти пункты в sub_submenu sub_submenu.append_items(&[&icon_item_1, &icon_item_2]); - // Теперь в "Window" (window_m) добавляем это подменю window_m.append(&sub_submenu); - #[cfg(target_os = "windows")] { use tao::rwh_06::*; From 1ea4b1316b6162792ecf77f754cda62ef71f70dd Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 13:22:43 +0500 Subject: [PATCH 11/31] feat(target): add `gtk` dependency for Linux support in Cargo.toml This commit introduces the `gtk` dependency for Linux targets in the `examples/windows-common-controls-v6/Cargo.toml` file, enabling cross-platform compatibility with GTK-based UI elements. --- examples/windows-common-controls-v6/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/windows-common-controls-v6/Cargo.toml b/examples/windows-common-controls-v6/Cargo.toml index f397c825..a4ebddbe 100644 --- a/examples/windows-common-controls-v6/Cargo.toml +++ b/examples/windows-common-controls-v6/Cargo.toml @@ -10,6 +10,9 @@ muda = { path = "../../", features = ["common-controls-v6"] } tao = "0.28" image = "0.25" +[target.'cfg(target_os = "linux")'.dependencies] +gtk = "0.18" + [target."cfg(target_os = \"windows\")".dependencies.windows-sys] version = "0.59" features = ["Win32_UI_WindowsAndMessaging", "Win32_Foundation"] From 3241cae81f146444372a56473334b3ce385e01d5 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 13:22:46 +0500 Subject: [PATCH 12/31] feat(target): add `gtk` dependency for Linux support in Cargo.toml This commit introduces the `gtk` dependency for Linux targets in the `examples/windows-common-controls-v6/Cargo.toml` file, enabling cross-platform compatibility with GTK-based UI elements. --- examples/windows-common-controls-v6/src/main.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index e19b8aaa..e051a26d 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -13,6 +13,10 @@ use muda::{ use tao::platform::macos::WindowExtMacOS; #[cfg(target_os = "windows")] use tao::platform::windows::{EventLoopBuilderExtWindows, WindowExtWindows}; +#[cfg(target_os = "linux")] +use { + gtk::prelude::*, +}; use tao::{ event::{ElementState, Event, MouseButton, WindowEvent}, event_loop::{ControlFlow, EventLoopBuilder}, @@ -233,6 +237,15 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option

muda::Icon { From 90c8d442554d5949fc4f180e20cde5b1755dd9ee Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 13:23:44 +0500 Subject: [PATCH 13/31] feat(linux): add GTK prelude import for Linux platform support Included the `gtk::prelude::*` import conditionally for Linux platforms to ensure compatibility with GTK-based UI components. This change enhances the cross-platform functionality of the application by properly handling Linux-specific dependencies. --- examples/windows-common-controls-v6/src/main.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index e051a26d..2250d6a5 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT #![allow(unused)] +#[cfg(target_os = "linux")] +use gtk::prelude::*; use muda::{ accelerator::{Accelerator, Code, Modifiers}, dpi::{PhysicalPosition, Position}, @@ -13,10 +15,6 @@ use muda::{ use tao::platform::macos::WindowExtMacOS; #[cfg(target_os = "windows")] use tao::platform::windows::{EventLoopBuilderExtWindows, WindowExtWindows}; -#[cfg(target_os = "linux")] -use { - gtk::prelude::*, -}; use tao::{ event::{ElementState, Event, MouseButton, WindowEvent}, event_loop::{ControlFlow, EventLoopBuilder}, From 9444fbe2111fc7691ce2905e67bb336bb4be1046 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Thu, 27 Feb 2025 14:30:18 +0500 Subject: [PATCH 14/31] feat(gtk): enhance menu item creation with customizable layout Add support for custom layout in menu items by introducing a new parameter `for_menu_bar`. This allows for specific styling when creating menu items for the menu bar. The layout now includes an image and label in a horizontal box, improving the visual appearance and usability of menu items. Adjustments to CSS styling are also included to better align items when not used in the menu bar. --- src/platform_impl/gtk/mod.rs | 44 +++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index 7a9875ab..20de849b 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -1005,17 +1005,45 @@ impl MenuChild { menu_id: u32, accel_group: Option<>k::AccelGroup>, add_to_cache: bool, + for_menu_bar: bool, ) -> crate::Result { let submenu = gtk::Menu::new(); - let item = gtk::MenuItem::builder() + + let image = self + .icon + .as_ref() + .map(|icon| gtk::Image::from_pixbuf(Some(&icon.inner.to_pixbuf_scale(16, 16)))) + .unwrap_or_default(); + + let label = gtk::AccelLabel::builder() .label(to_gtk_mnemonic(&self.text)) .use_underline(true) - .submenu(&submenu) + .xalign(0.0) + .build(); + + let box_container = gtk::Box::new(gtk::Orientation::Horizontal, 6); + if !for_menu_bar { + let style_context = box_container.style_context(); + let css_provider = gtk::CssProvider::new(); + let theme = r#" + box { + margin-left: -22px; + } + "#; + let _ = css_provider.load_from_data(theme.as_bytes()); + style_context.add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION); + } + box_container.pack_start(&image, false, false, 0); + box_container.pack_start(&label, true, true, 0); + box_container.show_all(); + + let item = gtk::MenuItem::builder() + .child(&box_container) .sensitive(self.enabled) .build(); - item.show(); item.set_submenu(Some(&submenu)); + item.show(); self.accel_group = accel_group.cloned(); @@ -1036,12 +1064,12 @@ impl MenuChild { .push((id, submenu.clone())); } - for item in self.items() { + for child_item in self.items() { if add_to_cache { - self.add_menu_item_with_id(item.as_ref(), id)?; + self.add_menu_item_with_id(child_item.as_ref(), id)?; } else { - let gtk_item = item.make_gtk_menu_item(0, None, false, false)?; - submenu.append(>k_item); + let gtk_child_item = child_item.make_gtk_menu_item(0, None, false, false)?; + submenu.append(>k_child_item); } } @@ -1326,7 +1354,7 @@ impl MenuItemKind { let mut child = self.child_mut(); match child.item_type() { MenuItemType::Submenu => { - child.create_gtk_item_for_submenu(menu_id, accel_group, add_to_cache) + child.create_gtk_item_for_submenu(menu_id, accel_group, add_to_cache, for_menu_bar) } MenuItemType::MenuItem => { child.create_gtk_item_for_menu_item(menu_id, accel_group, add_to_cache) From 607e6609ca130bf93ed07f588b3cfac42ee75c77 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Sat, 14 Jun 2025 22:34:28 +0500 Subject: [PATCH 15/31] feat(items): add new methods for creating submenus with icons --- src/items/submenu.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/items/submenu.rs b/src/items/submenu.rs index c36c7eb5..68c9736a 100644 --- a/src/items/submenu.rs +++ b/src/items/submenu.rs @@ -223,6 +223,46 @@ impl Submenu { #[cfg(target_os = "macos")] self.inner.borrow_mut().set_native_icon(_icon) } + + /// Create a new submenu with an icon. + /// + /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic + /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. + pub fn with_icon>(text: S, enabled: bool, icon: Icon) -> Self { + let submenu = Self::new(text, enabled); + submenu.set_icon(Some(icon)); + submenu + } + + /// Create a new submenu with the specified id and icon. + /// + /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic + /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. + pub fn with_id_and_icon, S: AsRef>(id: I, text: S, enabled: bool, icon: Icon) -> Self { + let submenu = Self::with_id(id, text, enabled); + submenu.set_icon(Some(icon)); + submenu + } + + /// Create a new submenu with a native icon. + /// + /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic + /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. + pub fn with_native_icon>(text: S, enabled: bool, icon: NativeIcon) -> Self { + let submenu = Self::new(text, enabled); + submenu.set_native_icon(Some(icon)); + submenu + } + + /// Create a new submenu with the specified id and native icon. + /// + /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic + /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. + pub fn with_id_and_native_icon, S: AsRef>(id: I, text: S, enabled: bool, icon: NativeIcon) -> Self { + let submenu = Self::with_id(id, text, enabled); + submenu.set_native_icon(Some(icon)); + submenu + } } impl ContextMenu for Submenu { From bddb6c37c01422ca4ef2baf7ce13268be492d303 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Sat, 14 Jun 2025 22:34:44 +0500 Subject: [PATCH 16/31] feat(submenu): add support for icons in submenu builder --- src/builders/submenu.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/builders/submenu.rs b/src/builders/submenu.rs index 15a01ea6..cceabbc4 100644 --- a/src/builders/submenu.rs +++ b/src/builders/submenu.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{IsMenuItem, MenuId, Submenu}; +use crate::{Icon, IsMenuItem, MenuId, NativeIcon, Submenu}; /// A builder type for [`Submenu`] #[derive(Clone, Default)] @@ -11,6 +11,8 @@ pub struct SubmenuBuilder<'a> { enabled: bool, id: Option, items: Vec<&'a dyn IsMenuItem>, + icon: Option, + native_icon: Option, } impl std::fmt::Debug for SubmenuBuilder<'_> { @@ -59,12 +61,34 @@ impl<'a> SubmenuBuilder<'a> { self } + /// Set an icon for this submenu. + pub fn icon(mut self, icon: Icon) -> Self { + self.icon = Some(icon); + self + } + + /// Set a native icon for this submenu. + pub fn native_icon(mut self, icon: NativeIcon) -> Self { + self.native_icon = Some(icon); + self + } + /// Build this menu item. pub fn build(self) -> crate::Result { - if let Some(id) = self.id { - Submenu::with_id_and_items(id, self.text, self.enabled, &self.items) + let submenu = if let Some(id) = self.id { + Submenu::with_id_and_items(id, self.text, self.enabled, &self.items)? } else { - Submenu::with_items(self.text, self.enabled, &self.items) + Submenu::with_items(self.text, self.enabled, &self.items)? + }; + + if let Some(icon) = self.icon { + submenu.set_icon(Some(icon)); } + + if let Some(native_icon) = self.native_icon { + submenu.set_native_icon(Some(native_icon)); + } + + Ok(submenu) } } From a7fdc4bcb487034bf83cf375b23ddc965b8a8404 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Sat, 14 Jun 2025 22:36:21 +0500 Subject: [PATCH 17/31] refactor(gtk): simplify context menu item addition and signal connections --- src/platform_impl/gtk/mod.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index 20de849b..b4f3032d 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -836,12 +836,13 @@ impl MenuChild { fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> { return_if_item_not_supported!(item); - let (menu_id, menu) = self.gtk_menu.as_ref().unwrap(); - if let Some(menu) = menu { - let gtk_item = - item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; - menu.append(>k_item); - gtk_item.show(); + if let Some((menu_id, menu)) = self.gtk_menu.as_ref() { + if let Some(menu) = menu { + let gtk_item = + item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; + menu.append(>k_item); + gtk_item.show(); + } } Ok(()) @@ -1441,9 +1442,12 @@ fn show_context_menu( let (tx, rx) = crossbeam_channel::unbounded(); let tx_clone = tx.clone(); - let id = gtk_menu.connect_cancel(move |_| tx_clone.send(false).unwrap_or(())); - let id2 = gtk_menu.connect_selection_done(move |_| tx.send(true).unwrap_or(())); - + let id = gtk_menu.connect_cancel(move |_| { + tx_clone.send(false).unwrap_or(()) + }); + let id2 = gtk_menu.connect_selection_done(move |_| { + tx.send(true).unwrap_or(()) + }); gtk_menu.popup_at_rect( &window, &gdk::Rectangle::new(pos.0, pos.1, 0, 0), From 2f047468f70a89e97b8ff5f1032ac86683465b20 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Sat, 14 Jun 2025 22:36:32 +0500 Subject: [PATCH 18/31] feat(linux): implement context menu for GTK window --- .../windows-common-controls-v6/src/main.rs | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index 2250d6a5..fd65039f 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -235,14 +235,35 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option

Date: Sat, 14 Jun 2025 22:37:30 +0500 Subject: [PATCH 19/31] feat(platform_impl): add support for icons in menu items --- src/platform_impl/windows/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 790d40a0..df00119e 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -246,6 +246,16 @@ impl Menu { SetMenuItemInfoW(self.hmenu, child_.internal_id, FALSE, &info); SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, FALSE, &info); }; + } else if matches!(child_.item_type(), MenuItemType::Icon | MenuItemType::Submenu) { + if let Some(icon) = &child_.icon { + let hbitmap = unsafe { icon.inner.to_hbitmap() }; + let info = create_icon_item_info(hbitmap); + + unsafe { + SetMenuItemInfoW(self.hmenu, child_.internal_id, FALSE, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, FALSE, &info); + } + } } } From 72241a97d8b712036f3a1fb607b10af7344394cc Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Sat, 14 Jun 2025 22:58:25 +0500 Subject: [PATCH 20/31] fix(windows-common-controls): remove temporary window comment --- examples/windows-common-controls-v6/src/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index fd65039f..a4484dfc 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -260,8 +260,6 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option

Date: Sat, 14 Jun 2025 23:21:22 +0500 Subject: [PATCH 21/31] fix(windows): correct method call for internal_id retrieval --- src/platform_impl/windows/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index df00119e..b2ad41cc 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -892,8 +892,8 @@ impl MenuChild { .unwrap_or(std::ptr::null_mut()); let info = create_icon_item_info(hbitmap); unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id, FALSE, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, FALSE, &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id(), FALSE, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), FALSE, &info); }; } else if child_.item_type() == MenuItemType::Submenu { if let Some(icon) = &child_.icon { From 1e106c128f2a1c060b7647a4e8266411e4bba6f4 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Sat, 14 Jun 2025 23:23:40 +0500 Subject: [PATCH 22/31] refactor(windows): simplify menu item info setting --- src/platform_impl/windows/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index b2ad41cc..3ea8e912 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -36,7 +36,7 @@ use windows_sys::Win32::{ WindowsAndMessaging::{ AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, - GetMenu, GetMenuItemCount, GetMenuItemInfoW, InsertMenuW, PostMessageW, + GetMenu, GetMenuItemInfoW, InsertMenuW, PostMessageW, PostQuitMessage, RemoveMenu, SendMessageW, SetForegroundWindow, SetMenu, SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED, @@ -901,10 +901,8 @@ impl MenuChild { let info = create_icon_item_info(hbitmap); unsafe { - let pos_main = GetMenuItemCount(self.hmenu).saturating_sub(1); - let pos_popup = GetMenuItemCount(self.hpopupmenu).saturating_sub(1); - SetMenuItemInfoW(self.hmenu, pos_main as u32, true.into(), &info); - SetMenuItemInfoW(self.hpopupmenu, pos_popup as u32, true.into(), &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id(), FALSE, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), FALSE, &info); } } } From 60dc41d2949175adfbcdfd12ca7a11fd0475df96 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Sun, 15 Jun 2025 01:27:20 +0500 Subject: [PATCH 23/31] refactor(gtk): streamline menu item handling by reducing block scope --- src/platform_impl/gtk/mod.rs | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index b4f3032d..27132584 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -107,16 +107,14 @@ impl Menu { gtk_item.show(); } - { - if let (menu_id, Some(menu)) = &self.gtk_menu { - let gtk_item = - item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; - match op { - AddOp::Append => menu.append(>k_item), - AddOp::Insert(position) => menu.insert(>k_item, position as i32), - } - gtk_item.show(); + if let (menu_id, Some(menu)) = &self.gtk_menu { + let gtk_item = + item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; + match op { + AddOp::Append => menu.append(>k_item), + AddOp::Insert(position) => menu.insert(>k_item, position as i32), } + gtk_item.show(); } } @@ -144,8 +142,7 @@ impl Menu { fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> { return_if_item_not_supported!(item); - let (menu_id, menu) = &self.gtk_menu; - if let Some(menu) = menu { + if let Some((menu_id, menu)) = &self.gtk_menu { let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; menu.append(>k_item); @@ -793,16 +790,14 @@ impl MenuChild { } } - { - if let (menu_id, Some(menu)) = self.gtk_menu.as_ref().unwrap() { - let gtk_item = - item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; - match op { - AddOp::Append => menu.append(>k_item), - AddOp::Insert(position) => menu.insert(>k_item, position as i32), - } - gtk_item.show(); + if let Some((menu_id, Some(menu))) = self.gtk_menu.as_ref() { + let gtk_item = + item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; + match op { + AddOp::Append => menu.append(>k_item), + AddOp::Insert(position) => menu.insert(>k_item, position as i32), } + gtk_item.show(); } } From 4e50fe5cf73d1776e877dfd8258969c7d3c8a54a Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Mon, 16 Jun 2025 20:25:01 +0500 Subject: [PATCH 24/31] refactor(platform_impl): streamline GTK menu connection closures --- .../windows-common-controls-v6/src/main.rs | 14 ++++---- src/items/submenu.rs | 14 ++++++-- src/platform_impl/gtk/mod.rs | 8 ++--- src/platform_impl/windows/mod.rs | 34 +++++++------------ 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index a4484dfc..21e382f3 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -237,20 +237,20 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option

, S: AsRef>(id: I, text: S, enabled: bool, icon: Icon) -> Self { + pub fn with_id_and_icon, S: AsRef>( + id: I, + text: S, + enabled: bool, + icon: Icon, + ) -> Self { let submenu = Self::with_id(id, text, enabled); submenu.set_icon(Some(icon)); submenu @@ -258,7 +263,12 @@ impl Submenu { /// /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. - pub fn with_id_and_native_icon, S: AsRef>(id: I, text: S, enabled: bool, icon: NativeIcon) -> Self { + pub fn with_id_and_native_icon, S: AsRef>( + id: I, + text: S, + enabled: bool, + icon: NativeIcon, + ) -> Self { let submenu = Self::with_id(id, text, enabled); submenu.set_native_icon(Some(icon)); submenu diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index 27132584..d7b88efe 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -1437,12 +1437,8 @@ fn show_context_menu( let (tx, rx) = crossbeam_channel::unbounded(); let tx_clone = tx.clone(); - let id = gtk_menu.connect_cancel(move |_| { - tx_clone.send(false).unwrap_or(()) - }); - let id2 = gtk_menu.connect_selection_done(move |_| { - tx.send(true).unwrap_or(()) - }); + let id = gtk_menu.connect_cancel(move |_| tx_clone.send(false).unwrap_or(())); + let id2 = gtk_menu.connect_selection_done(move |_| tx.send(true).unwrap_or(())); gtk_menu.popup_at_rect( &window, &gdk::Rectangle::new(pos.0, pos.1, 0, 0), diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index e30d2cb1..a29b91fd 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -25,26 +25,15 @@ use std::{ rc::Rc, }; use util::{decode_wide, encode_wide, Accel}; -use windows_sys::Win32::{ - Foundation::{FALSE, LPARAM, LRESULT, POINT, WPARAM}, - Graphics::Gdi::{ClientToScreen, HBITMAP}, - UI::{ - Input::KeyboardAndMouse::{ - GetActiveWindow, SendInput, INPUT, INPUT_KEYBOARD, KEYEVENTF_KEYUP, VK_CONTROL, - }, - Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass}, - WindowsAndMessaging::{ - AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, - DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, - GetMenu, GetMenuItemInfoW, InsertMenuW, PostMessageW, - PostQuitMessage, RemoveMenu, SendMessageW, SetForegroundWindow, SetMenu, - SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, - MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED, - MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, MF_STRING, MF_UNCHECKED, MIIM_BITMAP, - MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, - TPM_RETURNCMD, WM_CLOSE, WM_COMMAND, WM_NCACTIVATE, WM_NCPAINT, - }, - }, +use windows::Win32::UI::WindowsAndMessaging::{ + AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, DestroyAcceleratorTable, + DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, GetMenu, GetMenuItemInfoW, InsertMenuW, + PostMessageW, PostQuitMessage, RemoveMenu, SendMessageW, SetForegroundWindow, SetMenu, + SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, MFS_CHECKED, + MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED, MF_ENABLED, MF_GRAYED, + MF_POPUP, MF_SEPARATOR, MF_STRING, MF_UNCHECKED, MIIM_BITMAP, MIIM_STATE, MIIM_STRING, SW_HIDE, + SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, TPM_RETURNCMD, WM_CLOSE, WM_COMMAND, WM_NCACTIVATE, + WM_NCPAINT, }; type Hwnd = isize; @@ -246,7 +235,10 @@ impl Menu { SetMenuItemInfoW(self.hmenu, child_.internal_id, FALSE, &info); SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, FALSE, &info); }; - } else if matches!(child_.item_type(), MenuItemType::Icon | MenuItemType::Submenu) { + } else if matches!( + child_.item_type(), + MenuItemType::Icon | MenuItemType::Submenu + ) { if let Some(icon) = &child_.icon { let hbitmap = unsafe { icon.inner.to_hbitmap() }; let info = create_icon_item_info(hbitmap); From 9926a232a2957c49dd2ad5af5246881c2792714a Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Mon, 16 Jun 2025 20:39:01 +0500 Subject: [PATCH 25/31] fix(gtk): simplify menu item handling in context menu --- src/platform_impl/gtk/mod.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index d7b88efe..f820fca7 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -142,9 +142,8 @@ impl Menu { fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> { return_if_item_not_supported!(item); - if let Some((menu_id, menu)) = &self.gtk_menu { - let gtk_item = - item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; + if let (menu_id, Some(menu)) = &self.gtk_menu { + let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; menu.append(>k_item); gtk_item.show(); } @@ -831,13 +830,10 @@ impl MenuChild { fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> { return_if_item_not_supported!(item); - if let Some((menu_id, menu)) = self.gtk_menu.as_ref() { - if let Some(menu) = menu { - let gtk_item = - item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; - menu.append(>k_item); - gtk_item.show(); - } + if let Some((menu_id, Some(menu))) = self.gtk_menu.as_ref() { + let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; + menu.append(>k_item); + gtk_item.show(); } Ok(()) From ed7fe4d6ab0ab8865fdef86073cada2b15ed1443 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Mon, 16 Jun 2025 20:40:23 +0500 Subject: [PATCH 26/31] style(gtk): format long lines in `mod.rs` for better readability Refactor long lines in `src/platform_impl/gtk/mod.rs` to improve readability. This change enhances code maintainability without altering functionality. --- src/platform_impl/gtk/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index f820fca7..66b9e06b 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -143,7 +143,8 @@ impl Menu { return_if_item_not_supported!(item); if let (menu_id, Some(menu)) = &self.gtk_menu { - let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; + let gtk_item = + item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; menu.append(>k_item); gtk_item.show(); } @@ -831,7 +832,8 @@ impl MenuChild { return_if_item_not_supported!(item); if let Some((menu_id, Some(menu))) = self.gtk_menu.as_ref() { - let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; + let gtk_item = + item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; menu.append(>k_item); gtk_item.show(); } From 0dbf3b2fd5244930930dace2ca1c8fe4cbbb56ab Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Mon, 16 Jun 2025 20:43:16 +0500 Subject: [PATCH 27/31] refactor(gtk): remove unnecessary line breaks in menu item creation --- src/platform_impl/gtk/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index 66b9e06b..18bce50c 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -143,7 +143,7 @@ impl Menu { return_if_item_not_supported!(item); if let (menu_id, Some(menu)) = &self.gtk_menu { - let gtk_item = + let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; menu.append(>k_item); gtk_item.show(); @@ -832,7 +832,7 @@ impl MenuChild { return_if_item_not_supported!(item); if let Some((menu_id, Some(menu))) = self.gtk_menu.as_ref() { - let gtk_item = + let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true, false)?; menu.append(>k_item); gtk_item.show(); From 1d3dfa290424462a45304aab31bcde1515b8e605 Mon Sep 17 00:00:00 2001 From: Pavel Kuzmin Date: Mon, 16 Jun 2025 20:54:15 +0500 Subject: [PATCH 28/31] feat(platform_impl): update menu item info parameter from FALSE to 0 --- src/platform_impl/windows/mod.rs | 76 ++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index a29b91fd..60c34796 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -25,15 +25,26 @@ use std::{ rc::Rc, }; use util::{decode_wide, encode_wide, Accel}; -use windows::Win32::UI::WindowsAndMessaging::{ - AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, DestroyAcceleratorTable, - DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, GetMenu, GetMenuItemInfoW, InsertMenuW, - PostMessageW, PostQuitMessage, RemoveMenu, SendMessageW, SetForegroundWindow, SetMenu, - SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, MFS_CHECKED, - MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED, MF_ENABLED, MF_GRAYED, - MF_POPUP, MF_SEPARATOR, MF_STRING, MF_UNCHECKED, MIIM_BITMAP, MIIM_STATE, MIIM_STRING, SW_HIDE, - SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, TPM_RETURNCMD, WM_CLOSE, WM_COMMAND, WM_NCACTIVATE, - WM_NCPAINT, +use windows_sys::Win32::{ + Foundation::{LPARAM, LRESULT, POINT, WPARAM}, + Graphics::Gdi::{ClientToScreen, HBITMAP}, + UI::{ + Input::KeyboardAndMouse::{ + GetActiveWindow, SendInput, INPUT, INPUT_KEYBOARD, KEYEVENTF_KEYUP, VK_CONTROL, + }, + Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass}, + WindowsAndMessaging::{ + AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu, + DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos, + GetMenu, GetMenuItemInfoW, InsertMenuW, PostMessageW, PostQuitMessage, RemoveMenu, + SendMessageW, SetForegroundWindow, SetMenu, SetMenuItemInfoW, ShowWindow, + TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW, MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, + MF_BYPOSITION, MF_CHECKED, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_SEPARATOR, + MF_STRING, MF_UNCHECKED, MIIM_BITMAP, MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, + SW_MINIMIZE, TPM_LEFTALIGN, TPM_RETURNCMD, WM_CLOSE, WM_COMMAND, WM_NCACTIVATE, + WM_NCPAINT, + }, + }, }; type Hwnd = isize; @@ -232,8 +243,8 @@ impl Menu { let info = create_icon_item_info(hbitmap); unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id, FALSE, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, FALSE, &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id, 0i32, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, 0i32, &info); }; } else if matches!( child_.item_type(), @@ -244,8 +255,8 @@ impl Menu { let info = create_icon_item_info(hbitmap); unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id, FALSE, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, FALSE, &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id, 0i32, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, 0i32, &info); } } } @@ -670,13 +681,13 @@ impl MenuChild { info.cbSize = std::mem::size_of::() as _; info.fMask = MIIM_STRING; - unsafe { GetMenuItemInfoW(*hmenu, id, FALSE, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, id, 0i32, &mut info) }; info.cch += 1; let mut dw_type_data = Vec::with_capacity(info.cch as usize); info.dwTypeData = dw_type_data.as_mut_ptr(); - unsafe { GetMenuItemInfoW(*hmenu, id, FALSE, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, id, 0i32, &mut info) }; let text = decode_wide(info.dwTypeData); text.split('\t').next().unwrap().to_string() @@ -698,7 +709,7 @@ impl MenuChild { info.fMask = MIIM_STRING; info.dwTypeData = text.as_mut_ptr(); - unsafe { SetMenuItemInfoW(*parent, self.internal_id(), FALSE, &info) }; + unsafe { SetMenuItemInfoW(*parent, self.internal_id(), 0i32, &info) }; if let Some(menu_bars) = menu_bars { for hwnd in menu_bars.borrow().keys() { @@ -716,7 +727,7 @@ impl MenuChild { info.cbSize = std::mem::size_of::() as _; info.fMask = MIIM_STATE; - unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), FALSE, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), 0i32, &mut info) }; (info.fState & MFS_DISABLED) == 0 }) @@ -764,7 +775,7 @@ impl MenuChild { info.cbSize = std::mem::size_of::() as _; info.fMask = MIIM_STATE; - unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), FALSE, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), 0i32, &mut info) }; (info.fState & MFS_CHECKED) != 0 }) @@ -798,7 +809,7 @@ impl MenuChild { .unwrap_or(std::ptr::null_mut()); let info = create_icon_item_info(hbitmap); for (parent, menu_bars) in &self.parents_hemnu { - unsafe { SetMenuItemInfoW(*parent, self.internal_id(), FALSE, &info) }; + unsafe { SetMenuItemInfoW(*parent, self.internal_id(), 0i32, &info) }; if let Some(menu_bars) = menu_bars { for hwnd in menu_bars.borrow().keys() { @@ -884,8 +895,8 @@ impl MenuChild { .unwrap_or(std::ptr::null_mut()); let info = create_icon_item_info(hbitmap); unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id(), FALSE, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), FALSE, &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id(), 0i32, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), 0i32, &info); }; } else if child_.item_type() == MenuItemType::Submenu { if let Some(icon) = &child_.icon { @@ -893,8 +904,8 @@ impl MenuChild { let info = create_icon_item_info(hbitmap); unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id(), FALSE, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), FALSE, &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id(), 0i32, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), 0i32, &info); } } } @@ -1163,7 +1174,7 @@ unsafe extern "system" fn menu_subclass_proc( DefSubclassProc(hwnd as _, msg, wparam, lparam) } } - WM_NCACTIVATE | WM_NCPAINT => { + WM_NCACTIVATE => { // DefSubclassProc needs to be called before calling the // custom dark menu redraw let res = DefSubclassProc(hwnd as _, msg, wparam, lparam); @@ -1181,7 +1192,24 @@ unsafe extern "system" fn menu_subclass_proc( res } + WM_NCPAINT => { + // DefSubclassProc needs to be called before calling the + // custom dark menu redraw + let res = DefSubclassProc(hwnd as _, msg, wparam, lparam); + + let menu = obj_from_dwrefdata::

(dwrefdata); + let theme = menu + .hwnds + .borrow() + .get(&(hwnd as _)) + .copied() + .unwrap_or(MenuTheme::Auto); + if theme.should_use_dark(hwnd as _) { + dark_menu_bar::draw(hwnd as _, msg, wparam, lparam); + } + res + } _ => DefSubclassProc(hwnd as _, msg, wparam, lparam), } } From 08f2bc5081d89b0ce86d4e837286f3dc0d7f75b0 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Tue, 17 Jun 2025 04:13:11 +0300 Subject: [PATCH 29/31] revert common controls example changes --- Cargo.lock | 6 +- .../windows-common-controls-v6/Cargo.toml | 3 - .../windows-common-controls-v6/src/main.rs | 67 +++---------------- 3 files changed, 14 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 799b1220..8367ab16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3091,9 +3091,9 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc2 0.6.0", - "objc2-app-kit 0.3.0", - "objc2-foundation 0.3.0", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-foundation 0.3.1", "once_cell", "parking_lot", "raw-window-handle", diff --git a/examples/windows-common-controls-v6/Cargo.toml b/examples/windows-common-controls-v6/Cargo.toml index a4ebddbe..f397c825 100644 --- a/examples/windows-common-controls-v6/Cargo.toml +++ b/examples/windows-common-controls-v6/Cargo.toml @@ -10,9 +10,6 @@ muda = { path = "../../", features = ["common-controls-v6"] } tao = "0.28" image = "0.25" -[target.'cfg(target_os = "linux")'.dependencies] -gtk = "0.18" - [target."cfg(target_os = \"windows\")".dependencies.windows-sys] version = "0.59" features = ["Win32_UI_WindowsAndMessaging", "Win32_Foundation"] diff --git a/examples/windows-common-controls-v6/src/main.rs b/examples/windows-common-controls-v6/src/main.rs index 21e382f3..21114605 100644 --- a/examples/windows-common-controls-v6/src/main.rs +++ b/examples/windows-common-controls-v6/src/main.rs @@ -3,12 +3,10 @@ // SPDX-License-Identifier: MIT #![allow(unused)] -#[cfg(target_os = "linux")] -use gtk::prelude::*; use muda::{ accelerator::{Accelerator, Code, Modifiers}, dpi::{PhysicalPosition, Position}, - AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem, NativeIcon, + AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem, PredefinedMenuItem, Submenu, }; #[cfg(target_os = "macos")] @@ -38,8 +36,6 @@ fn main() { } }); } - // #[cfg(target_os = "macos")] - // event_loop_builder.with_default_menu(false); let event_loop = event_loop_builder.build(); @@ -69,15 +65,10 @@ fn main() { ]); } - let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../icon.png"); - let icon = load_icon(std::path::Path::new(path)); - let file_m = Submenu::new("&File", true); let edit_m = Submenu::new("&Edit", true); let window_m = Submenu::new("&Window", true); - window_m.set_icon(Some(icon.clone())); - menu_bar.append_items(&[&file_m, &edit_m, &window_m]); let custom_i_1 = MenuItem::new( @@ -86,7 +77,7 @@ fn main() { Some(Accelerator::new(Some(Modifiers::ALT), Code::KeyC)), ); - let image_item = IconMenuItem::new("Image Custom 1", true, Some(icon.clone()), None); + 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); @@ -132,28 +123,14 @@ fn main() { edit_m.append_items(&[©_i, &PredefinedMenuItem::separator(), &paste_i]); - let sub_submenu = Submenu::new("Sub Submenu", true); - - // sub_submenu.set_native_icon(Some(NativeIcon::Add)); - sub_submenu.set_icon(Some(icon.clone())); - - let icon_item_1 = IconMenuItem::new("Icon Item 1", true, Some(icon.clone()), None); - let icon_item_2 = IconMenuItem::new("Icon Item 2", true, Some(icon.clone()), None); - - sub_submenu.append_items(&[&icon_item_1, &icon_item_2]); - - window_m.append(&sub_submenu); - #[cfg(target_os = "windows")] - { + unsafe { use tao::rwh_06::*; - unsafe { - if let RawWindowHandle::Win32(handle) = window.window_handle().unwrap().as_raw() { - menu_bar.init_for_hwnd(handle.hwnd.get()).unwrap(); - } - if let RawWindowHandle::Win32(handle) = window2.window_handle().unwrap().as_raw() { - menu_bar.init_for_hwnd(handle.hwnd.get()).unwrap(); - } + if let RawWindowHandle::Win32(handle) = window.window_handle().unwrap().as_raw() { + menu_bar.init_for_hwnd(handle.hwnd.get()).unwrap(); + } + if let RawWindowHandle::Win32(handle) = window2.window_handle().unwrap().as_raw() { + menu_bar.init_for_hwnd(handle.hwnd.get()).unwrap(); } } #[cfg(target_os = "macos")] @@ -228,6 +205,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option

Date: Tue, 17 Jun 2025 04:21:46 +0300 Subject: [PATCH 30/31] revert 0i32 back to FALSE, revert WM_NCPAINT redundant block, remove redundant set icon for submenu block --- src/platform_impl/windows/mod.rs | 71 +++++++------------------------- 1 file changed, 16 insertions(+), 55 deletions(-) diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 60c34796..4e081005 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -26,7 +26,7 @@ use std::{ }; use util::{decode_wide, encode_wide, Accel}; use windows_sys::Win32::{ - Foundation::{LPARAM, LRESULT, POINT, WPARAM}, + Foundation::{FALSE, LPARAM, LRESULT, POINT, WPARAM}, Graphics::Gdi::{ClientToScreen, HBITMAP}, UI::{ Input::KeyboardAndMouse::{ @@ -233,8 +233,9 @@ impl Menu { { let child_ = child.borrow(); + let item_type = child_.item_type(); - if child_.item_type() == MenuItemType::Icon { + if matches!(item_type, MenuItemType::Icon | MenuItemType::Submenu) { let hbitmap = child_ .icon .as_ref() @@ -243,22 +244,9 @@ impl Menu { let info = create_icon_item_info(hbitmap); unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id, 0i32, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, 0i32, &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id, FALSE, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, FALSE, &info); }; - } else if matches!( - child_.item_type(), - MenuItemType::Icon | MenuItemType::Submenu - ) { - if let Some(icon) = &child_.icon { - let hbitmap = unsafe { icon.inner.to_hbitmap() }; - let info = create_icon_item_info(hbitmap); - - unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id, 0i32, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id, 0i32, &info); - } - } } } @@ -681,13 +669,13 @@ impl MenuChild { info.cbSize = std::mem::size_of::() as _; info.fMask = MIIM_STRING; - unsafe { GetMenuItemInfoW(*hmenu, id, 0i32, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, id, FALSE, &mut info) }; info.cch += 1; let mut dw_type_data = Vec::with_capacity(info.cch as usize); info.dwTypeData = dw_type_data.as_mut_ptr(); - unsafe { GetMenuItemInfoW(*hmenu, id, 0i32, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, id, FALSE, &mut info) }; let text = decode_wide(info.dwTypeData); text.split('\t').next().unwrap().to_string() @@ -709,7 +697,7 @@ impl MenuChild { info.fMask = MIIM_STRING; info.dwTypeData = text.as_mut_ptr(); - unsafe { SetMenuItemInfoW(*parent, self.internal_id(), 0i32, &info) }; + unsafe { SetMenuItemInfoW(*parent, self.internal_id(), FALSE, &info) }; if let Some(menu_bars) = menu_bars { for hwnd in menu_bars.borrow().keys() { @@ -727,7 +715,7 @@ impl MenuChild { info.cbSize = std::mem::size_of::() as _; info.fMask = MIIM_STATE; - unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), 0i32, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), FALSE, &mut info) }; (info.fState & MFS_DISABLED) == 0 }) @@ -775,7 +763,7 @@ impl MenuChild { info.cbSize = std::mem::size_of::() as _; info.fMask = MIIM_STATE; - unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), 0i32, &mut info) }; + unsafe { GetMenuItemInfoW(*hmenu, self.internal_id(), FALSE, &mut info) }; (info.fState & MFS_CHECKED) != 0 }) @@ -809,7 +797,7 @@ impl MenuChild { .unwrap_or(std::ptr::null_mut()); let info = create_icon_item_info(hbitmap); for (parent, menu_bars) in &self.parents_hemnu { - unsafe { SetMenuItemInfoW(*parent, self.internal_id(), 0i32, &info) }; + unsafe { SetMenuItemInfoW(*parent, self.internal_id(), FALSE, &info) }; if let Some(menu_bars) = menu_bars { for hwnd in menu_bars.borrow().keys() { @@ -886,8 +874,9 @@ impl MenuChild { { let child_ = child.borrow(); + let item_type = child_.item_type(); - if child_.item_type() == MenuItemType::Icon { + if matches!(item_type, MenuItemType::Icon | MenuItemType::Submenu) { let hbitmap = child_ .icon .as_ref() @@ -895,19 +884,9 @@ impl MenuChild { .unwrap_or(std::ptr::null_mut()); let info = create_icon_item_info(hbitmap); unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id(), 0i32, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), 0i32, &info); + SetMenuItemInfoW(self.hmenu, child_.internal_id(), FALSE, &info); + SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), FALSE, &info); }; - } else if child_.item_type() == MenuItemType::Submenu { - if let Some(icon) = &child_.icon { - let hbitmap = unsafe { icon.inner.to_hbitmap() }; - let info = create_icon_item_info(hbitmap); - - unsafe { - SetMenuItemInfoW(self.hmenu, child_.internal_id(), 0i32, &info); - SetMenuItemInfoW(self.hpopupmenu, child_.internal_id(), 0i32, &info); - } - } } } @@ -1174,25 +1153,7 @@ unsafe extern "system" fn menu_subclass_proc( DefSubclassProc(hwnd as _, msg, wparam, lparam) } } - WM_NCACTIVATE => { - // DefSubclassProc needs to be called before calling the - // custom dark menu redraw - let res = DefSubclassProc(hwnd as _, msg, wparam, lparam); - - let menu = obj_from_dwrefdata::

(dwrefdata); - let theme = menu - .hwnds - .borrow() - .get(&(hwnd as _)) - .copied() - .unwrap_or(MenuTheme::Auto); - if theme.should_use_dark(hwnd as _) { - dark_menu_bar::draw(hwnd as _, msg, wparam, lparam); - } - - res - } - WM_NCPAINT => { + WM_NCACTIVATE | WM_NCPAINT => { // DefSubclassProc needs to be called before calling the // custom dark menu redraw let res = DefSubclassProc(hwnd as _, msg, wparam, lparam); From 86302493897c2684f124b0a9b1890dac249d28f0 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Tue, 17 Jun 2025 04:28:32 +0300 Subject: [PATCH 31/31] change file, revert constructor items on Submenu, builder is enough --- .changes/submenu-set-icon.md | 10 ++++++++ src/items/submenu.rs | 50 ------------------------------------ 2 files changed, 10 insertions(+), 50 deletions(-) create mode 100644 .changes/submenu-set-icon.md diff --git a/.changes/submenu-set-icon.md b/.changes/submenu-set-icon.md new file mode 100644 index 00000000..a26d5f6c --- /dev/null +++ b/.changes/submenu-set-icon.md @@ -0,0 +1,10 @@ +--- +"muda": "patch" +--- + +Add support for icons on `Submenu` so we added: + +- `Submenu::set_icon` +- `Submenu::set_native_icon` +- `SubmenuBuilder::icon` +- `SubmenuBuilder::native_icon` diff --git a/src/items/submenu.rs b/src/items/submenu.rs index 6c48bd23..a93b841c 100644 --- a/src/items/submenu.rs +++ b/src/items/submenu.rs @@ -223,56 +223,6 @@ impl Submenu { #[cfg(target_os = "macos")] self.inner.borrow_mut().set_native_icon(_icon) } - - /// Create a new submenu with an icon. - /// - /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic - /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. - pub fn with_icon>(text: S, enabled: bool, icon: Icon) -> Self { - let submenu = Self::new(text, enabled); - submenu.set_icon(Some(icon)); - submenu - } - - /// Create a new submenu with the specified id and icon. - /// - /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic - /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. - pub fn with_id_and_icon, S: AsRef>( - id: I, - text: S, - enabled: bool, - icon: Icon, - ) -> Self { - let submenu = Self::with_id(id, text, enabled); - submenu.set_icon(Some(icon)); - submenu - } - - /// Create a new submenu with a native icon. - /// - /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic - /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. - pub fn with_native_icon>(text: S, enabled: bool, icon: NativeIcon) -> Self { - let submenu = Self::new(text, enabled); - submenu.set_native_icon(Some(icon)); - submenu - } - - /// Create a new submenu with the specified id and native icon. - /// - /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic - /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. - pub fn with_id_and_native_icon, S: AsRef>( - id: I, - text: S, - enabled: bool, - icon: NativeIcon, - ) -> Self { - let submenu = Self::with_id(id, text, enabled); - submenu.set_native_icon(Some(icon)); - submenu - } } impl ContextMenu for Submenu {