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_METACLASS_$_NSTouchBar']
LDFLAGS += ['-Wl,-U,_OBJC_CLASS_$_NSCustomTouchBarItem']
LDFLAGS += ['-Wl,-U,_OBJC_CLASS_$_NSPopoverTouchBarItem']
LDFLAGS += ['-lresolv']
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
* 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.
*/
- (void)updateButton:(NSButton*)aButton input:(TouchBarInput*)aInput;
- (void)updateMainButton:(NSButton*)aMainButton input:(TouchBarInput*)aInput;
- (void)updatePopover:(NSPopoverTouchBarItem*)aPopoverItem input:(TouchBarInput*)aInput;
- (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;

View file

@ -26,6 +26,7 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
@"button",
@"mainButton",
@"scrubber",
@"popover",
];
// The system default width for Touch Bar inputs is 128px. This is double.
@ -106,6 +107,15 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
- (void)dealloc {
for (NSTouchBarItemIdentifier identifier in self.mappedLayoutItems) {
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];
}
@ -139,6 +149,17 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
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.
NSCustomTouchBarItem* newItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:aIdentifier];
@ -154,10 +175,14 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
return newItem;
}
- (void)updateItem:(TouchBarInput*)aInput {
- (bool)updateItem:(TouchBarInput*)aInput {
NSTouchBarItem* item = [self itemForIdentifier:[aInput nativeIdentifier]];
if (!item) {
return;
if ([self maybeUpdatePopoverChild:aInput]) {
[self replaceMappedLayoutItem:aInput];
return true;
}
return false;
}
if ([[aInput type] hasSuffix:@"button"]) {
@ -166,10 +191,34 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
} else if ([[aInput type] hasSuffix:@"mainButton"]) {
[(NSCustomTouchBarItem*)item setCustomizationLabel:[aInput title]];
[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.mappedLayoutItems[[aInput nativeIdentifier]] = aInput;
[self replaceMappedLayoutItem: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 {
@ -220,6 +269,25 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
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 {
TouchBarInput* input = self.mappedLayoutItems[aIdentifier];
// System-default share menu
@ -242,6 +310,22 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
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 {
NSTouchBarItemIdentifier identifier =
objc_getAssociatedObject(aSender, &sIdentifierAssociationKey);
@ -271,6 +355,12 @@ static const NSArray<NSString*>* kAllowedInputTypes = @[
if (!input) {
continue;
}
if ([[input type] hasSuffix:@"popover"]) {
NSTouchBarItem* item = [self itemForIdentifier:identifier];
[(nsTouchBar*)[(NSPopoverTouchBarItem*)item popoverTouchBar] releaseJSObjects];
}
[input setCallback:nil];
[input setDocument:nil];
[input setImageURI:nil];

View file

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

View file

@ -22,10 +22,14 @@ using namespace mozilla;
static const uint32_t kIconSize = 16;
static const CGFloat kHiDPIScalingFactor = 2.0f;
nsTouchBarInputIcon::nsTouchBarInputIcon(RefPtr<Document> aDocument,
NSButton* aButton,
NSSharingServicePickerTouchBarItem* aShareScrubber)
: mDocument(aDocument), mSetIcon(false), mButton(aButton), mShareScrubber(aShareScrubber) {
nsTouchBarInputIcon::nsTouchBarInputIcon(RefPtr<Document> aDocument, NSButton* aButton,
NSSharingServicePickerTouchBarItem* aShareScrubber,
NSPopoverTouchBarItem* aPopoverItem)
: mDocument(aDocument),
mSetIcon(false),
mButton(aButton),
mShareScrubber(aShareScrubber),
mPopoverItem(aPopoverItem) {
MOZ_COUNT_CTOR(nsTouchBarInputIcon);
}
@ -45,13 +49,14 @@ void nsTouchBarInputIcon::Destroy() {
mButton = nil;
mShareScrubber = nil;
mPopoverItem = nil;
}
nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
// 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");
return NS_ERROR_FAILURE;
}
@ -70,6 +75,7 @@ nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
// Load placeholder icon.
[mButton setImage:mIconLoader->GetNativeIconImage()];
[mShareScrubber setButtonImage:mIconLoader->GetNativeIconImage()];
[mPopoverItem setCollapsedRepresentationImage:mIconLoader->GetNativeIconImage()];
}
nsresult rv = mIconLoader->LoadIcon(aIconURI);
@ -79,6 +85,7 @@ nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
// been set. Clear it.
[mButton setImage:nil];
[mShareScrubber setButtonImage:nil];
[mPopoverItem setCollapsedRepresentationImage:nil];
}
mSetIcon = true;
@ -95,6 +102,7 @@ nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
nsresult nsTouchBarInputIcon::OnComplete(NSImage* aImage) {
[mButton setImage:aImage];
[mShareScrubber setButtonImage:aImage];
[mPopoverItem setCollapsedRepresentationImage:aImage];
[aImage release];
return NS_OK;

View file

@ -58,6 +58,17 @@ __attribute__((weak_import)) @interface NSTouchBar : NSObject
- (NSTouchBarItem*)itemForIdentifier:(NSTouchBarItemIdentifier)aIdentifier;
@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)
@property(strong) NSColor* bezelColor;
@end

View file

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

View file

@ -9,7 +9,6 @@
#include "nsTouchBarUpdater.h"
#include "nsTouchBarNativeAPIDefines.h"
#include "nsCocoaWindow.h"
#include "nsIArray.h"
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
@ -28,12 +27,11 @@ NS_IMPL_ISUPPORTS(nsTouchBarUpdater, nsITouchBarUpdater);
NS_IMETHODIMP
nsTouchBarUpdater::UpdateTouchBarInputs(nsIBaseWindow* aWindow,
const nsTArray<RefPtr<nsITouchBarInput>>& aInputs) {
nsCOMPtr<nsIWidget> widget = nullptr;
aWindow->GetMainWidget(getter_AddRefs(widget));
if (!widget) {
return NS_ERROR_FAILURE;
if (!sTouchBarIsInitialized) {
return NS_OK;
}
BaseWindow* cocoaWin = (BaseWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
BaseWindow* cocoaWin = nsTouchBarUpdater::GetCocoaWindow(aWindow);
if (!cocoaWin) {
return NS_ERROR_FAILURE;
}
@ -54,6 +52,42 @@ nsTouchBarUpdater::UpdateTouchBarInputs(nsIBaseWindow* aWindow,
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
nsTouchBarUpdater::EnterCustomizeMode() {
[NSApp toggleTouchBarCustomizationPalette:(id)this];
@ -66,6 +100,19 @@ nsTouchBarUpdater::IsTouchBarInitialized(bool* aResult) {
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.
NS_IMETHODIMP
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.
*/
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);
};