Bug 1563349 - Part 4 - Support NSPopoverTouchBarItem. r=spohl

Differential Revision: https://phabricator.services.mozilla.com/D47621

--HG--
extra : moz-landing-system : lando
This commit is contained in:
harry 2019-10-09 17:55:15 +00:00
parent dc2a166e28
commit 525bd39f48
9 changed files with 205 additions and 20 deletions

View file

@ -74,6 +74,7 @@ def Libxul(name, output_category=None):
LDFLAGS += ['-Wl,-U,_OBJC_CLASS_$_NSSharingServicePickerTouchBarItem'] LDFLAGS += ['-Wl,-U,_OBJC_CLASS_$_NSSharingServicePickerTouchBarItem']
LDFLAGS += ['-Wl,-U,_OBJC_METACLASS_$_NSTouchBar'] LDFLAGS += ['-Wl,-U,_OBJC_METACLASS_$_NSTouchBar']
LDFLAGS += ['-Wl,-U,_OBJC_CLASS_$_NSCustomTouchBarItem'] LDFLAGS += ['-Wl,-U,_OBJC_CLASS_$_NSCustomTouchBarItem']
LDFLAGS += ['-Wl,-U,_OBJC_CLASS_$_NSPopoverTouchBarItem']
LDFLAGS += ['-lresolv'] LDFLAGS += ['-lresolv']
if CONFIG['MOZ_DEBUG_SYMBOLS'] and CONFIG['CC_TYPE'] == 'clang-cl': if CONFIG['MOZ_DEBUG_SYMBOLS'] and CONFIG['CC_TYPE'] == 'clang-cl':

View file

@ -130,18 +130,37 @@ using namespace mozilla::dom;
/** /**
* Updates an input on the Touch Bar by redirecting to one of the specific * Updates an input on the Touch Bar by redirecting to one of the specific
* TouchBarItem types updaters. * TouchBarItem types updaters.
* Returns true if the input was successfully updated.
*/ */
- (void)updateItem:(TouchBarInput*)aInput; - (bool)updateItem:(TouchBarInput*)aInput;
/**
* Helper function for updateItem. Checks to see if a given input exists within
* any of this Touch Bar's popovers and updates it if it exists.
*/
- (bool)maybeUpdatePopoverChild:(TouchBarInput*)aInput;
/**
* Helper function for updateItem. Replaces an item in the
* self.mappedLayoutItems dictionary.
*/
- (void)replaceMappedLayoutItem:(TouchBarInput*)aItem;
/** /**
* Update or create various subclasses of TouchBarItem. * Update or create various subclasses of TouchBarItem.
*/ */
- (void)updateButton:(NSButton*)aButton input:(TouchBarInput*)aInput; - (void)updateButton:(NSButton*)aButton input:(TouchBarInput*)aInput;
- (void)updateMainButton:(NSButton*)aMainButton input:(TouchBarInput*)aInput; - (void)updateMainButton:(NSButton*)aMainButton input:(TouchBarInput*)aInput;
- (void)updatePopover:(NSPopoverTouchBarItem*)aPopoverItem input:(TouchBarInput*)aInput;
- (NSTouchBarItem*)makeShareScrubberForIdentifier:(NSTouchBarItemIdentifier)aIdentifier; - (NSTouchBarItem*)makeShareScrubberForIdentifier:(NSTouchBarItemIdentifier)aIdentifier;
/** /**
* Redirects button actions to the appropriate handler and handles telemetry. * If aShowing is true, aPopover is shown. Otherwise, it is hidden.
*/
- (void)showPopover:(TouchBarInput*)aPopover showing:(bool)aShowing;
/**
* Redirects button actions to the appropriate handler.
*/ */
- (void)touchBarAction:(id)aSender; - (void)touchBarAction:(id)aSender;

View file

@ -26,6 +26,7 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
@"button", @"button",
@"mainButton", @"mainButton",
@"scrubber", @"scrubber",
@"popover",
]; ];
// The system default width for Touch Bar inputs is 128px. This is double. // The system default width for Touch Bar inputs is 128px. This is double.
@ -106,6 +107,15 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
- (void)dealloc { - (void)dealloc {
for (NSTouchBarItemIdentifier identifier in self.mappedLayoutItems) { for (NSTouchBarItemIdentifier identifier in self.mappedLayoutItems) {
NSTouchBarItem* item = [self itemForIdentifier:identifier]; NSTouchBarItem* item = [self itemForIdentifier:identifier];
if (!item) {
continue;
}
if ([item isKindOfClass:[NSPopoverTouchBarItem class]]) {
[(NSPopoverTouchBarItem*)item setCollapsedRepresentation:nil];
[(NSPopoverTouchBarItem*)item setCollapsedRepresentationImage:nil];
[(nsTouchBar*)[(NSPopoverTouchBarItem*)item popoverTouchBar] release];
}
[item release]; [item release];
} }
@ -139,6 +149,17 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
return [self makeShareScrubberForIdentifier:aIdentifier]; return [self makeShareScrubberForIdentifier:aIdentifier];
} }
if ([[input type] hasSuffix:@"popover"]) {
NSPopoverTouchBarItem* newPopoverItem =
[[NSPopoverTouchBarItem alloc] initWithIdentifier:aIdentifier];
// We initialize popoverTouchBar here because we only allow setting this
// property on popover creation. Updating popoverTouchBar for every update
// of the popover item would be very expensive.
newPopoverItem.popoverTouchBar = [[nsTouchBar alloc] initWithInputs:[input children]];
[self updatePopover:newPopoverItem input:input];
return newPopoverItem;
}
// Our new item, which will be initialized depending on aIdentifier. // Our new item, which will be initialized depending on aIdentifier.
NSCustomTouchBarItem* newItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:aIdentifier]; NSCustomTouchBarItem* newItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:aIdentifier];
@ -154,10 +175,14 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
return newItem; return newItem;
} }
- (void)updateItem:(TouchBarInput*)aInput { - (bool)updateItem:(TouchBarInput*)aInput {
NSTouchBarItem* item = [self itemForIdentifier:[aInput nativeIdentifier]]; NSTouchBarItem* item = [self itemForIdentifier:[aInput nativeIdentifier]];
if (!item) { if (!item) {
return; if ([self maybeUpdatePopoverChild:aInput]) {
[self replaceMappedLayoutItem:aInput];
return true;
}
return false;
} }
if ([[aInput type] hasSuffix:@"button"]) { if ([[aInput type] hasSuffix:@"button"]) {
@ -166,10 +191,34 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
} else if ([[aInput type] hasSuffix:@"mainButton"]) { } else if ([[aInput type] hasSuffix:@"mainButton"]) {
[(NSCustomTouchBarItem*)item setCustomizationLabel:[aInput title]]; [(NSCustomTouchBarItem*)item setCustomizationLabel:[aInput title]];
[self updateMainButton:(NSButton*)item.view input:aInput]; [self updateMainButton:(NSButton*)item.view input:aInput];
} else if ([[aInput type] hasSuffix:@"popover"]) {
[(NSPopoverTouchBarItem*)item setCustomizationLabel:[aInput title]];
[self updatePopover:(NSPopoverTouchBarItem*)item input:aInput];
} }
[self.mappedLayoutItems[[aInput nativeIdentifier]] release]; [self replaceMappedLayoutItem:aInput];
self.mappedLayoutItems[[aInput nativeIdentifier]] = aInput; return true;
}
- (bool)maybeUpdatePopoverChild:(TouchBarInput*)aInput {
for (NSTouchBarItemIdentifier identifier in self.mappedLayoutItems) {
TouchBarInput* potentialPopover = self.mappedLayoutItems[identifier];
if (![[potentialPopover type] hasSuffix:@"popover"]) {
continue;
}
NSTouchBarItem* popover = [self itemForIdentifier:[potentialPopover nativeIdentifier]];
if (popover) {
if ([(nsTouchBar*)[(NSPopoverTouchBarItem*)popover popoverTouchBar] updateItem:aInput]) {
return true;
}
}
}
return false;
}
- (void)replaceMappedLayoutItem:(TouchBarInput*)aItem {
[self.mappedLayoutItems[[aItem nativeIdentifier]] release];
self.mappedLayoutItems[[aItem nativeIdentifier]] = aItem;
} }
- (void)updateButton:(NSButton*)aButton input:(TouchBarInput*)aInput { - (void)updateButton:(NSButton*)aButton input:(TouchBarInput*)aInput {
@ -220,6 +269,25 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
forOrientation:NSLayoutConstraintOrientationHorizontal]; forOrientation:NSLayoutConstraintOrientationHorizontal];
} }
- (void)updatePopover:(NSPopoverTouchBarItem*)aPopoverItem input:(TouchBarInput*)aInput {
if (!aPopoverItem || !aInput) {
return;
}
aPopoverItem.showsCloseButton = YES;
if ([aInput imageURI]) {
RefPtr<nsTouchBarInputIcon> icon = [aInput icon];
if (!icon) {
icon = new nsTouchBarInputIcon([aInput document], nil, nil, aPopoverItem);
[aInput setIcon:icon];
}
icon->SetupIcon([aInput imageURI]);
} else if ([aInput title]) {
aPopoverItem.collapsedRepresentationLabel = [aInput title];
} else {
aPopoverItem.collapsedRepresentation = nil;
}
}
- (NSTouchBarItem*)makeShareScrubberForIdentifier:(NSTouchBarItemIdentifier)aIdentifier { - (NSTouchBarItem*)makeShareScrubberForIdentifier:(NSTouchBarItemIdentifier)aIdentifier {
TouchBarInput* input = self.mappedLayoutItems[aIdentifier]; TouchBarInput* input = self.mappedLayoutItems[aIdentifier];
// System-default share menu // System-default share menu
@ -242,6 +310,22 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
return servicesItem; return servicesItem;
} }
- (void)showPopover:(TouchBarInput*)aPopover showing:(bool)aShowing {
if (!aPopover) {
return;
}
NSPopoverTouchBarItem* popoverItem =
(NSPopoverTouchBarItem*)[self itemForIdentifier:[aPopover nativeIdentifier]];
if (!popoverItem) {
return;
}
if (aShowing) {
[popoverItem showPopover:self];
} else {
[popoverItem dismissPopover:self];
}
}
- (void)touchBarAction:(id)aSender { - (void)touchBarAction:(id)aSender {
NSTouchBarItemIdentifier identifier = NSTouchBarItemIdentifier identifier =
objc_getAssociatedObject(aSender, &sIdentifierAssociationKey); objc_getAssociatedObject(aSender, &sIdentifierAssociationKey);
@ -271,6 +355,12 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
if (!input) { if (!input) {
continue; continue;
} }
if ([[input type] hasSuffix:@"popover"]) {
NSTouchBarItem* item = [self itemForIdentifier:identifier];
[(nsTouchBar*)[(NSPopoverTouchBarItem*)item popoverTouchBar] releaseJSObjects];
}
[input setCallback:nil]; [input setCallback:nil];
[input setDocument:nil]; [input setDocument:nil];
[input setImageURI:nil]; [input setImageURI:nil];

View file

@ -26,9 +26,9 @@ class imgRequestProxy;
class nsTouchBarInputIcon : public nsIconLoaderObserver { class nsTouchBarInputIcon : public nsIconLoaderObserver {
public: public:
explicit nsTouchBarInputIcon( explicit nsTouchBarInputIcon(
RefPtr<Document> aDocument, RefPtr<Document> aDocument, NSButton* aButton,
NSButton* aButton, NSSharingServicePickerTouchBarItem* aShareScrubber = nil,
NSSharingServicePickerTouchBarItem* aShareScrubber = nil); NSPopoverTouchBarItem* aPopoverItem = nil);
private: private:
virtual ~nsTouchBarInputIcon(); virtual ~nsTouchBarInputIcon();
@ -59,6 +59,8 @@ class nsTouchBarInputIcon : public nsIconLoaderObserver {
// NSSharingServicePickerTouchBarItem does not expose an NSButton* on which we // NSSharingServicePickerTouchBarItem does not expose an NSButton* on which we
// can set the `image` property. // can set the `image` property.
NSSharingServicePickerTouchBarItem* mShareScrubber; NSSharingServicePickerTouchBarItem* mShareScrubber;
// We accept a popover only as a special case.
NSPopoverTouchBarItem* mPopoverItem;
// The icon loader object should never outlive its creating // The icon loader object should never outlive its creating
// nsTouchBarInputIcon object. // nsTouchBarInputIcon object.
RefPtr<nsIconLoaderService> mIconLoader; RefPtr<nsIconLoaderService> mIconLoader;

View file

@ -22,10 +22,14 @@ using namespace mozilla;
static const uint32_t kIconSize = 16; static const uint32_t kIconSize = 16;
static const CGFloat kHiDPIScalingFactor = 2.0f; static const CGFloat kHiDPIScalingFactor = 2.0f;
nsTouchBarInputIcon::nsTouchBarInputIcon(RefPtr<Document> aDocument, nsTouchBarInputIcon::nsTouchBarInputIcon(RefPtr<Document> aDocument, NSButton* aButton,
NSButton* aButton, NSSharingServicePickerTouchBarItem* aShareScrubber,
NSSharingServicePickerTouchBarItem* aShareScrubber) NSPopoverTouchBarItem* aPopoverItem)
: mDocument(aDocument), mSetIcon(false), mButton(aButton), mShareScrubber(aShareScrubber) { : mDocument(aDocument),
mSetIcon(false),
mButton(aButton),
mShareScrubber(aShareScrubber),
mPopoverItem(aPopoverItem) {
MOZ_COUNT_CTOR(nsTouchBarInputIcon); MOZ_COUNT_CTOR(nsTouchBarInputIcon);
} }
@ -45,13 +49,14 @@ void nsTouchBarInputIcon::Destroy() {
mButton = nil; mButton = nil;
mShareScrubber = nil; mShareScrubber = nil;
mPopoverItem = nil;
} }
nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) { nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
// Still don't have one, then something is wrong, get out of here. // Still don't have one, then something is wrong, get out of here.
if (!(mButton || mShareScrubber)) { if (!(mButton || mShareScrubber || mPopoverItem)) {
NS_ERROR("No Touch Bar button"); NS_ERROR("No Touch Bar button");
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -70,6 +75,7 @@ nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
// Load placeholder icon. // Load placeholder icon.
[mButton setImage:mIconLoader->GetNativeIconImage()]; [mButton setImage:mIconLoader->GetNativeIconImage()];
[mShareScrubber setButtonImage:mIconLoader->GetNativeIconImage()]; [mShareScrubber setButtonImage:mIconLoader->GetNativeIconImage()];
[mPopoverItem setCollapsedRepresentationImage:mIconLoader->GetNativeIconImage()];
} }
nsresult rv = mIconLoader->LoadIcon(aIconURI); nsresult rv = mIconLoader->LoadIcon(aIconURI);
@ -79,6 +85,7 @@ nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
// been set. Clear it. // been set. Clear it.
[mButton setImage:nil]; [mButton setImage:nil];
[mShareScrubber setButtonImage:nil]; [mShareScrubber setButtonImage:nil];
[mPopoverItem setCollapsedRepresentationImage:nil];
} }
mSetIcon = true; mSetIcon = true;
@ -95,6 +102,7 @@ nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
nsresult nsTouchBarInputIcon::OnComplete(NSImage* aImage) { nsresult nsTouchBarInputIcon::OnComplete(NSImage* aImage) {
[mButton setImage:aImage]; [mButton setImage:aImage];
[mShareScrubber setButtonImage:aImage]; [mShareScrubber setButtonImage:aImage];
[mPopoverItem setCollapsedRepresentationImage:aImage];
[aImage release]; [aImage release];
return NS_OK; return NS_OK;

View file

@ -58,6 +58,17 @@ __attribute__((weak_import)) @interface NSTouchBar : NSObject
- (NSTouchBarItem*)itemForIdentifier:(NSTouchBarItemIdentifier)aIdentifier; - (NSTouchBarItem*)itemForIdentifier:(NSTouchBarItemIdentifier)aIdentifier;
@end @end
__attribute__((weak_import)) @interface NSPopoverTouchBarItem : NSTouchBarItem
@property(strong) NSString* customizationLabel;
@property(strong) NSView* collapsedRepresentation;
@property(strong) NSImage* collapsedRepresentationImage;
@property(strong) NSString* collapsedRepresentationLabel;
@property(strong) NSTouchBar* popoverTouchBar;
@property BOOL showsCloseButton;
- (void)showPopover:(id)sender;
- (void)dismissPopover:(id)sender;
@end
@interface NSButton (TouchBarButton) @interface NSButton (TouchBarButton)
@property(strong) NSColor* bezelColor; @property(strong) NSColor* bezelColor;
@end @end

View file

@ -6,6 +6,7 @@
#define nsTouchBarUpdater_h_ #define nsTouchBarUpdater_h_
#include "nsITouchBarUpdater.h" #include "nsITouchBarUpdater.h"
#include "nsCocoaWindow.h"
class nsTouchBarUpdater : public nsITouchBarUpdater { class nsTouchBarUpdater : public nsITouchBarUpdater {
public: public:
@ -16,6 +17,7 @@ class nsTouchBarUpdater : public nsITouchBarUpdater {
protected: protected:
virtual ~nsTouchBarUpdater() {} virtual ~nsTouchBarUpdater() {}
BaseWindow* GetCocoaWindow(nsIBaseWindow* aWindow);
}; };
#endif // nsTouchBarUpdater_h_ #endif // nsTouchBarUpdater_h_

View file

@ -9,7 +9,6 @@
#include "nsTouchBarUpdater.h" #include "nsTouchBarUpdater.h"
#include "nsTouchBarNativeAPIDefines.h" #include "nsTouchBarNativeAPIDefines.h"
#include "nsCocoaWindow.h"
#include "nsIArray.h" #include "nsIArray.h"
#include "nsIBaseWindow.h" #include "nsIBaseWindow.h"
#include "nsIWidget.h" #include "nsIWidget.h"
@ -28,12 +27,11 @@ NS_IMPL_ISUPPORTS(nsTouchBarUpdater, nsITouchBarUpdater);
NS_IMETHODIMP NS_IMETHODIMP
nsTouchBarUpdater::UpdateTouchBarInputs(nsIBaseWindow* aWindow, nsTouchBarUpdater::UpdateTouchBarInputs(nsIBaseWindow* aWindow,
const nsTArray<RefPtr<nsITouchBarInput>>& aInputs) { const nsTArray<RefPtr<nsITouchBarInput>>& aInputs) {
nsCOMPtr<nsIWidget> widget = nullptr; if (!sTouchBarIsInitialized) {
aWindow->GetMainWidget(getter_AddRefs(widget)); return NS_OK;
if (!widget) {
return NS_ERROR_FAILURE;
} }
BaseWindow* cocoaWin = (BaseWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
BaseWindow* cocoaWin = nsTouchBarUpdater::GetCocoaWindow(aWindow);
if (!cocoaWin) { if (!cocoaWin) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -54,6 +52,42 @@ nsTouchBarUpdater::UpdateTouchBarInputs(nsIBaseWindow* aWindow,
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsTouchBarUpdater::ShowPopover(nsIBaseWindow* aWindow, nsITouchBarInput* aPopover, bool aShowing) {
if (!sTouchBarIsInitialized || !aPopover) {
return NS_OK;
}
BaseWindow* cocoaWin = nsTouchBarUpdater::GetCocoaWindow(aWindow);
if (!cocoaWin) {
return NS_ERROR_FAILURE;
}
if ([cocoaWin respondsToSelector:@selector(touchBar)]) {
// We don't need to completely reinitialize the popover. We only need its
// identifier to look it up in [nsTouchBar mappedLayoutItems].
nsAutoString keyStr;
nsresult rv = aPopover->GetKey(keyStr);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
NSString* key = nsCocoaUtils::ToNSString(keyStr);
nsAutoString typeStr;
rv = aPopover->GetType(typeStr);
if (NS_FAILED(rv)) {
return NS_ERROR_FAILURE;
}
NSString* type = nsCocoaUtils::ToNSString(typeStr);
TouchBarInput* popoverItem = [[(nsTouchBar*)cocoaWin.touchBar mappedLayoutItems]
objectForKey:[TouchBarInput nativeIdentifierWithType:type withKey:key]];
[(nsTouchBar*)cocoaWin.touchBar showPopover:popoverItem showing:aShowing];
}
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsTouchBarUpdater::EnterCustomizeMode() { nsTouchBarUpdater::EnterCustomizeMode() {
[NSApp toggleTouchBarCustomizationPalette:(id)this]; [NSApp toggleTouchBarCustomizationPalette:(id)this];
@ -66,6 +100,19 @@ nsTouchBarUpdater::IsTouchBarInitialized(bool* aResult) {
return NS_OK; return NS_OK;
} }
BaseWindow* nsTouchBarUpdater::GetCocoaWindow(nsIBaseWindow* aWindow) {
nsCOMPtr<nsIWidget> widget = nullptr;
aWindow->GetMainWidget(getter_AddRefs(widget));
if (!widget) {
return nil;
}
BaseWindow* cocoaWin = (BaseWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
if (!cocoaWin) {
return nil;
}
return cocoaWin;
}
// NOTE: This method is for internal unit tests only. // NOTE: This method is for internal unit tests only.
NS_IMETHODIMP NS_IMETHODIMP
nsTouchBarUpdater::SetTouchBarInitialized(bool aIsInitialized) { nsTouchBarUpdater::SetTouchBarInitialized(bool aIsInitialized) {

View file

@ -34,4 +34,9 @@ interface nsITouchBarUpdater : nsISupports
* sets this value after a Touch Bar is initialized on compatible Macs. * sets this value after a Touch Bar is initialized on compatible Macs.
*/ */
void setTouchBarInitialized(in boolean aIsInitialized); void setTouchBarInitialized(in boolean aIsInitialized);
/**
* If aShowing is true, aPopover is shown. Otherwise, it is hidden.
*/
void showPopover(in nsIBaseWindow aWindow, in nsITouchBarInput aPopover, in boolean aShowing);
}; };