in native/desktop-macos/src/macos/application_menu.rs [385:450]
fn reconcile_ns_menu_items(mtm: MainThreadMarker, menu: &NSMenu, is_top_level: bool, new_items: &[AppMenuItemSafe]) {
let items_array = menu.itemArray();
let menu_titles: Vec<_> = items_array.iter().map(|submenu| submenu.title()).collect();
// sometimes macOS can surround our items with new ones,
// but usually it just add items to the end
let old_item_ids: Vec<ItemIdentity> = items_array
.iter()
.zip(menu_titles.iter())
.enumerate()
.map(|(i, (item, title))| {
if is_top_level && i == 0 {
// the first item in a top level menu is always the item with the app name
ItemIdentity::AppNameSubMenu
} else {
item.representedObject()
.map(Retained::downcast::<MenuItemRepresenter>)
.map_or(ItemIdentity::MacOSProvided, |_rep_obj| {
if item.isSeparatorItem() {
ItemIdentity::Separator
} else if item.hasSubmenu() {
ItemIdentity::SubMenu { title }
} else {
ItemIdentity::Action { title }
}
})
}
})
.collect();
let new_item_ids: Vec<_> = new_items.iter().map(ItemIdentity::new).collect();
let first_item = old_item_ids.iter().position(|it| *it != ItemIdentity::MacOSProvided);
let last_item = old_item_ids.iter().rposition(|it| *it != ItemIdentity::MacOSProvided);
let (old_item_ids, base_position) = match (first_item, last_item) {
(Some(first_item), Some(last_item)) => (&old_item_ids[first_item..=last_item], first_item),
// All items in the menu are macOS provided
// Our items will be placed before them
_ => ([].as_slice(), 0),
};
let operations = edit_operations(old_item_ids, &new_item_ids);
let mut position_shift: isize = base_position as isize;
for op in operations {
match op {
Operation::Insert { position, item_idx } => {
let new_ns_menu_item = new_items[item_idx].create_ns_menu_item(mtm);
menu.insertItem_atIndex(&new_ns_menu_item, position as isize + position_shift);
position_shift += 1;
}
Operation::Reconcile { position, item_idx } => {
let ns_menu_item = menu.itemAtIndex(position as isize + position_shift).unwrap();
new_items[item_idx].reconcile_ns_menu_item(mtm, &ns_menu_item);
}
Operation::Remove { position } => {
let ns_menu_item = menu.itemAtIndex(position as isize + position_shift).unwrap();
let rep_obj = ns_menu_item.representedObject().map(Retained::downcast::<MenuItemRepresenter>);
// Just skip remove commands for macOS provided items
if rep_obj.is_some() {
menu.removeItemAtIndex(position as isize + position_shift);
position_shift -= 1;
}
}
}
}
}