fix: weakly reference MenuModel from MenuController (#23778)
This commit is contained in:
parent
f78504515b
commit
7b0a84477a
3 changed files with 73 additions and 20 deletions
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "base/callback.h"
|
#include "base/callback.h"
|
||||||
#include "base/mac/scoped_nsobject.h"
|
#include "base/mac/scoped_nsobject.h"
|
||||||
|
#include "base/memory/weak_ptr.h"
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
@ -24,15 +25,13 @@ class ElectronMenuModel;
|
||||||
// as it only maintains weak references.
|
// as it only maintains weak references.
|
||||||
@interface ElectronMenuController : NSObject <NSMenuDelegate> {
|
@interface ElectronMenuController : NSObject <NSMenuDelegate> {
|
||||||
@protected
|
@protected
|
||||||
electron::ElectronMenuModel* model_; // weak
|
base::WeakPtr<electron::ElectronMenuModel> model_;
|
||||||
base::scoped_nsobject<NSMenu> menu_;
|
base::scoped_nsobject<NSMenu> menu_;
|
||||||
BOOL isMenuOpen_;
|
BOOL isMenuOpen_;
|
||||||
BOOL useDefaultAccelerator_;
|
BOOL useDefaultAccelerator_;
|
||||||
base::OnceClosure closeCallback;
|
base::OnceClosure closeCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property(nonatomic, assign) electron::ElectronMenuModel* model;
|
|
||||||
|
|
||||||
// Builds a NSMenu from the pre-built model (must not be nil). Changes made
|
// Builds a NSMenu from the pre-built model (must not be nil). Changes made
|
||||||
// to the contents of the model after calling this will not be noticed.
|
// to the contents of the model after calling this will not be noticed.
|
||||||
- (id)initWithModel:(electron::ElectronMenuModel*)model
|
- (id)initWithModel:(electron::ElectronMenuModel*)model
|
||||||
|
@ -46,6 +45,9 @@ class ElectronMenuModel;
|
||||||
// Programmatically close the constructed menu.
|
// Programmatically close the constructed menu.
|
||||||
- (void)cancel;
|
- (void)cancel;
|
||||||
|
|
||||||
|
- (electron::ElectronMenuModel*)model;
|
||||||
|
- (void)setModel:(electron::ElectronMenuModel*)model;
|
||||||
|
|
||||||
// Access to the constructed menu if the complex initializer was used. If the
|
// Access to the constructed menu if the complex initializer was used. If the
|
||||||
// default initializer was used, then this will create the menu on first call.
|
// default initializer was used, then this will create the menu on first call.
|
||||||
- (NSMenu*)menu;
|
- (NSMenu*)menu;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "base/mac/foundation_util.h"
|
||||||
#include "base/strings/sys_string_conversions.h"
|
#include "base/strings/sys_string_conversions.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "base/task/post_task.h"
|
#include "base/task/post_task.h"
|
||||||
|
@ -87,6 +88,44 @@ NSMenu* MakeEmptySubmenu() {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// This class stores a base::WeakPtr<electron::ElectronMenuModel> as an
|
||||||
|
// Objective-C object, which allows it to be stored in the representedObject
|
||||||
|
// field of an NSMenuItem.
|
||||||
|
@interface WeakPtrToElectronMenuModelAsNSObject : NSObject
|
||||||
|
+ (instancetype)weakPtrForModel:(electron::ElectronMenuModel*)model;
|
||||||
|
+ (electron::ElectronMenuModel*)getFrom:(id)instance;
|
||||||
|
- (instancetype)initWithModel:(electron::ElectronMenuModel*)model;
|
||||||
|
- (electron::ElectronMenuModel*)menuModel;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation WeakPtrToElectronMenuModelAsNSObject {
|
||||||
|
base::WeakPtr<electron::ElectronMenuModel> _model;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (instancetype)weakPtrForModel:(electron::ElectronMenuModel*)model {
|
||||||
|
return [[[WeakPtrToElectronMenuModelAsNSObject alloc] initWithModel:model]
|
||||||
|
autorelease];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (electron::ElectronMenuModel*)getFrom:(id)instance {
|
||||||
|
return
|
||||||
|
[base::mac::ObjCCastStrict<WeakPtrToElectronMenuModelAsNSObject>(instance)
|
||||||
|
menuModel];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithModel:(electron::ElectronMenuModel*)model {
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_model = model->GetWeakPtr();
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (electron::ElectronMenuModel*)menuModel {
|
||||||
|
return _model.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
// Menu item is located for ease of removing it from the parent owner
|
// Menu item is located for ease of removing it from the parent owner
|
||||||
static base::scoped_nsobject<NSMenuItem> recentDocumentsMenuItem_;
|
static base::scoped_nsobject<NSMenuItem> recentDocumentsMenuItem_;
|
||||||
|
|
||||||
|
@ -95,12 +134,18 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
|
|
||||||
@implementation ElectronMenuController
|
@implementation ElectronMenuController
|
||||||
|
|
||||||
@synthesize model = model_;
|
- (electron::ElectronMenuModel*)model {
|
||||||
|
return model_.get();
|
||||||
|
}
|
||||||
|
|
||||||
- (id)initWithModel:(electron::ElectronMenuModel*)model
|
- (void)setModel:(electron::ElectronMenuModel*)model {
|
||||||
useDefaultAccelerator:(BOOL)use {
|
model_ = model->GetWeakPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithModel:(electron::ElectronMenuModel*)model
|
||||||
|
useDefaultAccelerator:(BOOL)use {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
model_ = model;
|
model_ = model->GetWeakPtr();
|
||||||
isMenuOpen_ = NO;
|
isMenuOpen_ = NO;
|
||||||
useDefaultAccelerator_ = use;
|
useDefaultAccelerator_ = use;
|
||||||
[self menu];
|
[self menu];
|
||||||
|
@ -115,8 +160,7 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
// while its context menu is still open.
|
// while its context menu is still open.
|
||||||
[self cancel];
|
[self cancel];
|
||||||
|
|
||||||
model_ = nil;
|
model_ = nullptr;
|
||||||
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +181,7 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
itemWithTitle:@"Electron"] submenu] itemWithTitle:openTitle] retain]);
|
itemWithTitle:@"Electron"] submenu] itemWithTitle:openTitle] retain]);
|
||||||
}
|
}
|
||||||
|
|
||||||
model_ = model;
|
model_ = model->GetWeakPtr();
|
||||||
[menu_ removeAllItems];
|
[menu_ removeAllItems];
|
||||||
|
|
||||||
const int count = model->GetItemCount();
|
const int count = model->GetItemCount();
|
||||||
|
@ -153,7 +197,8 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
if (isMenuOpen_) {
|
if (isMenuOpen_) {
|
||||||
[menu_ cancelTracking];
|
[menu_ cancelTracking];
|
||||||
isMenuOpen_ = NO;
|
isMenuOpen_ = NO;
|
||||||
model_->MenuWillClose();
|
if (model_)
|
||||||
|
model_->MenuWillClose();
|
||||||
if (!closeCallback.is_null()) {
|
if (!closeCallback.is_null()) {
|
||||||
base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(closeCallback));
|
base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(closeCallback));
|
||||||
}
|
}
|
||||||
|
@ -290,8 +335,8 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
// model. Setting the target to |self| allows this class to participate
|
// model. Setting the target to |self| allows this class to participate
|
||||||
// in validation of the menu items.
|
// in validation of the menu items.
|
||||||
[item setTag:index];
|
[item setTag:index];
|
||||||
NSValue* modelObject = [NSValue valueWithPointer:model];
|
[item setRepresentedObject:[WeakPtrToElectronMenuModelAsNSObject
|
||||||
[item setRepresentedObject:modelObject]; // Retains |modelObject|.
|
weakPtrForModel:model]];
|
||||||
ui::Accelerator accelerator;
|
ui::Accelerator accelerator;
|
||||||
if (model->GetAcceleratorAtWithParams(index, useDefaultAccelerator_,
|
if (model->GetAcceleratorAtWithParams(index, useDefaultAccelerator_,
|
||||||
&accelerator)) {
|
&accelerator)) {
|
||||||
|
@ -331,9 +376,8 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
NSInteger modelIndex = [item tag];
|
NSInteger modelIndex = [item tag];
|
||||||
electron::ElectronMenuModel* model =
|
electron::ElectronMenuModel* model = [WeakPtrToElectronMenuModelAsNSObject
|
||||||
static_cast<electron::ElectronMenuModel*>(
|
getFrom:[(id)item representedObject]];
|
||||||
[[(id)item representedObject] pointerValue]);
|
|
||||||
DCHECK(model);
|
DCHECK(model);
|
||||||
if (model) {
|
if (model) {
|
||||||
BOOL checked = model->IsItemCheckedAt(modelIndex);
|
BOOL checked = model->IsItemCheckedAt(modelIndex);
|
||||||
|
@ -352,8 +396,7 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
- (void)itemSelected:(id)sender {
|
- (void)itemSelected:(id)sender {
|
||||||
NSInteger modelIndex = [sender tag];
|
NSInteger modelIndex = [sender tag];
|
||||||
electron::ElectronMenuModel* model =
|
electron::ElectronMenuModel* model =
|
||||||
static_cast<electron::ElectronMenuModel*>(
|
[WeakPtrToElectronMenuModelAsNSObject getFrom:[sender representedObject]];
|
||||||
[[sender representedObject] pointerValue]);
|
|
||||||
DCHECK(model);
|
DCHECK(model);
|
||||||
if (model) {
|
if (model) {
|
||||||
NSEvent* event = [NSApp currentEvent];
|
NSEvent* event = [NSApp currentEvent];
|
||||||
|
@ -369,7 +412,7 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
menu_.reset([[NSMenu alloc] initWithTitle:@""]);
|
menu_.reset([[NSMenu alloc] initWithTitle:@""]);
|
||||||
[menu_ setDelegate:self];
|
[menu_ setDelegate:self];
|
||||||
if (model_)
|
if (model_)
|
||||||
[self populateWithModel:model_];
|
[self populateWithModel:model_.get()];
|
||||||
return menu_.get();
|
return menu_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +422,8 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||||
|
|
||||||
- (void)menuWillOpen:(NSMenu*)menu {
|
- (void)menuWillOpen:(NSMenu*)menu {
|
||||||
isMenuOpen_ = YES;
|
isMenuOpen_ = YES;
|
||||||
model_->MenuWillShow();
|
if (model_)
|
||||||
|
model_->MenuWillShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)menuDidClose:(NSMenu*)menu {
|
- (void)menuDidClose:(NSMenu*)menu {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "base/memory/weak_ptr.h"
|
||||||
#include "base/observer_list.h"
|
#include "base/observer_list.h"
|
||||||
#include "base/observer_list_types.h"
|
#include "base/observer_list_types.h"
|
||||||
#include "ui/base/models/simple_menu_model.h"
|
#include "ui/base/models/simple_menu_model.h"
|
||||||
|
@ -69,6 +70,10 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
|
||||||
void MenuWillClose() override;
|
void MenuWillClose() override;
|
||||||
void MenuWillShow() override;
|
void MenuWillShow() override;
|
||||||
|
|
||||||
|
base::WeakPtr<ElectronMenuModel> GetWeakPtr() {
|
||||||
|
return weak_factory_.GetWeakPtr();
|
||||||
|
}
|
||||||
|
|
||||||
using SimpleMenuModel::GetSubmenuModelAt;
|
using SimpleMenuModel::GetSubmenuModelAt;
|
||||||
ElectronMenuModel* GetSubmenuModelAt(int index);
|
ElectronMenuModel* GetSubmenuModelAt(int index);
|
||||||
|
|
||||||
|
@ -80,6 +85,8 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
|
||||||
std::map<int, base::string16> sublabels_; // command id -> sublabel
|
std::map<int, base::string16> sublabels_; // command id -> sublabel
|
||||||
base::ObserverList<Observer> observers_;
|
base::ObserverList<Observer> observers_;
|
||||||
|
|
||||||
|
base::WeakPtrFactory<ElectronMenuModel> weak_factory_{this};
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ElectronMenuModel);
|
DISALLOW_COPY_AND_ASSIGN(ElectronMenuModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue