mirror of
https://github.com/italicsjenga/muda.git
synced 2025-01-10 03:41:30 +11:00
refactor: impl drop for inner types (#92)
* refactor: impl drop for inner types * macos * impl todos in macos .remove() * remove code * fix gtk accelerator not working after creating the context menu
This commit is contained in:
parent
043026c30d
commit
662e17d0ec
5
.changes/drop.md
Normal file
5
.changes/drop.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"muda": "minor"
|
||||||
|
---
|
||||||
|
|
||||||
|
Add `Drop` implementation for the inner types to release memory and OS resources.
|
5
.changes/set-text-windows.md
Normal file
5
.changes/set-text-windows.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"muda": "patch"
|
||||||
|
---
|
||||||
|
|
||||||
|
On Windows, fix `.set_text()` sometimes adding gebberish characters after multiple calls.
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
IsMenuItem, MenuEvent, MenuId, MenuItemKind, MenuItemType, Position,
|
IsMenuItem, MenuEvent, MenuId, MenuItemKind, MenuItemType, Position,
|
||||||
};
|
};
|
||||||
use accelerator::{from_gtk_mnemonic, parse_accelerator, to_gtk_mnemonic};
|
use accelerator::{from_gtk_mnemonic, parse_accelerator, to_gtk_mnemonic};
|
||||||
use gtk::{prelude::*, Orientation};
|
use gtk::{prelude::*, Container, Orientation};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -25,41 +25,59 @@ use std::{
|
||||||
|
|
||||||
static COUNTER: Counter = Counter::new();
|
static COUNTER: Counter = Counter::new();
|
||||||
|
|
||||||
macro_rules! return_if_predefined_item_not_supported {
|
macro_rules! is_item_supported {
|
||||||
($item:tt) => {
|
($item:tt) => {{
|
||||||
let child = $item.child();
|
let child = $item.child();
|
||||||
let child_ = child.borrow();
|
let child_ = child.borrow();
|
||||||
match (&child_.item_type, &child_.predefined_item_type) {
|
let supported = if let Some(predefined_item_type) = &child_.predefined_item_type {
|
||||||
(
|
matches!(
|
||||||
MenuItemType::Predefined,
|
predefined_item_type,
|
||||||
PredefinedMenuItemType::Separator
|
PredefinedMenuItemType::Separator
|
||||||
| PredefinedMenuItemType::Copy
|
| PredefinedMenuItemType::Copy
|
||||||
| PredefinedMenuItemType::Cut
|
| PredefinedMenuItemType::Cut
|
||||||
| PredefinedMenuItemType::Paste
|
| PredefinedMenuItemType::Paste
|
||||||
| PredefinedMenuItemType::SelectAll
|
| PredefinedMenuItemType::SelectAll
|
||||||
| PredefinedMenuItemType::About(_),
|
| PredefinedMenuItemType::About(_)
|
||||||
) => {}
|
)
|
||||||
(
|
} else {
|
||||||
MenuItemType::Submenu
|
true
|
||||||
| MenuItemType::MenuItem
|
};
|
||||||
| MenuItemType::Check
|
|
||||||
| MenuItemType::Icon,
|
|
||||||
_,
|
|
||||||
) => {}
|
|
||||||
_ => return Ok(()),
|
|
||||||
}
|
|
||||||
drop(child_);
|
drop(child_);
|
||||||
|
supported
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! return_if_item_not_supported {
|
||||||
|
($item:tt) => {
|
||||||
|
if !is_item_supported!($item) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Menu {
|
pub struct Menu {
|
||||||
id: MenuId,
|
id: MenuId,
|
||||||
children: Vec<Rc<RefCell<MenuChild>>>,
|
children: Vec<Rc<RefCell<MenuChild>>>,
|
||||||
|
// TODO: maybe save a reference to the window?
|
||||||
gtk_menubars: HashMap<u32, gtk::MenuBar>,
|
gtk_menubars: HashMap<u32, gtk::MenuBar>,
|
||||||
accel_group: Option<gtk::AccelGroup>,
|
accel_group: Option<gtk::AccelGroup>,
|
||||||
gtk_menu: (u32, Option<gtk::Menu>), // dedicated menu for tray or context menus
|
gtk_menu: (u32, Option<gtk::Menu>), // dedicated menu for tray or context menus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Menu {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for (id, menu) in &self.gtk_menubars {
|
||||||
|
drop_children_from_menu_and_destroy(*id, menu, &self.children);
|
||||||
|
unsafe { menu.destroy() }
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (id, Some(menu)) = &self.gtk_menu {
|
||||||
|
drop_children_from_menu_and_destroy(*id, menu, &self.children);
|
||||||
|
unsafe { menu.destroy() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Menu {
|
impl Menu {
|
||||||
pub fn new(id: Option<MenuId>) -> Self {
|
pub fn new(id: Option<MenuId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -76,28 +94,28 @@ impl Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
||||||
return_if_predefined_item_not_supported!(item);
|
if is_item_supported!(item) {
|
||||||
|
for (menu_id, menu_bar) in &self.gtk_menubars {
|
||||||
for (menu_id, menu_bar) in &self.gtk_menubars {
|
|
||||||
let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
|
||||||
match op {
|
|
||||||
AddOp::Append => menu_bar.append(>k_item),
|
|
||||||
AddOp::Insert(position) => menu_bar.insert(>k_item, position as i32),
|
|
||||||
}
|
|
||||||
gtk_item.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let (menu_id, menu) = &self.gtk_menu;
|
|
||||||
if let Some(menu) = menu {
|
|
||||||
let gtk_item =
|
let gtk_item =
|
||||||
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
||||||
match op {
|
match op {
|
||||||
AddOp::Append => menu.append(>k_item),
|
AddOp::Append => menu_bar.append(>k_item),
|
||||||
AddOp::Insert(position) => menu.insert(>k_item, position as i32),
|
AddOp::Insert(position) => menu_bar.insert(>k_item, position as i32),
|
||||||
}
|
}
|
||||||
gtk_item.show();
|
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)?;
|
||||||
|
match op {
|
||||||
|
AddOp::Append => menu.append(>k_item),
|
||||||
|
AddOp::Insert(position) => menu.insert(>k_item, position as i32),
|
||||||
|
}
|
||||||
|
gtk_item.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
|
@ -109,7 +127,7 @@ impl Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_menu_item_with_id(&self, item: &dyn crate::IsMenuItem, id: u32) -> crate::Result<()> {
|
fn add_menu_item_with_id(&self, item: &dyn crate::IsMenuItem, id: u32) -> crate::Result<()> {
|
||||||
return_if_predefined_item_not_supported!(item);
|
return_if_item_not_supported!(item);
|
||||||
|
|
||||||
for (menu_id, menu_bar) in self.gtk_menubars.iter().filter(|m| *m.0 == id) {
|
for (menu_id, menu_bar) in self.gtk_menubars.iter().filter(|m| *m.0 == id) {
|
||||||
let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
||||||
|
@ -121,7 +139,7 @@ impl Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
||||||
return_if_predefined_item_not_supported!(item);
|
return_if_item_not_supported!(item);
|
||||||
|
|
||||||
let (menu_id, menu) = &self.gtk_menu;
|
let (menu_id, menu) = &self.gtk_menu;
|
||||||
if let Some(menu) = menu {
|
if let Some(menu) = menu {
|
||||||
|
@ -137,12 +155,13 @@ impl Menu {
|
||||||
self.remove_inner(item, true, None)
|
self.remove_inner(item, true, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_inner(
|
fn remove_inner(
|
||||||
&mut self,
|
&mut self,
|
||||||
item: &dyn crate::IsMenuItem,
|
item: &dyn crate::IsMenuItem,
|
||||||
remove_from_cache: bool,
|
remove_from_cache: bool,
|
||||||
id: Option<u32>,
|
id: Option<u32>,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
|
// get child
|
||||||
let child = {
|
let child = {
|
||||||
let index = self
|
let index = self
|
||||||
.children
|
.children
|
||||||
|
@ -156,46 +175,64 @@ impl Menu {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let MenuItemKind::Submenu(i) = item.kind() {
|
|
||||||
let gtk_menus = i.inner.borrow().gtk_menus.clone();
|
|
||||||
|
|
||||||
for (menu_id, _) in gtk_menus {
|
|
||||||
for item in i.items() {
|
|
||||||
i.inner
|
|
||||||
.borrow_mut()
|
|
||||||
.remove_inner(item.as_ref(), false, Some(menu_id))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (menu_id, menu_bar) in &self.gtk_menubars {
|
for (menu_id, menu_bar) in &self.gtk_menubars {
|
||||||
|
// check if we are removing this item from all gtk_menubars
|
||||||
|
// which is usually when this is the item the user is actaully removing
|
||||||
|
// or if we are removing from a specific menu (id)
|
||||||
|
// which is when the actual item being removed is a submenu
|
||||||
|
// and we are iterating through its children and removing
|
||||||
|
// each child gtk items that are related to this submenu.
|
||||||
if id.map(|i| i == *menu_id).unwrap_or(true) {
|
if id.map(|i| i == *menu_id).unwrap_or(true) {
|
||||||
if let Some(items) = child
|
// bail this is not a supported item like a close_window predefined menu item
|
||||||
.borrow_mut()
|
if is_item_supported!(item) {
|
||||||
.gtk_menu_items
|
let mut child_ = child.borrow_mut();
|
||||||
.borrow_mut()
|
|
||||||
.remove(menu_id)
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
{
|
let menus = child_.gtk_menus.as_ref().unwrap().get(menu_id).cloned();
|
||||||
for item in items {
|
if let Some(menus) = menus {
|
||||||
menu_bar.remove(&item);
|
for (id, menu) in menus {
|
||||||
|
// iterate through children and only remove the gtk items
|
||||||
|
// related to this submenu
|
||||||
|
for item in child_.items() {
|
||||||
|
child_.remove_inner(item.as_ref(), false, Some(id))?;
|
||||||
|
}
|
||||||
|
unsafe { menu.destroy() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child_.gtk_menus.as_mut().unwrap().remove(menu_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove all the gtk items that are related to this gtk menubar and destroy it
|
||||||
|
if let Some(items) = child_.gtk_menu_items.borrow_mut().remove(menu_id) {
|
||||||
|
for item in items {
|
||||||
|
menu_bar.remove(&item);
|
||||||
|
if let Some(accel_group) = &child_.accel_group {
|
||||||
|
if let Some((mods, key)) = child_.gtk_accelerator {
|
||||||
|
item.remove_accelerator(accel_group, key, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { item.destroy() };
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove from the gtk menu assigned to the context menu
|
||||||
if remove_from_cache {
|
if remove_from_cache {
|
||||||
let (menu_id, menu) = &self.gtk_menu;
|
if let (id, Some(menu)) = &self.gtk_menu {
|
||||||
if let Some(menu) = menu {
|
let child_ = child.borrow_mut();
|
||||||
if let Some(items) = child
|
if let Some(items) = child_.gtk_menu_items.borrow_mut().remove(id) {
|
||||||
.borrow_mut()
|
|
||||||
.gtk_menu_items
|
|
||||||
.borrow_mut()
|
|
||||||
.remove(menu_id)
|
|
||||||
{
|
|
||||||
for item in items {
|
for item in items {
|
||||||
menu.remove(&item);
|
menu.remove(&item);
|
||||||
|
if let Some(accel_group) = &child_.accel_group {
|
||||||
|
if let Some((mods, key)) = child_.gtk_accelerator {
|
||||||
|
item.remove_accelerator(accel_group, key, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { item.destroy() };
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -365,22 +402,85 @@ pub struct MenuChild {
|
||||||
gtk_accelerator: Option<(gdk::ModifierType, u32)>,
|
gtk_accelerator: Option<(gdk::ModifierType, u32)>,
|
||||||
|
|
||||||
// predefined menu item fields
|
// predefined menu item fields
|
||||||
predefined_item_type: PredefinedMenuItemType,
|
predefined_item_type: Option<PredefinedMenuItemType>,
|
||||||
|
|
||||||
// check menu item fields
|
// check menu item fields
|
||||||
checked: Rc<AtomicBool>,
|
checked: Option<Rc<AtomicBool>>,
|
||||||
is_syncing_checked_state: Rc<AtomicBool>,
|
is_syncing_checked_state: Option<Rc<AtomicBool>>,
|
||||||
|
|
||||||
// icon menu item fields
|
// icon menu item fields
|
||||||
icon: Option<Icon>,
|
icon: Option<Icon>,
|
||||||
|
|
||||||
// submenu fields
|
// submenu fields
|
||||||
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
||||||
gtk_menus: HashMap<u32, Vec<(u32, gtk::Menu)>>,
|
gtk_menus: Option<HashMap<u32, Vec<(u32, gtk::Menu)>>>,
|
||||||
gtk_menu: (u32, Option<gtk::Menu>), // dedicated menu for tray or context menus
|
gtk_menu: Option<(u32, Option<gtk::Menu>)>, // dedicated menu for tray or context menus
|
||||||
accel_group: Option<gtk::AccelGroup>,
|
accel_group: Option<gtk::AccelGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for MenuChild {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.item_type == MenuItemType::Submenu {
|
||||||
|
for menus in self.gtk_menus.as_ref().unwrap().values() {
|
||||||
|
for (id, menu) in menus {
|
||||||
|
drop_children_from_menu_and_destroy(*id, menu, self.children.as_ref().unwrap());
|
||||||
|
unsafe { menu.destroy() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((id, Some(menu))) = &self.gtk_menu {
|
||||||
|
drop_children_from_menu_and_destroy(*id, menu, self.children.as_ref().unwrap());
|
||||||
|
unsafe { menu.destroy() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for items in self.gtk_menu_items.borrow().values() {
|
||||||
|
for item in items {
|
||||||
|
if let Some(accel_group) = &self.accel_group {
|
||||||
|
if let Some((mods, key)) = self.gtk_accelerator {
|
||||||
|
item.remove_accelerator(accel_group, key, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { item.destroy() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_children_from_menu_and_destroy(
|
||||||
|
id: u32,
|
||||||
|
menu: &impl IsA<Container>,
|
||||||
|
children: &Vec<Rc<RefCell<MenuChild>>>,
|
||||||
|
) {
|
||||||
|
for child in children {
|
||||||
|
let mut child_ = child.borrow_mut();
|
||||||
|
{
|
||||||
|
let mut menu_items = child_.gtk_menu_items.borrow_mut();
|
||||||
|
if let Some(items) = menu_items.remove(&id) {
|
||||||
|
for item in items {
|
||||||
|
menu.remove(&item);
|
||||||
|
if let Some(accel_group) = &child_.accel_group {
|
||||||
|
if let Some((mods, key)) = child_.gtk_accelerator {
|
||||||
|
item.remove_accelerator(accel_group, key, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { item.destroy() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
|
if let Some(menus) = child_.gtk_menus.as_mut().unwrap().remove(&id) {
|
||||||
|
for (id, menu) in menus {
|
||||||
|
let children = child_.children.as_ref().unwrap();
|
||||||
|
drop_children_from_menu_and_destroy(id, &menu, children);
|
||||||
|
unsafe { menu.destroy() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructors
|
/// Constructors
|
||||||
impl MenuChild {
|
impl MenuChild {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -396,7 +496,15 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
item_type: MenuItemType::MenuItem,
|
item_type: MenuItemType::MenuItem,
|
||||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||||
..Default::default()
|
accel_group: None,
|
||||||
|
checked: None,
|
||||||
|
children: None,
|
||||||
|
gtk_accelerator: None,
|
||||||
|
gtk_menu: None,
|
||||||
|
gtk_menus: None,
|
||||||
|
icon: None,
|
||||||
|
is_syncing_checked_state: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,10 +515,16 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
children: Some(Vec::new()),
|
children: Some(Vec::new()),
|
||||||
item_type: MenuItemType::Submenu,
|
item_type: MenuItemType::Submenu,
|
||||||
gtk_menu: (COUNTER.next(), None),
|
gtk_menu: Some((COUNTER.next(), None)),
|
||||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||||
gtk_menus: HashMap::new(),
|
gtk_menus: Some(HashMap::new()),
|
||||||
..Default::default()
|
accel_group: None,
|
||||||
|
gtk_accelerator: None,
|
||||||
|
icon: None,
|
||||||
|
is_syncing_checked_state: None,
|
||||||
|
predefined_item_type: None,
|
||||||
|
accelerator: None,
|
||||||
|
checked: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,9 +535,16 @@ impl MenuChild {
|
||||||
accelerator: item_type.accelerator(),
|
accelerator: item_type.accelerator(),
|
||||||
id: MenuId(COUNTER.next().to_string()),
|
id: MenuId(COUNTER.next().to_string()),
|
||||||
item_type: MenuItemType::Predefined,
|
item_type: MenuItemType::Predefined,
|
||||||
predefined_item_type: item_type,
|
predefined_item_type: Some(item_type),
|
||||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||||
..Default::default()
|
accel_group: None,
|
||||||
|
checked: None,
|
||||||
|
children: None,
|
||||||
|
gtk_accelerator: None,
|
||||||
|
gtk_menu: None,
|
||||||
|
gtk_menus: None,
|
||||||
|
icon: None,
|
||||||
|
is_syncing_checked_state: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,13 +558,19 @@ impl MenuChild {
|
||||||
Self {
|
Self {
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
enabled,
|
enabled,
|
||||||
checked: Rc::new(AtomicBool::new(checked)),
|
checked: Some(Rc::new(AtomicBool::new(checked))),
|
||||||
|
is_syncing_checked_state: Some(Rc::new(AtomicBool::new(false))),
|
||||||
accelerator,
|
accelerator,
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
item_type: MenuItemType::Check,
|
item_type: MenuItemType::Check,
|
||||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||||
is_syncing_checked_state: Rc::new(AtomicBool::new(false)),
|
accel_group: None,
|
||||||
..Default::default()
|
children: None,
|
||||||
|
gtk_accelerator: None,
|
||||||
|
gtk_menu: None,
|
||||||
|
gtk_menus: None,
|
||||||
|
icon: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,8 +589,14 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
item_type: MenuItemType::Icon,
|
item_type: MenuItemType::Icon,
|
||||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||||
is_syncing_checked_state: Rc::new(AtomicBool::new(false)),
|
accel_group: None,
|
||||||
..Default::default()
|
checked: None,
|
||||||
|
children: None,
|
||||||
|
gtk_accelerator: None,
|
||||||
|
gtk_menu: None,
|
||||||
|
gtk_menus: None,
|
||||||
|
is_syncing_checked_state: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,8 +614,15 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
item_type: MenuItemType::Icon,
|
item_type: MenuItemType::Icon,
|
||||||
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
gtk_menu_items: Rc::new(RefCell::new(HashMap::new())),
|
||||||
is_syncing_checked_state: Rc::new(AtomicBool::new(false)),
|
accel_group: None,
|
||||||
..Default::default()
|
checked: None,
|
||||||
|
children: None,
|
||||||
|
gtk_accelerator: None,
|
||||||
|
gtk_menu: None,
|
||||||
|
gtk_menus: None,
|
||||||
|
icon: None,
|
||||||
|
is_syncing_checked_state: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,13 +731,17 @@ impl MenuChild {
|
||||||
.map(|e| e.map(|i| i.downcast_ref::<gtk::CheckMenuItem>().unwrap().is_active()))
|
.map(|e| e.map(|i| i.downcast_ref::<gtk::CheckMenuItem>().unwrap().is_active()))
|
||||||
{
|
{
|
||||||
Some(Some(checked)) => checked,
|
Some(Some(checked)) => checked,
|
||||||
_ => self.checked.load(Ordering::Relaxed),
|
_ => self.checked.as_ref().unwrap().load(Ordering::Relaxed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_checked(&mut self, checked: bool) {
|
pub fn set_checked(&mut self, checked: bool) {
|
||||||
self.checked.store(checked, Ordering::Release);
|
self.checked
|
||||||
self.is_syncing_checked_state.store(true, Ordering::Release);
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.store(checked, Ordering::Release);
|
||||||
|
let is_syncing = self.is_syncing_checked_state.as_ref().unwrap();
|
||||||
|
is_syncing.store(true, Ordering::Release);
|
||||||
for items in self.gtk_menu_items.borrow().values() {
|
for items in self.gtk_menu_items.borrow().values() {
|
||||||
for i in items {
|
for i in items {
|
||||||
i.downcast_ref::<gtk::CheckMenuItem>()
|
i.downcast_ref::<gtk::CheckMenuItem>()
|
||||||
|
@ -605,8 +749,7 @@ impl MenuChild {
|
||||||
.set_active(checked);
|
.set_active(checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.is_syncing_checked_state
|
is_syncing.store(false, Ordering::Release);
|
||||||
.store(false, Ordering::Release);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,30 +774,29 @@ impl MenuChild {
|
||||||
/// Submenu methods
|
/// Submenu methods
|
||||||
impl MenuChild {
|
impl MenuChild {
|
||||||
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
||||||
return_if_predefined_item_not_supported!(item);
|
if is_item_supported!(item) {
|
||||||
|
for menus in self.gtk_menus.as_ref().unwrap().values() {
|
||||||
for menus in self.gtk_menus.values() {
|
for (menu_id, menu) in menus {
|
||||||
for (menu_id, menu) in menus {
|
let gtk_item =
|
||||||
let gtk_item =
|
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
||||||
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
match op {
|
||||||
match op {
|
AddOp::Append => menu.append(>k_item),
|
||||||
AddOp::Append => menu.append(>k_item),
|
AddOp::Insert(position) => menu.insert(>k_item, position as i32),
|
||||||
AddOp::Insert(position) => menu.insert(>k_item, position as i32),
|
}
|
||||||
|
gtk_item.show();
|
||||||
}
|
}
|
||||||
gtk_item.show();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let (menu_id, menu) = &self.gtk_menu;
|
if let (menu_id, Some(menu)) = self.gtk_menu.as_ref().unwrap() {
|
||||||
if let Some(menu) = menu {
|
let gtk_item =
|
||||||
let gtk_item =
|
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
||||||
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
match op {
|
||||||
match op {
|
AddOp::Append => menu.append(>k_item),
|
||||||
AddOp::Append => menu.append(>k_item),
|
AddOp::Insert(position) => menu.insert(>k_item, position as i32),
|
||||||
AddOp::Insert(position) => menu.insert(>k_item, position as i32),
|
}
|
||||||
|
gtk_item.show();
|
||||||
}
|
}
|
||||||
gtk_item.show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,9 +813,9 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_menu_item_with_id(&self, item: &dyn crate::IsMenuItem, id: u32) -> crate::Result<()> {
|
fn add_menu_item_with_id(&self, item: &dyn crate::IsMenuItem, id: u32) -> crate::Result<()> {
|
||||||
return_if_predefined_item_not_supported!(item);
|
return_if_item_not_supported!(item);
|
||||||
|
|
||||||
for menus in self.gtk_menus.values() {
|
for menus in self.gtk_menus.as_ref().unwrap().values() {
|
||||||
for (menu_id, menu) in menus.iter().filter(|m| m.0 == id) {
|
for (menu_id, menu) in menus.iter().filter(|m| m.0 == id) {
|
||||||
let gtk_item =
|
let gtk_item =
|
||||||
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
||||||
|
@ -686,11 +828,11 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
fn add_menu_item_to_context_menu(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
||||||
return_if_predefined_item_not_supported!(item);
|
return_if_item_not_supported!(item);
|
||||||
|
|
||||||
let (menu_id, menu) = &self.gtk_menu;
|
let (menu_id, menu) = self.gtk_menu.as_ref().unwrap();
|
||||||
if let Some(menu) = menu {
|
if let Some(menu) = menu {
|
||||||
let gtk_item = item.make_gtk_menu_item(*menu_id, None, true)?;
|
let gtk_item = item.make_gtk_menu_item(*menu_id, self.accel_group.as_ref(), true)?;
|
||||||
menu.append(>k_item);
|
menu.append(>k_item);
|
||||||
gtk_item.show();
|
gtk_item.show();
|
||||||
}
|
}
|
||||||
|
@ -708,6 +850,7 @@ impl MenuChild {
|
||||||
remove_from_cache: bool,
|
remove_from_cache: bool,
|
||||||
id: Option<u32>,
|
id: Option<u32>,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
|
// get child
|
||||||
let child = {
|
let child = {
|
||||||
let index = self
|
let index = self
|
||||||
.children
|
.children
|
||||||
|
@ -723,48 +866,66 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let MenuItemKind::Submenu(i) = item.kind() {
|
for menus in self.gtk_menus.as_ref().unwrap().values() {
|
||||||
let gtk_menus = i.inner.borrow().gtk_menus.clone();
|
|
||||||
|
|
||||||
for (menu_id, _) in gtk_menus {
|
|
||||||
for item in i.items() {
|
|
||||||
i.inner
|
|
||||||
.borrow_mut()
|
|
||||||
.remove_inner(item.as_ref(), false, Some(menu_id))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for menus in self.gtk_menus.values() {
|
|
||||||
for (menu_id, menu) in menus {
|
for (menu_id, menu) in menus {
|
||||||
|
// check if we are removing this item from all gtk_menus
|
||||||
|
// which is usually when this is the item the user is actaully removing
|
||||||
|
// or if we are removing from a specific menu (id)
|
||||||
|
// which is when the actual item being removed is a submenu
|
||||||
|
// and we are iterating through its children and removing
|
||||||
|
// each child gtk items that are related to this submenu.
|
||||||
if id.map(|i| i == *menu_id).unwrap_or(true) {
|
if id.map(|i| i == *menu_id).unwrap_or(true) {
|
||||||
if let Some(items) = child
|
// bail this is not a supported item like a close_window predefined menu item
|
||||||
.borrow_mut()
|
if is_item_supported!(item) {
|
||||||
.gtk_menu_items
|
let mut child_ = child.borrow_mut();
|
||||||
.borrow_mut()
|
|
||||||
.remove(menu_id)
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
{
|
let menus = child_.gtk_menus.as_ref().unwrap().get(menu_id).cloned();
|
||||||
for item in items {
|
if let Some(menus) = menus {
|
||||||
menu.remove(&item);
|
for (id, menu) in menus {
|
||||||
|
// iterate through children and only remove the gtk items
|
||||||
|
// related to this submenu
|
||||||
|
for item in child_.items() {
|
||||||
|
child_.remove_inner(item.as_ref(), false, Some(id))?;
|
||||||
|
}
|
||||||
|
unsafe { menu.destroy() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child_.gtk_menus.as_mut().unwrap().remove(menu_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove all the gtk items that are related to this gtk menu and destroy it
|
||||||
|
if let Some(items) = child_.gtk_menu_items.borrow_mut().remove(menu_id) {
|
||||||
|
for item in items {
|
||||||
|
menu.remove(&item);
|
||||||
|
if let Some(accel_group) = &child_.accel_group {
|
||||||
|
if let Some((mods, key)) = child_.gtk_accelerator {
|
||||||
|
item.remove_accelerator(accel_group, key, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { item.destroy() };
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove from the gtk menu assigned to the context menu
|
||||||
if remove_from_cache {
|
if remove_from_cache {
|
||||||
let (menu_id, menu) = &self.gtk_menu;
|
if let (id, Some(menu)) = self.gtk_menu.as_ref().unwrap() {
|
||||||
if let Some(menu) = menu {
|
let child_ = child.borrow_mut();
|
||||||
if let Some(items) = child
|
if let Some(items) = child_.gtk_menu_items.borrow_mut().remove(id) {
|
||||||
.borrow_mut()
|
|
||||||
.gtk_menu_items
|
|
||||||
.borrow_mut()
|
|
||||||
.remove(menu_id)
|
|
||||||
{
|
|
||||||
for item in items {
|
for item in items {
|
||||||
menu.remove(&item);
|
menu.remove(&item);
|
||||||
|
if let Some(accel_group) = &child_.accel_group {
|
||||||
|
if let Some((mods, key)) = child_.gtk_accelerator {
|
||||||
|
item.remove_accelerator(accel_group, key, mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { item.destroy() };
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -791,8 +952,9 @@ impl MenuChild {
|
||||||
pub fn gtk_context_menu(&mut self) -> gtk::Menu {
|
pub fn gtk_context_menu(&mut self) -> gtk::Menu {
|
||||||
let mut add_items = false;
|
let mut add_items = false;
|
||||||
{
|
{
|
||||||
if self.gtk_menu.1.is_none() {
|
let gtk_menu = self.gtk_menu.as_mut().unwrap();
|
||||||
self.gtk_menu.1 = Some(gtk::Menu::new());
|
if gtk_menu.1.is_none() {
|
||||||
|
gtk_menu.1 = Some(gtk::Menu::new());
|
||||||
add_items = true;
|
add_items = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -803,7 +965,7 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gtk_menu.1.as_ref().unwrap().clone()
|
self.gtk_menu.as_ref().unwrap().1.as_ref().unwrap().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,6 +1022,8 @@ impl MenuChild {
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(item.clone());
|
.push(item.clone());
|
||||||
self.gtk_menus
|
self.gtk_menus
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
.entry(menu_id)
|
.entry(menu_id)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push((id, submenu.clone()));
|
.push((id, submenu.clone()));
|
||||||
|
@ -921,7 +1085,7 @@ impl MenuChild {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(parse_accelerator)
|
.map(parse_accelerator)
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let predefined_item_type = self.predefined_item_type.clone();
|
let predefined_item_type = self.predefined_item_type.clone().unwrap();
|
||||||
|
|
||||||
let make_item = || {
|
let make_item = || {
|
||||||
gtk::MenuItem::builder()
|
gtk::MenuItem::builder()
|
||||||
|
@ -1038,7 +1202,7 @@ impl MenuChild {
|
||||||
.label(&to_gtk_mnemonic(&self.text))
|
.label(&to_gtk_mnemonic(&self.text))
|
||||||
.use_underline(true)
|
.use_underline(true)
|
||||||
.sensitive(self.enabled)
|
.sensitive(self.enabled)
|
||||||
.active(self.checked.load(Ordering::Relaxed))
|
.active(self.checked.as_ref().unwrap().load(Ordering::Relaxed))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
self.accel_group = accel_group.cloned();
|
self.accel_group = accel_group.cloned();
|
||||||
|
@ -1046,8 +1210,8 @@ impl MenuChild {
|
||||||
register_accel!(self, item, accel_group);
|
register_accel!(self, item, accel_group);
|
||||||
|
|
||||||
let id = self.id.clone();
|
let id = self.id.clone();
|
||||||
let is_syncing_checked_state = self.is_syncing_checked_state.clone();
|
let is_syncing_checked_state = self.is_syncing_checked_state.clone().unwrap();
|
||||||
let checked = self.checked.clone();
|
let checked = self.checked.clone().unwrap();
|
||||||
let store = self.gtk_menu_items.clone();
|
let store = self.gtk_menu_items.clone();
|
||||||
item.connect_toggled(move |i| {
|
item.connect_toggled(move |i| {
|
||||||
let should_dispatch = is_syncing_checked_state
|
let should_dispatch = is_syncing_checked_state
|
||||||
|
|
|
@ -47,17 +47,33 @@ extern "C" {
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
const NSAboutPanelOptionCopyright: &str = "Copyright";
|
const NSAboutPanelOptionCopyright: &str = "Copyright";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct NsMenuRef(u32, id);
|
||||||
|
|
||||||
|
impl Drop for NsMenuRef {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![self.1, removeAllItems];
|
||||||
|
let _: () = msg_send![self.1, release];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Menu {
|
pub struct Menu {
|
||||||
id: MenuId,
|
id: MenuId,
|
||||||
ns_menu: id,
|
ns_menu: NsMenuRef,
|
||||||
children: Rc<RefCell<Vec<Rc<RefCell<MenuChild>>>>>,
|
children: Vec<Rc<RefCell<MenuChild>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Menu {
|
impl Drop for Menu {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
for child in &self.children {
|
||||||
let _: () = msg_send![self.ns_menu, release];
|
let mut child_ = child.borrow_mut();
|
||||||
|
child_.ns_menu_items.remove(&self.ns_menu.0);
|
||||||
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
|
child_.ns_menus.as_mut().unwrap().remove(&self.ns_menu.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,13 +82,13 @@ impl Menu {
|
||||||
pub fn new(id: Option<MenuId>) -> Self {
|
pub fn new(id: Option<MenuId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
ns_menu: unsafe {
|
ns_menu: NsMenuRef(COUNTER.next(), unsafe {
|
||||||
let ns_menu = NSMenu::new(nil);
|
let ns_menu = NSMenu::new(nil);
|
||||||
ns_menu.setAutoenablesItems(NO);
|
ns_menu.setAutoenablesItems(NO);
|
||||||
let _: () = msg_send![ns_menu, retain];
|
let _: () = msg_send![ns_menu, retain];
|
||||||
ns_menu
|
ns_menu
|
||||||
},
|
}),
|
||||||
children: Rc::new(RefCell::new(Vec::new())),
|
children: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,18 +97,18 @@ impl Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
pub fn add_menu_item(&mut self, item: &dyn crate::IsMenuItem, op: AddOp) -> crate::Result<()> {
|
||||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.id)?;
|
let ns_menu_item: id = item.make_ns_item_for_menu(self.ns_menu.0)?;
|
||||||
let child = item.child();
|
let child = item.child();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
match op {
|
match op {
|
||||||
AddOp::Append => {
|
AddOp::Append => {
|
||||||
self.ns_menu.addItem_(ns_menu_item);
|
self.ns_menu.1.addItem_(ns_menu_item);
|
||||||
self.children.borrow_mut().push(child);
|
self.children.push(child);
|
||||||
}
|
}
|
||||||
AddOp::Insert(position) => {
|
AddOp::Insert(position) => {
|
||||||
let () = msg_send![self.ns_menu, insertItem: ns_menu_item atIndex: position as NSInteger];
|
let () = msg_send![self.ns_menu.1, insertItem: ns_menu_item atIndex: position as NSInteger];
|
||||||
self.children.borrow_mut().insert(position, child);
|
self.children.insert(position, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,46 +116,51 @@ impl Menu {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
pub fn remove(&mut self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
||||||
// get a list of instances of the specified `NSMenuItem` in this menu
|
// get child
|
||||||
let child = match item.kind() {
|
let child = {
|
||||||
MenuItemKind::Submenu(i) => i.inner.clone(),
|
let index = self
|
||||||
MenuItemKind::MenuItem(i) => i.inner.clone(),
|
.children
|
||||||
MenuItemKind::Predefined(i) => i.inner.clone(),
|
.iter()
|
||||||
MenuItemKind::Check(i) => i.inner.clone(),
|
.position(|e| e.borrow().id == item.id())
|
||||||
MenuItemKind::Icon(i) => i.inner.clone(),
|
.ok_or(crate::Error::NotAChildOfThisMenu)?;
|
||||||
|
self.children.remove(index)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut child_ = child.borrow_mut();
|
let mut child_ = child.borrow_mut();
|
||||||
if let Some(ns_menu_items) = child_.ns_menu_items.remove(&self.id) {
|
|
||||||
// remove each NSMenuItem from the NSMenu
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
unsafe {
|
let menu_id = &self.ns_menu.0;
|
||||||
for item in ns_menu_items {
|
let menus = child_.ns_menus.as_ref().unwrap().get(menu_id).cloned();
|
||||||
let () = msg_send![self.ns_menu, removeItem: item];
|
if let Some(menus) = menus {
|
||||||
|
for menu in menus {
|
||||||
|
for item in child_.items() {
|
||||||
|
child_.remove_inner(item.as_ref(), false, Some(menu.0))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
child_.ns_menus.as_mut().unwrap().remove(menu_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the item from our internal list of children
|
// remove each NSMenuItem from the NSMenu
|
||||||
let mut children = self.children.borrow_mut();
|
if let Some(ns_menu_items) = child_.ns_menu_items.remove(&self.ns_menu.0) {
|
||||||
let index = children
|
for item in ns_menu_items {
|
||||||
.iter()
|
let () = unsafe { msg_send![self.ns_menu.1, removeItem: item] };
|
||||||
.position(|e| e.borrow().id() == item.id())
|
}
|
||||||
.ok_or(crate::Error::NotAChildOfThisMenu)?;
|
}
|
||||||
children.remove(index);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn items(&self) -> Vec<MenuItemKind> {
|
pub fn items(&self) -> Vec<MenuItemKind> {
|
||||||
self.children
|
self.children
|
||||||
.borrow()
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.borrow().kind(c.clone()))
|
.map(|c| c.borrow().kind(c.clone()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_for_nsapp(&self) {
|
pub fn init_for_nsapp(&self) {
|
||||||
unsafe { NSApp().setMainMenu_(self.ns_menu) }
|
unsafe { NSApp().setMainMenu_(self.ns_menu.1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_for_nsapp(&self) {
|
pub fn remove_for_nsapp(&self) {
|
||||||
|
@ -147,16 +168,16 @@ impl Menu {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_context_menu_for_nsview(&self, view: id, position: Option<Position>) {
|
pub fn show_context_menu_for_nsview(&self, view: id, position: Option<Position>) {
|
||||||
show_context_menu(self.ns_menu, view, position)
|
show_context_menu(self.ns_menu.1, view, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ns_menu(&self) -> *mut std::ffi::c_void {
|
pub fn ns_menu(&self) -> *mut std::ffi::c_void {
|
||||||
self.ns_menu as _
|
self.ns_menu.1 as _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic child in a menu
|
/// A generic child in a menu
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MenuChild {
|
pub struct MenuChild {
|
||||||
// shared fields between submenus and menu items
|
// shared fields between submenus and menu items
|
||||||
item_type: MenuItemType,
|
item_type: MenuItemType,
|
||||||
|
@ -164,13 +185,13 @@ pub struct MenuChild {
|
||||||
text: String,
|
text: String,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
|
|
||||||
ns_menu_items: HashMap<MenuId, Vec<id>>,
|
ns_menu_items: HashMap<u32, Vec<id>>,
|
||||||
|
|
||||||
// menu item fields
|
// menu item fields
|
||||||
accelerator: Option<Accelerator>,
|
accelerator: Option<Accelerator>,
|
||||||
|
|
||||||
// predefined menu item fields
|
// predefined menu item fields
|
||||||
predefined_item_type: PredefinedMenuItemType,
|
predefined_item_type: Option<PredefinedMenuItemType>,
|
||||||
|
|
||||||
// check menu item fields
|
// check menu item fields
|
||||||
checked: bool,
|
checked: bool,
|
||||||
|
@ -181,37 +202,37 @@ pub struct MenuChild {
|
||||||
|
|
||||||
// submenu fields
|
// submenu fields
|
||||||
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
||||||
ns_menus: HashMap<MenuId, Vec<id>>,
|
ns_menus: Option<HashMap<u32, Vec<NsMenuRef>>>,
|
||||||
ns_menu: NsMenuRef,
|
ns_menu: Option<NsMenuRef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl Drop for MenuChild {
|
||||||
struct NsMenuRef(MenuId, id);
|
|
||||||
|
|
||||||
impl Drop for NsMenuRef {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
fn drop_children(id: u32, children: &Vec<Rc<RefCell<MenuChild>>>) {
|
||||||
let _: () = msg_send![self.1, release];
|
for child in children {
|
||||||
}
|
let mut child_ = child.borrow_mut();
|
||||||
}
|
child_.ns_menu_items.remove(&id);
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MenuChild {
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
fn default() -> Self {
|
if let Some(menus) = child_.ns_menus.as_mut().unwrap().remove(&id) {
|
||||||
Self {
|
for menu in menus {
|
||||||
item_type: Default::default(),
|
drop_children(menu.0, child_.children.as_ref().unwrap());
|
||||||
id: Default::default(),
|
}
|
||||||
text: Default::default(),
|
}
|
||||||
enabled: Default::default(),
|
}
|
||||||
ns_menu_items: Default::default(),
|
}
|
||||||
accelerator: Default::default(),
|
}
|
||||||
predefined_item_type: Default::default(),
|
|
||||||
checked: Default::default(),
|
if self.item_type == MenuItemType::Submenu {
|
||||||
icon: Default::default(),
|
for menus in self.ns_menus.as_ref().unwrap().values() {
|
||||||
native_icon: Default::default(),
|
for menu in menus {
|
||||||
children: Default::default(),
|
drop_children(menu.0, self.children.as_ref().unwrap())
|
||||||
ns_menus: Default::default(),
|
}
|
||||||
ns_menu: NsMenuRef(MenuId(COUNTER.next().to_string()), 0 as _),
|
}
|
||||||
|
|
||||||
|
if let Some(menu) = &self.ns_menu {
|
||||||
|
drop_children(menu.0, self.children.as_ref().unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +251,14 @@ impl MenuChild {
|
||||||
enabled,
|
enabled,
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
accelerator,
|
accelerator,
|
||||||
..Default::default()
|
checked: false,
|
||||||
|
children: None,
|
||||||
|
icon: None,
|
||||||
|
native_icon: None,
|
||||||
|
ns_menu: None,
|
||||||
|
ns_menu_items: HashMap::new(),
|
||||||
|
ns_menus: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,12 +269,18 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
enabled,
|
enabled,
|
||||||
children: Some(Vec::new()),
|
children: Some(Vec::new()),
|
||||||
ns_menu: NsMenuRef(MenuId(COUNTER.next().to_string()), unsafe {
|
ns_menu: Some(NsMenuRef(COUNTER.next(), unsafe {
|
||||||
let menu = NSMenu::new(nil);
|
let menu = NSMenu::new(nil);
|
||||||
let _: () = msg_send![menu, retain];
|
let _: () = msg_send![menu, retain];
|
||||||
menu
|
menu
|
||||||
}),
|
})),
|
||||||
..Default::default()
|
accelerator: None,
|
||||||
|
checked: false,
|
||||||
|
icon: None,
|
||||||
|
native_icon: None,
|
||||||
|
ns_menu_items: HashMap::new(),
|
||||||
|
ns_menus: Some(HashMap::new()),
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,9 +313,14 @@ impl MenuChild {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: MenuId(COUNTER.next().to_string()),
|
id: MenuId(COUNTER.next().to_string()),
|
||||||
accelerator,
|
accelerator,
|
||||||
predefined_item_type: item_type,
|
predefined_item_type: Some(item_type),
|
||||||
// ns_menu_item,
|
checked: false,
|
||||||
..Default::default()
|
children: None,
|
||||||
|
icon: None,
|
||||||
|
native_icon: None,
|
||||||
|
ns_menu: None,
|
||||||
|
ns_menu_items: HashMap::new(),
|
||||||
|
ns_menus: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +338,13 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
accelerator,
|
accelerator,
|
||||||
checked,
|
checked,
|
||||||
..Default::default()
|
children: None,
|
||||||
|
icon: None,
|
||||||
|
native_icon: None,
|
||||||
|
ns_menu: None,
|
||||||
|
ns_menu_items: HashMap::new(),
|
||||||
|
ns_menus: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +362,13 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
icon,
|
icon,
|
||||||
accelerator,
|
accelerator,
|
||||||
..Default::default()
|
checked: false,
|
||||||
|
children: None,
|
||||||
|
native_icon: None,
|
||||||
|
ns_menu: None,
|
||||||
|
ns_menu_items: HashMap::new(),
|
||||||
|
ns_menus: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +386,13 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
id: id.unwrap_or_else(|| MenuId(COUNTER.next().to_string())),
|
||||||
native_icon,
|
native_icon,
|
||||||
accelerator,
|
accelerator,
|
||||||
..Default::default()
|
checked: false,
|
||||||
|
children: None,
|
||||||
|
icon: None,
|
||||||
|
ns_menu: None,
|
||||||
|
ns_menu_items: HashMap::new(),
|
||||||
|
ns_menus: None,
|
||||||
|
predefined_item_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -361,7 +418,7 @@ impl MenuChild {
|
||||||
for ns_items in self.ns_menu_items.values() {
|
for ns_items in self.ns_menu_items.values() {
|
||||||
for &ns_item in ns_items {
|
for &ns_item in ns_items {
|
||||||
let () = msg_send![ns_item, setTitle: title];
|
let () = msg_send![ns_item, setTitle: title];
|
||||||
let ns_submenu: *mut Object = msg_send![ns_item, submenu];
|
let ns_submenu: id = msg_send![ns_item, submenu];
|
||||||
if ns_submenu != nil {
|
if ns_submenu != nil {
|
||||||
let () = msg_send![ns_submenu, setTitle: title];
|
let () = msg_send![ns_submenu, setTitle: title];
|
||||||
}
|
}
|
||||||
|
@ -468,28 +525,32 @@ impl MenuChild {
|
||||||
unsafe {
|
unsafe {
|
||||||
match op {
|
match op {
|
||||||
AddOp::Append => {
|
AddOp::Append => {
|
||||||
for menus in self.ns_menus.values() {
|
for menus in self.ns_menus.as_ref().unwrap().values() {
|
||||||
for ns_menu in menus {
|
for ns_menu in menus {
|
||||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.id)?;
|
let ns_menu_item: id =
|
||||||
ns_menu.addItem_(ns_menu_item);
|
item.make_ns_item_for_menu(self.ns_menu.as_ref().unwrap().0)?;
|
||||||
|
ns_menu.1.addItem_(ns_menu_item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.ns_menu.0)?;
|
let ns_menu_item: id =
|
||||||
self.ns_menu.1.addItem_(ns_menu_item);
|
item.make_ns_item_for_menu(self.ns_menu.as_ref().unwrap().0)?;
|
||||||
|
self.ns_menu.as_ref().unwrap().1.addItem_(ns_menu_item);
|
||||||
|
|
||||||
self.children.as_mut().unwrap().push(child);
|
self.children.as_mut().unwrap().push(child);
|
||||||
}
|
}
|
||||||
AddOp::Insert(position) => {
|
AddOp::Insert(position) => {
|
||||||
for menus in self.ns_menus.values() {
|
for menus in self.ns_menus.as_ref().unwrap().values() {
|
||||||
for &ns_menu in menus {
|
for ns_menu in menus {
|
||||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.id)?;
|
let ns_menu_item: id =
|
||||||
let () = msg_send![ns_menu, insertItem: ns_menu_item atIndex: position as NSInteger];
|
item.make_ns_item_for_menu(self.ns_menu.as_ref().unwrap().0)?;
|
||||||
|
let () = msg_send![ns_menu.1, insertItem: ns_menu_item atIndex: position as NSInteger];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(&self.ns_menu.0)?;
|
let ns_menu_item: id =
|
||||||
let () = msg_send![ self.ns_menu.1, insertItem: ns_menu_item atIndex: position as NSInteger];
|
item.make_ns_item_for_menu(self.ns_menu.as_ref().unwrap().0)?;
|
||||||
|
let () = msg_send![ self.ns_menu.as_ref().unwrap().1, insertItem: ns_menu_item atIndex: position as NSInteger];
|
||||||
|
|
||||||
self.children.as_mut().unwrap().insert(position, child);
|
self.children.as_mut().unwrap().insert(position, child);
|
||||||
}
|
}
|
||||||
|
@ -500,40 +561,77 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
pub fn remove(&mut self, item: &dyn crate::IsMenuItem) -> crate::Result<()> {
|
||||||
let child = item.child();
|
self.remove_inner(item, true, None)
|
||||||
|
}
|
||||||
|
pub fn remove_inner(
|
||||||
|
&mut self,
|
||||||
|
item: &dyn crate::IsMenuItem,
|
||||||
|
remove_from_cache: bool,
|
||||||
|
id: Option<u32>,
|
||||||
|
) -> crate::Result<()> {
|
||||||
|
// get child
|
||||||
|
let child = {
|
||||||
|
let index = self
|
||||||
|
.children
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.position(|e| e.borrow().id == item.id())
|
||||||
|
.ok_or(crate::Error::NotAChildOfThisMenu)?;
|
||||||
|
if remove_from_cache {
|
||||||
|
self.children.as_mut().unwrap().remove(index)
|
||||||
|
} else {
|
||||||
|
self.children.as_ref().unwrap().get(index).cloned().unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// get a list of instances of the specified NSMenuItem in this menu
|
for menus in self.ns_menus.as_ref().unwrap().values() {
|
||||||
if let Some(ns_menu_items) = child.borrow_mut().ns_menu_items.remove(&self.id) {
|
for menu in menus {
|
||||||
// remove each NSMenuItem from the NSMenu
|
// check if we are removing this item from all ns_menus
|
||||||
unsafe {
|
// which is usually when this is the item the user is actaully removing
|
||||||
for item in ns_menu_items {
|
// or if we are removing from a specific menu (id)
|
||||||
for menus in self.ns_menus.values() {
|
// which is when the actual item being removed is a submenu
|
||||||
for &ns_menu in menus {
|
// and we are iterating through its children and removing
|
||||||
let () = msg_send![ns_menu, removeItem: item];
|
// each child ns menu item that are related to this submenu.
|
||||||
|
if id.map(|i| i == menu.0).unwrap_or(true) {
|
||||||
|
let mut child_ = child.borrow_mut();
|
||||||
|
|
||||||
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
|
let menus = child_.ns_menus.as_ref().unwrap().get(&menu.0).cloned();
|
||||||
|
if let Some(menus) = menus {
|
||||||
|
for menu in menus {
|
||||||
|
// iterate through children and only remove the ns menu items
|
||||||
|
// related to this submenu
|
||||||
|
for item in child_.items() {
|
||||||
|
child_.remove_inner(item.as_ref(), false, Some(menu.0))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
child_.ns_menus.as_mut().unwrap().remove(&menu.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let () = msg_send![self.ns_menu.1, removeItem: item];
|
if let Some(items) = child_.ns_menu_items.remove(&menu.0) {
|
||||||
|
for item in items {
|
||||||
|
let () = unsafe { msg_send![menu.1, removeItem: item] };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ns_menu_items) = child.borrow_mut().ns_menu_items.remove(&self.ns_menu.0) {
|
if remove_from_cache {
|
||||||
unsafe {
|
if let Some(ns_menu_items) = child
|
||||||
|
.borrow_mut()
|
||||||
|
.ns_menu_items
|
||||||
|
.remove(&self.ns_menu.as_ref().unwrap().0)
|
||||||
|
{
|
||||||
for item in ns_menu_items {
|
for item in ns_menu_items {
|
||||||
let () = msg_send![self.ns_menu.1, removeItem: item];
|
let () =
|
||||||
|
unsafe { msg_send![self.ns_menu.as_ref().unwrap().1, removeItem: item] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the item from our internal list of children
|
|
||||||
let children = self.children.as_mut().unwrap();
|
|
||||||
let index = children
|
|
||||||
.iter()
|
|
||||||
.position(|e| e.borrow().id == item.id())
|
|
||||||
.ok_or(crate::Error::NotAChildOfThisMenu)?;
|
|
||||||
children.remove(index);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,27 +645,27 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_context_menu_for_nsview(&self, view: id, position: Option<Position>) {
|
pub fn show_context_menu_for_nsview(&self, view: id, position: Option<Position>) {
|
||||||
show_context_menu(self.ns_menu.1, view, position)
|
show_context_menu(self.ns_menu.as_ref().unwrap().1, view, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_as_windows_menu_for_nsapp(&self) {
|
pub fn set_as_windows_menu_for_nsapp(&self) {
|
||||||
unsafe { NSApp().setWindowsMenu_(self.ns_menu.1) }
|
unsafe { NSApp().setWindowsMenu_(self.ns_menu.as_ref().unwrap().1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_as_help_menu_for_nsapp(&self) {
|
pub fn set_as_help_menu_for_nsapp(&self) {
|
||||||
unsafe { msg_send![NSApp(), setHelpMenu: self.ns_menu.1] }
|
unsafe { msg_send![NSApp(), setHelpMenu: self.ns_menu.as_ref().unwrap().1] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ns_menu(&self) -> *mut std::ffi::c_void {
|
pub fn ns_menu(&self) -> *mut std::ffi::c_void {
|
||||||
self.ns_menu.1 as _
|
self.ns_menu.as_ref().unwrap().1 as _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NSMenuItem item creation methods
|
/// NSMenuItem item creation methods
|
||||||
impl MenuChild {
|
impl MenuChild {
|
||||||
pub fn create_ns_item_for_submenu(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
pub fn create_ns_item_for_submenu(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||||
let ns_menu_item: *mut Object;
|
let ns_menu_item: id;
|
||||||
let ns_submenu: *mut Object;
|
let ns_submenu: id;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ns_menu_item = NSMenuItem::alloc(nil).autorelease();
|
ns_menu_item = NSMenuItem::alloc(nil).autorelease();
|
||||||
|
@ -583,25 +681,29 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let id = COUNTER.next();
|
||||||
|
|
||||||
for item in self.children.as_ref().unwrap() {
|
for item in self.children.as_ref().unwrap() {
|
||||||
let ns_item = item.borrow_mut().make_ns_item_for_menu(menu_id)?;
|
let ns_item = item.borrow_mut().make_ns_item_for_menu(id)?;
|
||||||
unsafe { ns_submenu.addItem_(ns_item) };
|
unsafe { ns_submenu.addItem_(ns_item) };
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ns_menus
|
self.ns_menus
|
||||||
.entry(menu_id.clone())
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.entry(menu_id)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(ns_submenu);
|
.push(NsMenuRef(id, ns_submenu));
|
||||||
|
|
||||||
self.ns_menu_items
|
self.ns_menu_items
|
||||||
.entry(menu_id.clone())
|
.entry(menu_id)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(ns_menu_item);
|
.push(ns_menu_item);
|
||||||
|
|
||||||
Ok(ns_menu_item)
|
Ok(ns_menu_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_ns_item_for_menu_item(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
pub fn create_ns_item_for_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||||
let ns_menu_item = create_ns_menu_item(
|
let ns_menu_item = create_ns_menu_item(
|
||||||
&self.text,
|
&self.text,
|
||||||
Some(sel!(fireMenuItemAction:)),
|
Some(sel!(fireMenuItemAction:)),
|
||||||
|
@ -628,11 +730,8 @@ impl MenuChild {
|
||||||
Ok(ns_menu_item)
|
Ok(ns_menu_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_ns_item_for_predefined_menu_item(
|
pub fn create_ns_item_for_predefined_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||||
&mut self,
|
let item_type = self.predefined_item_type.as_ref().unwrap();
|
||||||
menu_id: &MenuId,
|
|
||||||
) -> crate::Result<id> {
|
|
||||||
let item_type = &self.predefined_item_type;
|
|
||||||
let ns_menu_item = match item_type {
|
let ns_menu_item = match item_type {
|
||||||
PredefinedMenuItemType::Separator => unsafe {
|
PredefinedMenuItemType::Separator => unsafe {
|
||||||
NSMenuItem::separatorItem(nil).autorelease()
|
NSMenuItem::separatorItem(nil).autorelease()
|
||||||
|
@ -640,7 +739,7 @@ impl MenuChild {
|
||||||
_ => create_ns_menu_item(&self.text, item_type.selector(), &self.accelerator)?,
|
_ => create_ns_menu_item(&self.text, item_type.selector(), &self.accelerator)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let PredefinedMenuItemType::About(_) = self.predefined_item_type {
|
if let PredefinedMenuItemType::About(_) = item_type {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![ns_menu_item, setTarget: ns_menu_item];
|
let _: () = msg_send![ns_menu_item, setTarget: ns_menu_item];
|
||||||
|
|
||||||
|
@ -654,7 +753,7 @@ impl MenuChild {
|
||||||
if !self.enabled {
|
if !self.enabled {
|
||||||
let () = msg_send![ns_menu_item, setEnabled: NO];
|
let () = msg_send![ns_menu_item, setEnabled: NO];
|
||||||
}
|
}
|
||||||
if let PredefinedMenuItemType::Services = self.predefined_item_type {
|
if let PredefinedMenuItemType::Services = item_type {
|
||||||
// we have to assign an empty menu as the app's services menu, and macOS will populate it
|
// we have to assign an empty menu as the app's services menu, and macOS will populate it
|
||||||
let services_menu = NSMenu::new(nil).autorelease();
|
let services_menu = NSMenu::new(nil).autorelease();
|
||||||
let () = msg_send![NSApp(), setServicesMenu: services_menu];
|
let () = msg_send![NSApp(), setServicesMenu: services_menu];
|
||||||
|
@ -670,7 +769,7 @@ impl MenuChild {
|
||||||
Ok(ns_menu_item)
|
Ok(ns_menu_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_ns_item_for_check_menu_item(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
pub fn create_ns_item_for_check_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||||
let ns_menu_item = create_ns_menu_item(
|
let ns_menu_item = create_ns_menu_item(
|
||||||
&self.text,
|
&self.text,
|
||||||
Some(sel!(fireMenuItemAction:)),
|
Some(sel!(fireMenuItemAction:)),
|
||||||
|
@ -700,7 +799,7 @@ impl MenuChild {
|
||||||
Ok(ns_menu_item)
|
Ok(ns_menu_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_ns_item_for_icon_menu_item(&mut self, menu_id: &MenuId) -> crate::Result<id> {
|
pub fn create_ns_item_for_icon_menu_item(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||||
let ns_menu_item = create_ns_menu_item(
|
let ns_menu_item = create_ns_menu_item(
|
||||||
&self.text,
|
&self.text,
|
||||||
Some(sel!(fireMenuItemAction:)),
|
Some(sel!(fireMenuItemAction:)),
|
||||||
|
@ -733,7 +832,7 @@ impl MenuChild {
|
||||||
Ok(ns_menu_item)
|
Ok(ns_menu_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_ns_item_for_menu(&mut self, menu_id: &MenuId) -> crate::Result<*mut Object> {
|
fn make_ns_item_for_menu(&mut self, menu_id: u32) -> crate::Result<id> {
|
||||||
match self.item_type {
|
match self.item_type {
|
||||||
MenuItemType::Submenu => self.create_ns_item_for_submenu(menu_id),
|
MenuItemType::Submenu => self.create_ns_item_for_submenu(menu_id),
|
||||||
MenuItemType::MenuItem => self.create_ns_item_for_menu_item(menu_id),
|
MenuItemType::MenuItem => self.create_ns_item_for_menu_item(menu_id),
|
||||||
|
@ -771,7 +870,7 @@ impl PredefinedMenuItemType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn IsMenuItem + '_ {
|
impl dyn IsMenuItem + '_ {
|
||||||
fn make_ns_item_for_menu(&self, menu_id: &MenuId) -> crate::Result<*mut Object> {
|
fn make_ns_item_for_menu(&self, menu_id: u32) -> crate::Result<id> {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
MenuItemKind::Submenu(i) => i.inner.borrow_mut().create_ns_item_for_submenu(menu_id),
|
MenuItemKind::Submenu(i) => i.inner.borrow_mut().create_ns_item_for_submenu(menu_id),
|
||||||
MenuItemKind::MenuItem(i) => i.inner.borrow_mut().create_ns_item_for_menu_item(menu_id),
|
MenuItemKind::MenuItem(i) => i.inner.borrow_mut().create_ns_item_for_menu_item(menu_id),
|
||||||
|
@ -835,7 +934,7 @@ extern "C" fn fire_menu_item_click(this: &Object, _: Sel, _item: id) {
|
||||||
let ptr: usize = *this.get_ivar(BLOCK_PTR);
|
let ptr: usize = *this.get_ivar(BLOCK_PTR);
|
||||||
let item = ptr as *mut &mut MenuChild;
|
let item = ptr as *mut &mut MenuChild;
|
||||||
|
|
||||||
if let PredefinedMenuItemType::About(about_meta) = &(*item).predefined_item_type {
|
if let Some(PredefinedMenuItemType::About(about_meta)) = &(*item).predefined_item_type {
|
||||||
match about_meta {
|
match about_meta {
|
||||||
Some(about_meta) => {
|
Some(about_meta) => {
|
||||||
unsafe fn mkstr(s: &str) -> id {
|
unsafe fn mkstr(s: &str) -> id {
|
||||||
|
@ -923,7 +1022,7 @@ fn create_ns_menu_item(
|
||||||
.map(|accel| accel.key_modifier_mask())
|
.map(|accel| accel.key_modifier_mask())
|
||||||
.unwrap_or_else(NSEventModifierFlags::empty);
|
.unwrap_or_else(NSEventModifierFlags::empty);
|
||||||
|
|
||||||
let ns_menu_item: *mut Object = msg_send![make_menu_item_class(), alloc];
|
let ns_menu_item: id = msg_send![make_menu_item_class(), alloc];
|
||||||
|
|
||||||
ns_menu_item.initWithTitle_action_keyEquivalent_(title, selector, key_equivalent);
|
ns_menu_item.initWithTitle_action_keyEquivalent_(title, selector, key_equivalent);
|
||||||
ns_menu_item.setKeyEquivalentModifierMask_(modifier_mask);
|
ns_menu_item.setKeyEquivalentModifierMask_(modifier_mask);
|
||||||
|
|
|
@ -30,9 +30,9 @@ use windows_sys::Win32::{
|
||||||
Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass},
|
Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass},
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu,
|
AppendMenuW, CreateAcceleratorTableW, CreateMenu, CreatePopupMenu,
|
||||||
DestroyAcceleratorTable, DrawMenuBar, EnableMenuItem, GetCursorPos, GetMenu,
|
DestroyAcceleratorTable, DestroyMenu, DrawMenuBar, EnableMenuItem, GetCursorPos,
|
||||||
GetMenuItemInfoW, InsertMenuW, PostQuitMessage, RemoveMenu, SendMessageW, SetMenu,
|
GetMenu, GetMenuItemInfoW, InsertMenuW, PostQuitMessage, RemoveMenu, SendMessageW,
|
||||||
SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW,
|
SetMenu, SetMenuItemInfoW, ShowWindow, TrackPopupMenu, HACCEL, HMENU, MENUITEMINFOW,
|
||||||
MFS_CHECKED, MFS_DISABLED, MF_BYCOMMAND, MF_BYPOSITION, MF_CHECKED, MF_DISABLED,
|
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,
|
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, WM_CLOSE,
|
MIIM_STATE, MIIM_STRING, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, TPM_LEFTALIGN, WM_CLOSE,
|
||||||
|
@ -61,7 +61,7 @@ macro_rules! inner_menu_child_and_flags {
|
||||||
MenuItemKind::Predefined(i) => {
|
MenuItemKind::Predefined(i) => {
|
||||||
let child = i.inner.clone();
|
let child = i.inner.clone();
|
||||||
let child_ = child.borrow();
|
let child_ = child.borrow();
|
||||||
match child_.predefined_item_type {
|
match child_.predefined_item_type.as_ref().unwrap() {
|
||||||
PredefinedMenuItemType::None => return Ok(()),
|
PredefinedMenuItemType::None => return Ok(()),
|
||||||
PredefinedMenuItemType::Separator => {
|
PredefinedMenuItemType::Separator => {
|
||||||
flags |= MF_SEPARATOR;
|
flags |= MF_SEPARATOR;
|
||||||
|
@ -101,6 +101,44 @@ pub(crate) struct Menu {
|
||||||
children: Vec<Rc<RefCell<MenuChild>>>,
|
children: Vec<Rc<RefCell<MenuChild>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Menu {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for hwnd in self.hwnds.clone() {
|
||||||
|
let _ = self.remove_for_hwnd(hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_from_children_stores(id: &MenuId, children: &Vec<Rc<RefCell<MenuChild>>>) {
|
||||||
|
for child in children {
|
||||||
|
let mut child_ = child.borrow_mut();
|
||||||
|
child_.root_menu_haccel_stores.remove(id);
|
||||||
|
if child_.item_type == MenuItemType::Submenu {
|
||||||
|
remove_from_children_stores(id, child_.children.as_ref().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_from_children_stores(&self.id, &self.children);
|
||||||
|
|
||||||
|
for child in &self.children {
|
||||||
|
let child_ = child.borrow();
|
||||||
|
let id = if child_.item_type == MenuItemType::Submenu {
|
||||||
|
child_.hmenu as _
|
||||||
|
} else {
|
||||||
|
child_.internal_id
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
RemoveMenu(self.hpopupmenu, id, MF_BYCOMMAND);
|
||||||
|
RemoveMenu(self.hmenu, id, MF_BYCOMMAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
DestroyMenu(self.hmenu);
|
||||||
|
DestroyMenu(self.hpopupmenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Menu {
|
impl Menu {
|
||||||
pub fn new(id: Option<MenuId>) -> Self {
|
pub fn new(id: Option<MenuId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -124,9 +162,7 @@ impl Menu {
|
||||||
child
|
child
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.root_menu_haccel_stores
|
.root_menu_haccel_stores
|
||||||
.as_mut()
|
.insert(self.id.clone(), self.haccel_store.clone());
|
||||||
.unwrap()
|
|
||||||
.push(self.haccel_store.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -371,7 +407,7 @@ pub(crate) struct MenuChild {
|
||||||
text: String,
|
text: String,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
parents_hemnu: Vec<HMENU>,
|
parents_hemnu: Vec<HMENU>,
|
||||||
root_menu_haccel_stores: Option<Vec<Rc<RefCell<AccelWrapper>>>>,
|
root_menu_haccel_stores: HashMap<MenuId, Rc<RefCell<AccelWrapper>>>,
|
||||||
|
|
||||||
// menu item fields
|
// menu item fields
|
||||||
internal_id: u32,
|
internal_id: u32,
|
||||||
|
@ -379,7 +415,7 @@ pub(crate) struct MenuChild {
|
||||||
accelerator: Option<Accelerator>,
|
accelerator: Option<Accelerator>,
|
||||||
|
|
||||||
// predefined menu item fields
|
// predefined menu item fields
|
||||||
predefined_item_type: PredefinedMenuItemType,
|
predefined_item_type: Option<PredefinedMenuItemType>,
|
||||||
|
|
||||||
// check menu item fields
|
// check menu item fields
|
||||||
checked: bool,
|
checked: bool,
|
||||||
|
@ -393,6 +429,23 @@ pub(crate) struct MenuChild {
|
||||||
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
pub children: Option<Vec<Rc<RefCell<MenuChild>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for MenuChild {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.item_type == MenuItemType::Submenu {
|
||||||
|
unsafe {
|
||||||
|
DestroyMenu(self.hmenu);
|
||||||
|
DestroyMenu(self.hpopupmenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.accelerator.is_some() {
|
||||||
|
for store in self.root_menu_haccel_stores.values() {
|
||||||
|
AccelAction::remove(&mut store.borrow_mut(), self.internal_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructors
|
/// Constructors
|
||||||
impl MenuChild {
|
impl MenuChild {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -410,8 +463,13 @@ impl MenuChild {
|
||||||
internal_id,
|
internal_id,
|
||||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||||
accelerator,
|
accelerator,
|
||||||
root_menu_haccel_stores: Some(Vec::new()),
|
root_menu_haccel_stores: HashMap::new(),
|
||||||
..Default::default()
|
predefined_item_type: None,
|
||||||
|
icon: None,
|
||||||
|
checked: false,
|
||||||
|
children: None,
|
||||||
|
hmenu: 0,
|
||||||
|
hpopupmenu: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,8 +485,11 @@ impl MenuChild {
|
||||||
internal_id,
|
internal_id,
|
||||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||||
hpopupmenu: unsafe { CreatePopupMenu() },
|
hpopupmenu: unsafe { CreatePopupMenu() },
|
||||||
root_menu_haccel_stores: Some(Vec::new()),
|
root_menu_haccel_stores: HashMap::new(),
|
||||||
..Default::default()
|
predefined_item_type: None,
|
||||||
|
icon: None,
|
||||||
|
checked: false,
|
||||||
|
accelerator: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,9 +503,13 @@ impl MenuChild {
|
||||||
internal_id,
|
internal_id,
|
||||||
id: MenuId(internal_id.to_string()),
|
id: MenuId(internal_id.to_string()),
|
||||||
accelerator: item_type.accelerator(),
|
accelerator: item_type.accelerator(),
|
||||||
predefined_item_type: item_type,
|
predefined_item_type: Some(item_type),
|
||||||
root_menu_haccel_stores: Some(Vec::new()),
|
root_menu_haccel_stores: HashMap::new(),
|
||||||
..Default::default()
|
icon: None,
|
||||||
|
checked: false,
|
||||||
|
children: None,
|
||||||
|
hmenu: 0,
|
||||||
|
hpopupmenu: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,8 +530,12 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||||
accelerator,
|
accelerator,
|
||||||
checked,
|
checked,
|
||||||
root_menu_haccel_stores: Some(Vec::new()),
|
root_menu_haccel_stores: HashMap::new(),
|
||||||
..Default::default()
|
predefined_item_type: None,
|
||||||
|
icon: None,
|
||||||
|
children: None,
|
||||||
|
hmenu: 0,
|
||||||
|
hpopupmenu: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,8 +556,12 @@ impl MenuChild {
|
||||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||||
accelerator,
|
accelerator,
|
||||||
icon,
|
icon,
|
||||||
root_menu_haccel_stores: Some(Vec::new()),
|
root_menu_haccel_stores: HashMap::new(),
|
||||||
..Default::default()
|
predefined_item_type: None,
|
||||||
|
checked: false,
|
||||||
|
children: None,
|
||||||
|
hmenu: 0,
|
||||||
|
hpopupmenu: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,8 +581,13 @@ impl MenuChild {
|
||||||
internal_id,
|
internal_id,
|
||||||
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
id: id.unwrap_or_else(|| MenuId(internal_id.to_string())),
|
||||||
accelerator,
|
accelerator,
|
||||||
root_menu_haccel_stores: Some(Vec::new()),
|
root_menu_haccel_stores: HashMap::new(),
|
||||||
..Default::default()
|
predefined_item_type: None,
|
||||||
|
icon: None,
|
||||||
|
checked: false,
|
||||||
|
children: None,
|
||||||
|
hmenu: 0,
|
||||||
|
hpopupmenu: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,16 +634,17 @@ impl MenuChild {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_text(&mut self, text: &str) {
|
pub fn set_text(&mut self, text: &str) {
|
||||||
self.text = if let Some(accelerator) = self.accelerator {
|
self.text = text.to_string();
|
||||||
format!("{text}\t{}", accelerator)
|
let mut text = if let Some(accelerator) = self.accelerator {
|
||||||
|
encode_wide(format!("{text}\t{}", accelerator))
|
||||||
} else {
|
} else {
|
||||||
text.to_string()
|
encode_wide(text)
|
||||||
};
|
};
|
||||||
for parent in &self.parents_hemnu {
|
for parent in &self.parents_hemnu {
|
||||||
let mut info: MENUITEMINFOW = unsafe { std::mem::zeroed() };
|
let mut info: MENUITEMINFOW = unsafe { std::mem::zeroed() };
|
||||||
info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
|
info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
|
||||||
info.fMask = MIIM_STRING;
|
info.fMask = MIIM_STRING;
|
||||||
info.dwTypeData = encode_wide(&self.text).as_mut_ptr();
|
info.dwTypeData = text.as_mut_ptr();
|
||||||
|
|
||||||
unsafe { SetMenuItemInfoW(*parent, self.internal_id(), false.into(), &info) };
|
unsafe { SetMenuItemInfoW(*parent, self.internal_id(), false.into(), &info) };
|
||||||
}
|
}
|
||||||
|
@ -603,8 +682,7 @@ impl MenuChild {
|
||||||
self.accelerator = accelerator;
|
self.accelerator = accelerator;
|
||||||
self.set_text(&self.text.clone());
|
self.set_text(&self.text.clone());
|
||||||
|
|
||||||
let haccel_stores = self.root_menu_haccel_stores.as_mut().unwrap();
|
for store in self.root_menu_haccel_stores.values() {
|
||||||
for store in haccel_stores {
|
|
||||||
let mut store = store.borrow_mut();
|
let mut store = store.borrow_mut();
|
||||||
if let Some(accelerator) = self.accelerator {
|
if let Some(accelerator) = self.accelerator {
|
||||||
AccelAction::add(&mut store, self.internal_id, &accelerator)?
|
AccelAction::add(&mut store, self.internal_id, &accelerator)?
|
||||||
|
@ -676,9 +754,7 @@ impl MenuChild {
|
||||||
child
|
child
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.root_menu_haccel_stores
|
.root_menu_haccel_stores
|
||||||
.as_mut()
|
.extend(self.root_menu_haccel_stores.clone());
|
||||||
.unwrap()
|
|
||||||
.extend_from_slice(self.root_menu_haccel_stores.as_ref().unwrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -695,7 +771,7 @@ impl MenuChild {
|
||||||
text.push('\t');
|
text.push('\t');
|
||||||
text.push_str(&accel_str);
|
text.push_str(&accel_str);
|
||||||
|
|
||||||
for root_menu in self.root_menu_haccel_stores.as_mut().unwrap() {
|
for root_menu in self.root_menu_haccel_stores.values() {
|
||||||
let mut haccel = root_menu.borrow_mut();
|
let mut haccel = root_menu.borrow_mut();
|
||||||
AccelAction::add(&mut haccel, child_.internal_id(), accelerator)?;
|
AccelAction::add(&mut haccel, child_.internal_id(), accelerator)?;
|
||||||
}
|
}
|
||||||
|
@ -885,30 +961,21 @@ impl AccelAction {
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let accel = accelerator.to_accel(id as _)?;
|
let accel = accelerator.to_accel(id as _)?;
|
||||||
haccel_store.1.insert(id, Accel(accel));
|
haccel_store.1.insert(id, Accel(accel));
|
||||||
|
|
||||||
Self::update_store(haccel_store);
|
Self::update_store(haccel_store);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(haccel_store: &mut RefMut<AccelWrapper>, id: u32) {
|
fn remove(haccel_store: &mut RefMut<AccelWrapper>, id: u32) {
|
||||||
haccel_store.1.remove(&id);
|
haccel_store.1.remove(&id);
|
||||||
|
|
||||||
Self::update_store(haccel_store)
|
Self::update_store(haccel_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_store(haccel_store: &mut RefMut<AccelWrapper>) {
|
fn update_store(haccel_store: &mut RefMut<AccelWrapper>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
DestroyAcceleratorTable(haccel_store.0);
|
DestroyAcceleratorTable(haccel_store.0);
|
||||||
haccel_store.0 = CreateAcceleratorTableW(
|
let len = haccel_store.1.len();
|
||||||
haccel_store
|
let accels = haccel_store.1.values().map(|i| i.0).collect::<Vec<_>>();
|
||||||
.1
|
haccel_store.0 = CreateAcceleratorTableW(accels.as_ptr(), len as _);
|
||||||
.values()
|
|
||||||
.map(|i| i.0)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.as_ptr(),
|
|
||||||
haccel_store.1.len() as _,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -962,35 +1029,45 @@ unsafe extern "system" fn menu_subclass_proc(
|
||||||
let checked = !item.checked;
|
let checked = !item.checked;
|
||||||
item.set_checked(checked);
|
item.set_checked(checked);
|
||||||
}
|
}
|
||||||
MenuItemType::Predefined => match &item.predefined_item_type {
|
MenuItemType::Predefined => {
|
||||||
PredefinedMenuItemType::Copy => execute_edit_command(EditCommand::Copy),
|
if let Some(predefined_item_type) = &item.predefined_item_type {
|
||||||
PredefinedMenuItemType::Cut => execute_edit_command(EditCommand::Cut),
|
match predefined_item_type {
|
||||||
PredefinedMenuItemType::Paste => execute_edit_command(EditCommand::Paste),
|
PredefinedMenuItemType::Copy => {
|
||||||
PredefinedMenuItemType::SelectAll => {
|
execute_edit_command(EditCommand::Copy)
|
||||||
execute_edit_command(EditCommand::SelectAll)
|
}
|
||||||
}
|
PredefinedMenuItemType::Cut => {
|
||||||
PredefinedMenuItemType::Separator => {}
|
execute_edit_command(EditCommand::Cut)
|
||||||
PredefinedMenuItemType::Minimize => {
|
}
|
||||||
ShowWindow(hwnd, SW_MINIMIZE);
|
PredefinedMenuItemType::Paste => {
|
||||||
}
|
execute_edit_command(EditCommand::Paste)
|
||||||
PredefinedMenuItemType::Maximize => {
|
}
|
||||||
ShowWindow(hwnd, SW_MAXIMIZE);
|
PredefinedMenuItemType::SelectAll => {
|
||||||
}
|
execute_edit_command(EditCommand::SelectAll)
|
||||||
PredefinedMenuItemType::Hide => {
|
}
|
||||||
ShowWindow(hwnd, SW_HIDE);
|
PredefinedMenuItemType::Separator => {}
|
||||||
}
|
PredefinedMenuItemType::Minimize => {
|
||||||
PredefinedMenuItemType::CloseWindow => {
|
ShowWindow(hwnd, SW_MINIMIZE);
|
||||||
SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
}
|
||||||
}
|
PredefinedMenuItemType::Maximize => {
|
||||||
PredefinedMenuItemType::Quit => {
|
ShowWindow(hwnd, SW_MAXIMIZE);
|
||||||
PostQuitMessage(0);
|
}
|
||||||
}
|
PredefinedMenuItemType::Hide => {
|
||||||
PredefinedMenuItemType::About(Some(ref metadata)) => {
|
ShowWindow(hwnd, SW_HIDE);
|
||||||
show_about_dialog(hwnd, metadata)
|
}
|
||||||
}
|
PredefinedMenuItemType::CloseWindow => {
|
||||||
|
SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
||||||
|
}
|
||||||
|
PredefinedMenuItemType::Quit => {
|
||||||
|
PostQuitMessage(0);
|
||||||
|
}
|
||||||
|
PredefinedMenuItemType::About(Some(ref metadata)) => {
|
||||||
|
show_about_dialog(hwnd, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue