diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /widget/cocoa/nsStandaloneNativeMenu.mm | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | widget/cocoa/nsStandaloneNativeMenu.mm | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/widget/cocoa/nsStandaloneNativeMenu.mm b/widget/cocoa/nsStandaloneNativeMenu.mm new file mode 100644 index 0000000000..79dcb1add0 --- /dev/null +++ b/widget/cocoa/nsStandaloneNativeMenu.mm @@ -0,0 +1,191 @@ +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#import <Cocoa/Cocoa.h> + +#include "nsStandaloneNativeMenu.h" +#include "nsMenuUtilsX.h" +#include "nsIMutationObserver.h" +#include "nsGkAtoms.h" +#include "nsObjCExceptions.h" +#include "mozilla/dom/Element.h" + +using mozilla::dom::Element; + +NS_IMPL_ISUPPORTS_INHERITED(nsStandaloneNativeMenu, nsMenuGroupOwnerX, nsIMutationObserver, + nsIStandaloneNativeMenu) + +nsStandaloneNativeMenu::nsStandaloneNativeMenu() : mMenu(nullptr), mContainerStatusBarItem(nil) {} + +nsStandaloneNativeMenu::~nsStandaloneNativeMenu() { + if (mMenu) delete mMenu; +} + +NS_IMETHODIMP +nsStandaloneNativeMenu::Init(Element* aElement) { + NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!"); + + NS_ENSURE_ARG(aElement); + + if (!aElement->IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menupopup)) return NS_ERROR_FAILURE; + + nsresult rv = nsMenuGroupOwnerX::Create(aElement); + if (NS_FAILED(rv)) return rv; + + mMenu = new nsMenuX(); + rv = mMenu->Create(this, this, aElement); + if (NS_FAILED(rv)) { + delete mMenu; + mMenu = nullptr; + return rv; + } + + mMenu->SetupIcon(); + + return NS_OK; +} + +static void UpdateMenu(nsMenuX* aMenu) { + aMenu->MenuOpened(); + aMenu->MenuClosed(); + + uint32_t itemCount = aMenu->GetItemCount(); + for (uint32_t i = 0; i < itemCount; i++) { + nsMenuObjectX* menuObject = aMenu->GetItemAt(i); + if (menuObject->MenuObjectType() == eSubmenuObjectType) { + UpdateMenu(static_cast<nsMenuX*>(menuObject)); + } + } +} + +NS_IMETHODIMP +nsStandaloneNativeMenu::MenuWillOpen(bool* aResult) { + NS_ASSERTION(mMenu != nullptr, "nsStandaloneNativeMenu::OnOpen - mMenu is null!"); + + // Force an update on the mMenu by faking an open/close on all of + // its submenus. + UpdateMenu(mMenu); + + *aResult = true; + return NS_OK; +} + +NS_IMETHODIMP +nsStandaloneNativeMenu::GetNativeMenu(void** aVoidPointer) { + if (mMenu) { + *aVoidPointer = mMenu->NativeData(); + [[(NSObject*)(*aVoidPointer) retain] autorelease]; + return NS_OK; + } else { + *aVoidPointer = nullptr; + return NS_ERROR_NOT_INITIALIZED; + } +} + +static NSMenuItem* NativeMenuItemWithLocation(NSMenu* currentSubmenu, NSString* locationString) { + NSArray* indexes = [locationString componentsSeparatedByString:@"|"]; + NSUInteger indexCount = [indexes count]; + if (indexCount == 0) return nil; + + for (NSUInteger i = 0; i < indexCount; i++) { + NSInteger targetIndex = [[indexes objectAtIndex:i] integerValue]; + NSInteger itemCount = [currentSubmenu numberOfItems]; + if (targetIndex < itemCount) { + NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex]; + + // If this is the last index, just return the menu item. + if (i == (indexCount - 1)) return menuItem; + + // If this is not the last index, find the submenu and keep going. + if ([menuItem hasSubmenu]) + currentSubmenu = [menuItem submenu]; + else + return nil; + } + } + + return nil; +} + +NS_IMETHODIMP +nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString) { + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + if (!mMenu) return NS_ERROR_NOT_INITIALIZED; + + NSString* locationString = + [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading()) + length:indexString.Length()]; + NSMenu* menu = static_cast<NSMenu*>(mMenu->NativeData()); + NSMenuItem* item = NativeMenuItemWithLocation(menu, locationString); + + // We can't perform an action on an item with a submenu, that will raise + // an obj-c exception. + if (item && ![item hasSubmenu]) { + NSMenu* parent = [item menu]; + if (parent) { + // NSLog(@"Performing action for native menu item titled: %@\n", + // [[currentSubmenu itemAtIndex:targetIndex] title]); + [parent performActionForItemAtIndex:[parent indexOfItem:item]]; + return NS_OK; + } + } + + return NS_ERROR_FAILURE; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +NS_IMETHODIMP +nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString) { + if (!mMenu) return NS_ERROR_NOT_INITIALIZED; + + NSString* locationString = + [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading()) + length:indexString.Length()]; + NSArray* indexes = [locationString componentsSeparatedByString:@"|"]; + unsigned int indexCount = [indexes count]; + if (indexCount == 0) return NS_OK; + + nsMenuX* currentMenu = mMenu; + + // now find the correct submenu + for (unsigned int i = 1; currentMenu && i < indexCount; i++) { + int targetIndex = [[indexes objectAtIndex:i] intValue]; + int visible = 0; + uint32_t length = currentMenu->GetItemCount(); + for (unsigned int j = 0; j < length; j++) { + nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j); + if (!targetMenu) return NS_OK; + if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) { + visible++; + if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) { + currentMenu = static_cast<nsMenuX*>(targetMenu); + // fake open/close to cause lazy update to happen + currentMenu->MenuOpened(); + currentMenu->MenuClosed(); + break; + } + } + } + } + + return NS_OK; +} + +void nsStandaloneNativeMenu::IconUpdated() { + if (mContainerStatusBarItem) { + NSImage* menuImage = [mMenu->NativeMenuItem() image]; + if (menuImage) { + [menuImage setTemplate:true]; + } + [mContainerStatusBarItem setImage:menuImage]; + } +} + +void nsStandaloneNativeMenu::SetContainerStatusBarItem(NSStatusItem* aItem) { + mContainerStatusBarItem = aItem; + IconUpdated(); +} |