From 61c3f3e539f7dad2cc5557c17e0d99520b6ce107 Mon Sep 17 00:00:00 2001 From: Daniel Faust Date: Sun, 20 Oct 2024 17:18:02 +0200 Subject: [PATCH] Export more internals so muda can be used in combination with ksni --- Cargo.toml | 1 + src/icon.rs | 12 ++++++ src/items/icon.rs | 15 ++++--- src/items/predefined.rs | 7 +++- src/items/submenu.rs | 4 ++ src/lib.rs | 8 +++- src/menu.rs | 4 ++ src/platform_impl/gtk/about_dialog.rs | 56 +++++++++++++++++++++++++++ src/platform_impl/gtk/icon.rs | 18 +++++++++ src/platform_impl/gtk/mod.rs | 46 ++++------------------ src/platform_impl/macos/mod.rs | 4 +- src/platform_impl/mod.rs | 3 ++ src/platform_impl/windows/mod.rs | 4 +- 13 files changed, 132 insertions(+), 50 deletions(-) create mode 100644 src/platform_impl/gtk/about_dialog.rs diff --git a/Cargo.toml b/Cargo.toml index e8d72c07..8b3b25fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ features = [ [target.'cfg(target_os = "linux")'.dependencies] gtk = "0.18" libxdo = { version = "0.6.0", optional = true } +png = "0.17" [target.'cfg(target_os = "macos")'.dependencies] objc2 = "0.5.2" diff --git a/src/icon.rs b/src/icon.rs index 14294538..25bc9ec1 100644 --- a/src/icon.rs +++ b/src/icon.rs @@ -163,6 +163,18 @@ impl Icon { let win_icon = PlatformIcon::from_resource(ordinal, size)?; Ok(Icon { inner: win_icon }) } + + /// Convert the icon into a GTK pixbuf object. + #[cfg(target_os = "linux")] + pub fn to_pixbuf(&self) -> gtk::gdk_pixbuf::Pixbuf { + self.inner.to_pixbuf() + } + + /// Convert the icon into PNG. + #[cfg(target_os = "linux")] + pub fn to_png(&self) -> Vec { + self.inner.to_png() + } } /// A native Icon to be used for the menu item diff --git a/src/items/icon.rs b/src/items/icon.rs index 907a5492..daf04442 100644 --- a/src/items/icon.rs +++ b/src/items/icon.rs @@ -145,24 +145,24 @@ impl IconMenuItem { &self.id } - /// Get the text for this check menu item. + /// Get the text for this menu item. pub fn text(&self) -> String { self.inner.borrow().text() } - /// Set the text for this check menu item. `text` could optionally contain + /// Set the text for this menu item. `text` could optionally contain /// an `&` before a character to assign this character as the mnemonic - /// for this check menu item. To display a `&` without assigning a mnemenonic, use `&&`. + /// for this menu item. To display a `&` without assigning a mnemenonic, use `&&`. pub fn set_text>(&self, text: S) { self.inner.borrow_mut().set_text(text.as_ref()) } - /// Get whether this check menu item is enabled or not. + /// Get whether this menu item is enabled or not. pub fn is_enabled(&self) -> bool { self.inner.borrow().is_enabled() } - /// Enable or disable this check menu item. + /// Enable or disable this menu item. pub fn set_enabled(&self, enabled: bool) { self.inner.borrow_mut().set_enabled(enabled) } @@ -172,6 +172,11 @@ impl IconMenuItem { self.inner.borrow_mut().set_accelerator(accelerator) } + /// Get the icon for this menu item. + pub fn icon(&self) -> Option { + self.inner.borrow().icon.clone() + } + /// Change this menu item icon or remove it. pub fn set_icon(&self, icon: Option) { self.inner.borrow_mut().set_icon(icon) diff --git a/src/items/predefined.rs b/src/items/predefined.rs index 8ed4a342..53035548 100644 --- a/src/items/predefined.rs +++ b/src/items/predefined.rs @@ -34,6 +34,11 @@ impl IsMenuItem for PredefinedMenuItem { } impl PredefinedMenuItem { + /// The type of predefined menu item + pub fn predefined_item_type(&self) -> Option { + self.inner.borrow().predefined_item_type.clone() + } + /// Separator menu item pub fn separator() -> PredefinedMenuItem { PredefinedMenuItem::new::<&str>(PredefinedMenuItemType::Separator, None) @@ -241,7 +246,7 @@ fn test_about_metadata() { #[derive(Debug, Clone)] #[non_exhaustive] #[allow(clippy::large_enum_variant)] -pub(crate) enum PredefinedMenuItemType { +pub enum PredefinedMenuItemType { Separator, Copy, Cut, diff --git a/src/items/submenu.rs b/src/items/submenu.rs index cae67400..0aed868c 100644 --- a/src/items/submenu.rs +++ b/src/items/submenu.rs @@ -245,6 +245,10 @@ impl ContextMenu for Submenu { self.inner.borrow_mut().gtk_context_menu() } + fn items(&self) -> Vec { + self.inner.borrow_mut().items() + } + #[cfg(target_os = "macos")] unsafe fn show_context_menu_for_nsview( &self, diff --git a/src/lib.rs b/src/lib.rs index 6357384b..ec52c18e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -152,6 +152,9 @@ pub use items::*; pub use menu::*; pub use menu_id::MenuId; +#[cfg(target_os = "linux")] +pub use platform_impl::AboutDialog; + /// An enumeration of all available menu types, useful to match against /// the items returned from [`Menu::items`] or [`Submenu::items`] #[derive(Clone)] @@ -350,6 +353,9 @@ pub trait ContextMenu { #[cfg(target_os = "linux")] fn gtk_context_menu(&self) -> gtk::Menu; + /// Get all menu items as a vector of `MenuItemKind` elements. + fn items(&self) -> Vec; + /// Shows this menu as a context menu for the specified `NSView`. /// /// - `position` is relative to the window top-left corner, if `None`, the cursor position is used. @@ -417,7 +423,7 @@ impl MenuEvent { } } - pub(crate) fn send(event: MenuEvent) { + pub fn send(event: MenuEvent) { if let Some(handler) = MENU_EVENT_HANDLER.get_or_init(|| None) { handler(event); } else { diff --git a/src/menu.rs b/src/menu.rs index 18d9aaa7..290a37f4 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -403,6 +403,10 @@ impl ContextMenu for Menu { self.inner.borrow_mut().gtk_context_menu() } + fn items(&self) -> Vec { + self.inner.borrow_mut().items() + } + #[cfg(target_os = "macos")] unsafe fn show_context_menu_for_nsview( &self, diff --git a/src/platform_impl/gtk/about_dialog.rs b/src/platform_impl/gtk/about_dialog.rs new file mode 100644 index 00000000..3886f1f7 --- /dev/null +++ b/src/platform_impl/gtk/about_dialog.rs @@ -0,0 +1,56 @@ +use gtk::prelude::*; + +use crate::AboutMetadata; + +/// Displays an about dialog using GTK with the provided metadata. +pub struct AboutDialog { + metadata: AboutMetadata, +} + +impl AboutDialog { + /// Create a new about dialog using `metadata` but without showing it. + pub fn new(metadata: AboutMetadata) -> AboutDialog { + AboutDialog { metadata } + } + + /// Show the about dialog and block until it is closed. + pub fn show(&self) { + let mut builder = gtk::AboutDialog::builder().modal(true).resizable(false); + + if let Some(name) = &self.metadata.name { + builder = builder.program_name(name); + } + if let Some(version) = &self.metadata.full_version() { + builder = builder.version(version); + } + if let Some(authors) = &self.metadata.authors { + builder = builder.authors(authors.clone()); + } + if let Some(comments) = &self.metadata.comments { + builder = builder.comments(comments); + } + if let Some(copyright) = &self.metadata.copyright { + builder = builder.copyright(copyright); + } + if let Some(license) = &self.metadata.license { + builder = builder.license(license); + } + if let Some(website) = &self.metadata.website { + builder = builder.website(website); + } + if let Some(website_label) = &self.metadata.website_label { + builder = builder.website_label(website_label); + } + if let Some(icon) = &self.metadata.icon { + builder = builder.logo(&icon.to_pixbuf()); + } + + let about = builder.build(); + + about.run(); + + unsafe { + about.destroy(); + } + } +} diff --git a/src/platform_impl/gtk/icon.rs b/src/platform_impl/gtk/icon.rs index 31ddd003..37ad54a5 100644 --- a/src/platform_impl/gtk/icon.rs +++ b/src/platform_impl/gtk/icon.rs @@ -2,6 +2,8 @@ // Copyright 2021-2022 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 +use std::io::Cursor; + use gtk::gdk_pixbuf::{Colorspace, Pixbuf}; use crate::icon::BadIcon; @@ -62,4 +64,20 @@ impl PlatformIcon { .scale_simple(w, h, gtk::gdk_pixbuf::InterpType::Bilinear) .unwrap() } + + pub fn to_png(&self) -> Vec { + let mut png = Vec::new(); + + { + let mut encoder = + png::Encoder::new(Cursor::new(&mut png), self.width as _, self.height as _); + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + + let mut writer = encoder.write_header().unwrap(); + writer.write_image_data(&self.raw).unwrap(); + } + + png + } } diff --git a/src/platform_impl/gtk/mod.rs b/src/platform_impl/gtk/mod.rs index e1b53c60..41810449 100644 --- a/src/platform_impl/gtk/mod.rs +++ b/src/platform_impl/gtk/mod.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +mod about_dialog; mod accelerator; mod icon; +pub use about_dialog::AboutDialog; pub(crate) use icon::PlatformIcon; use crate::{ @@ -17,7 +19,7 @@ use crate::{ }; use accelerator::{from_gtk_mnemonic, parse_accelerator, to_gtk_mnemonic}; use glib::translate::ToGlibPtr; -use gtk::{gdk, glib, prelude::*, AboutDialog, Container, Orientation}; +use gtk::{gdk, glib, prelude::*, Container, Orientation}; use std::{ cell::RefCell, collections::{hash_map::Entry, HashMap}, @@ -408,14 +410,14 @@ pub struct MenuChild { gtk_accelerator: Option<(gdk::ModifierType, u32)>, // predefined menu item fields - predefined_item_type: Option, + pub(crate) predefined_item_type: Option, // check menu item fields checked: Option>, is_syncing_checked_state: Option>, // icon menu item fields - icon: Option, + pub(crate) icon: Option, // submenu fields pub children: Option>>>, @@ -1144,42 +1146,8 @@ impl MenuChild { let item = make_item(); register_accel(&item); item.connect_activate(move |_| { - if let Some(metadata) = &metadata { - let mut builder = AboutDialog::builder().modal(true).resizable(false); - - if let Some(name) = &metadata.name { - builder = builder.program_name(name); - } - if let Some(version) = &metadata.full_version() { - builder = builder.version(version); - } - if let Some(authors) = &metadata.authors { - builder = builder.authors(authors.clone()); - } - if let Some(comments) = &metadata.comments { - builder = builder.comments(comments); - } - if let Some(copyright) = &metadata.copyright { - builder = builder.copyright(copyright); - } - if let Some(license) = &metadata.license { - builder = builder.license(license); - } - if let Some(website) = &metadata.website { - builder = builder.website(website); - } - if let Some(website_label) = &metadata.website_label { - builder = builder.website_label(website_label); - } - if let Some(icon) = &metadata.icon { - builder = builder.logo(&icon.inner.to_pixbuf()); - } - - let about = builder.build(); - about.run(); - unsafe { - about.destroy(); - } + if let Some(metadata) = metadata.clone() { + AboutDialog::new(metadata).show(); } }); item diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 8ac3b91f..cae1f95e 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -200,13 +200,13 @@ pub struct MenuChild { accelerator: Option, // predefined menu item fields - predefined_item_type: Option, + pub(crate) predefined_item_type: Option, // check menu item fields checked: Cell, // icon menu item fields - icon: Option, + pub(crate) icon: Option, native_icon: Option, // submenu fields diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs index c6abb720..f978298d 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -12,6 +12,9 @@ mod platform; #[path = "macos/mod.rs"] mod platform; +#[cfg(target_os = "linux")] +pub use platform::AboutDialog; + use std::{ cell::{Ref, RefCell, RefMut}, rc::Rc, diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index e8af74dc..8a6534b3 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -450,13 +450,13 @@ pub(crate) struct MenuChild { accelerator: Option, // predefined menu item fields - predefined_item_type: Option, + pub(crate) predefined_item_type: Option, // check menu item fields checked: bool, // icon menu item fields - icon: Option, + pub(crate) icon: Option, // submenu fields hmenu: HMENU,