mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
7bb6e93a25
With -moz-appearance: dialog now always being transparent, setting up our own vibrant views for sheet windows is no longer necessary. We now let the regular sheet window background show through, and that background is already vibrant. Differential Revision: https://phabricator.services.mozilla.com/D51462 --HG-- extra : moz-landing-system : lando
180 lines
7.0 KiB
Plaintext
180 lines
7.0 KiB
Plaintext
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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/. */
|
|
|
|
#include "VibrancyManager.h"
|
|
|
|
#include "nsChildView.h"
|
|
#include "nsCocoaFeatures.h"
|
|
#import <objc/message.h>
|
|
|
|
using namespace mozilla;
|
|
|
|
bool VibrancyManager::UpdateVibrantRegion(VibrancyType aType,
|
|
const LayoutDeviceIntRegion& aRegion) {
|
|
if (aRegion.IsEmpty()) {
|
|
return mVibrantRegions.Remove(uint32_t(aType));
|
|
}
|
|
auto& vr = *mVibrantRegions.LookupOrAdd(uint32_t(aType));
|
|
return vr.UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() {
|
|
return this->CreateEffectView(aType);
|
|
});
|
|
}
|
|
|
|
LayoutDeviceIntRegion VibrancyManager::GetUnionOfVibrantRegions() const {
|
|
LayoutDeviceIntRegion result;
|
|
for (auto it = mVibrantRegions.ConstIter(); !it.Done(); it.Next()) {
|
|
result.OrWith(it.UserData()->Region());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static NSView* HitTestNil(id self, SEL _cmd, NSPoint aPoint) {
|
|
// This view must be transparent to mouse events.
|
|
return nil;
|
|
}
|
|
|
|
static BOOL AllowsVibrancyYes(id self, SEL _cmd) {
|
|
// Means that the foreground is blended using a vibrant blend mode.
|
|
return YES;
|
|
}
|
|
|
|
static Class CreateEffectViewClass(BOOL aForegroundVibrancy, BOOL aIsContainer) {
|
|
// Create a class that inherits from NSVisualEffectView and overrides the
|
|
// methods -[NSView hitTest:] and -[NSVisualEffectView allowsVibrancy].
|
|
Class NSVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView");
|
|
const char* className = aForegroundVibrancy ? "EffectViewWithForegroundVibrancy"
|
|
: "EffectViewWithoutForegroundVibrancy";
|
|
Class EffectViewClass = objc_allocateClassPair(NSVisualEffectViewClass, className, 0);
|
|
if (!aIsContainer) {
|
|
// Make this view transparent to mouse events.
|
|
class_addMethod(EffectViewClass, @selector(hitTest:), (IMP)HitTestNil, "@@:{CGPoint=dd}");
|
|
}
|
|
if (aForegroundVibrancy) {
|
|
// Override the -[NSView allowsVibrancy] method to return YES.
|
|
class_addMethod(EffectViewClass, @selector(allowsVibrancy), (IMP)AllowsVibrancyYes, "I@:");
|
|
}
|
|
return EffectViewClass;
|
|
}
|
|
|
|
static id AppearanceForVibrancyType(VibrancyType aType) {
|
|
Class NSAppearanceClass = NSClassFromString(@"NSAppearance");
|
|
switch (aType) {
|
|
case VibrancyType::LIGHT:
|
|
case VibrancyType::TOOLTIP:
|
|
case VibrancyType::MENU:
|
|
case VibrancyType::HIGHLIGHTED_MENUITEM:
|
|
case VibrancyType::SOURCE_LIST:
|
|
case VibrancyType::SOURCE_LIST_SELECTION:
|
|
case VibrancyType::ACTIVE_SOURCE_LIST_SELECTION:
|
|
return [NSAppearanceClass performSelector:@selector(appearanceNamed:)
|
|
withObject:@"NSAppearanceNameVibrantLight"];
|
|
case VibrancyType::DARK:
|
|
return [NSAppearanceClass performSelector:@selector(appearanceNamed:)
|
|
withObject:@"NSAppearanceNameVibrantDark"];
|
|
}
|
|
}
|
|
|
|
#if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
|
|
enum {
|
|
NSVisualEffectStateFollowsWindowActiveState,
|
|
NSVisualEffectStateActive,
|
|
NSVisualEffectStateInactive
|
|
};
|
|
|
|
enum { NSVisualEffectMaterialTitlebar = 3 };
|
|
#endif
|
|
|
|
#if !defined(MAC_OS_X_VERSION_10_11) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11
|
|
enum { NSVisualEffectMaterialMenu = 5, NSVisualEffectMaterialSidebar = 7 };
|
|
#endif
|
|
|
|
static NSUInteger VisualEffectStateForVibrancyType(VibrancyType aType) {
|
|
switch (aType) {
|
|
case VibrancyType::TOOLTIP:
|
|
case VibrancyType::MENU:
|
|
case VibrancyType::HIGHLIGHTED_MENUITEM:
|
|
// Tooltip and menu windows are never "key", so we need to tell the vibrancy effect to look
|
|
// active regardless of window state.
|
|
return NSVisualEffectStateActive;
|
|
default:
|
|
return NSVisualEffectStateFollowsWindowActiveState;
|
|
}
|
|
}
|
|
|
|
static BOOL HasVibrantForeground(VibrancyType aType) {
|
|
switch (aType) {
|
|
case VibrancyType::MENU:
|
|
return YES;
|
|
default:
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
#if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
|
|
enum { NSVisualEffectMaterialSelection = 4 };
|
|
#endif
|
|
|
|
@interface NSView (NSVisualEffectViewMethods)
|
|
- (void)setState:(NSUInteger)state;
|
|
- (void)setMaterial:(NSUInteger)material;
|
|
- (void)setEmphasized:(BOOL)emphasized;
|
|
@end
|
|
|
|
/* static */ NSView* VibrancyManager::CreateEffectView(VibrancyType aType, BOOL aIsContainer) {
|
|
static Class EffectViewWithoutForegroundVibrancy = CreateEffectViewClass(NO, NO);
|
|
static Class EffectViewWithForegroundVibrancy = CreateEffectViewClass(YES, NO);
|
|
static Class EffectViewContainer = CreateEffectViewClass(NO, YES);
|
|
|
|
// Pick the right NSVisualEffectView subclass for the desired vibrancy mode.
|
|
// For "container" views, never use foreground vibrancy, because returning
|
|
// YES from allowsVibrancy forces on foreground vibrancy for all descendant
|
|
// views which can have unintended effects.
|
|
Class EffectViewClass = aIsContainer
|
|
? EffectViewContainer
|
|
: (HasVibrantForeground(aType) ? EffectViewWithForegroundVibrancy
|
|
: EffectViewWithoutForegroundVibrancy);
|
|
|
|
NSView* effectView = [[EffectViewClass alloc] initWithFrame:NSZeroRect];
|
|
[effectView performSelector:@selector(setAppearance:)
|
|
withObject:AppearanceForVibrancyType(aType)];
|
|
[effectView setState:VisualEffectStateForVibrancyType(aType)];
|
|
|
|
BOOL canUseElCapitanMaterials = nsCocoaFeatures::OnElCapitanOrLater();
|
|
if (aType == VibrancyType::MENU) {
|
|
// Before 10.11 there is no material that perfectly matches the menu
|
|
// look. Of all available material types, NSVisualEffectMaterialTitlebar
|
|
// is the one that comes closest.
|
|
[effectView setMaterial:canUseElCapitanMaterials ? NSVisualEffectMaterialMenu
|
|
: NSVisualEffectMaterialTitlebar];
|
|
} else if (aType == VibrancyType::SOURCE_LIST && canUseElCapitanMaterials) {
|
|
[effectView setMaterial:NSVisualEffectMaterialSidebar];
|
|
} else if (aType == VibrancyType::HIGHLIGHTED_MENUITEM ||
|
|
aType == VibrancyType::SOURCE_LIST_SELECTION ||
|
|
aType == VibrancyType::ACTIVE_SOURCE_LIST_SELECTION) {
|
|
[effectView setMaterial:NSVisualEffectMaterialSelection];
|
|
if ([effectView respondsToSelector:@selector(setEmphasized:)] &&
|
|
aType != VibrancyType::SOURCE_LIST_SELECTION) {
|
|
[effectView setEmphasized:YES];
|
|
}
|
|
}
|
|
|
|
return effectView;
|
|
}
|
|
|
|
static bool ComputeSystemSupportsVibrancy() {
|
|
#ifdef __x86_64__
|
|
return NSClassFromString(@"NSAppearance") && NSClassFromString(@"NSVisualEffectView");
|
|
#else
|
|
// objc_allocateClassPair doesn't work in 32 bit mode, so turn off vibrancy.
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/* static */ bool VibrancyManager::SystemSupportsVibrancy() {
|
|
static bool supportsVibrancy = ComputeSystemSupportsVibrancy();
|
|
return supportsVibrancy;
|
|
}
|