Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add icon to submenu #277

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/windows-common-controls-v6/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
54 changes: 41 additions & 13 deletions examples/windows-common-controls-v6/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
// 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,
AboutMetadata, CheckMenuItem, ContextMenu, IconMenuItem, Menu, MenuEvent, MenuItem, NativeIcon,
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::{
Expand All @@ -36,8 +38,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();

Expand Down Expand Up @@ -67,10 +69,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(
Expand All @@ -79,9 +86,7 @@ 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);
Expand Down Expand Up @@ -127,14 +132,28 @@ fn main() {

edit_m.append_items(&[&copy_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")]
{
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")]
Expand Down Expand Up @@ -206,7 +225,7 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option<P
{
use tao::rwh_06::*;
if let RawWindowHandle::Win32(handle) = window.window_handle().unwrap().as_raw() {
menu.show_context_menu_for_hwnd(handle.hwnd.get(), position);
unsafe { menu.show_context_menu_for_hwnd(handle.hwnd.get(), position) };
}
}
#[cfg(target_os = "macos")]
Expand All @@ -216,6 +235,15 @@ fn show_context_menu(window: &Window, menu: &dyn ContextMenu, position: Option<P
unsafe { menu.show_context_menu_for_nsview(handle.ns_view.as_ptr() as _, position) };
}
}

#[cfg(target_os = "linux")]
{
gtk::init().ok();
let w = gtk::Window::new(gtk::WindowType::Popup);
w.hide();

menu.show_context_menu_for_gtk_window(&w, position);
}
}

fn load_icon(path: &std::path::Path) -> muda::Icon {
Expand Down
19 changes: 17 additions & 2 deletions src/items/submenu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use std::{cell::RefCell, mem, rc::Rc};

use crate::{
dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, IsMenuItem, MenuId,
MenuItemKind,
dpi::Position, sealed::IsMenuItemBase, util::AddOp, ContextMenu, Icon, IsMenuItem, MenuId,
MenuItemKind, NativeIcon,
};

/// A menu that can be added to a [`Menu`] or another [`Submenu`].
Expand Down Expand Up @@ -208,6 +208,21 @@ impl Submenu {
self.id().clone()
}
}

/// Change this menu item icon or remove it.
pub fn set_icon(&self, icon: Option<Icon>) {
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<NativeIcon>) {
#[cfg(target_os = "macos")]
self.inner.borrow_mut().set_native_icon(_icon)
}
}

impl ContextMenu for Submenu {
Expand Down
44 changes: 36 additions & 8 deletions src/platform_impl/gtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1005,17 +1005,45 @@ impl MenuChild {
menu_id: u32,
accel_group: Option<&gtk::AccelGroup>,
add_to_cache: bool,
for_menu_bar: bool,
) -> crate::Result<gtk::MenuItem> {
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();

Expand All @@ -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(&gtk_item);
let gtk_child_item = child_item.make_gtk_menu_item(0, None, false, false)?;
submenu.append(&gtk_child_item);
}
}

Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,14 @@ impl MenuChild {
ns_submenu.setAutoenablesItems(false);

ns_menu_item.setEnabled(self.enabled);

if let Some(native_icon) = self.native_icon {
menuitem_set_native_icon(&ns_menu_item, Some(native_icon));
}

if let Some(icon) = self.icon.as_ref() {
menuitem_set_icon(&ns_menu_item, Some(icon));
}
}

let id = COUNTER.next();
Expand Down
27 changes: 19 additions & 8 deletions src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
};
Expand Down Expand Up @@ -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);
}
}
}
}

Expand Down