forked from mirrors/gecko-dev
Bug 1891354 - Clean up VibrancyManager a bit. r=sam,mac-reviewers,bradwerth
Make the tooltip menupopup view not go through VibrancyManager at all (don't really need to). Use an EnumeratedArray for storage rather than a hashmap. Differential Revision: https://phabricator.services.mozilla.com/D207390
This commit is contained in:
parent
9d3a3d6b34
commit
cd7d835e04
5 changed files with 99 additions and 127 deletions
|
|
@ -7,24 +7,24 @@
|
||||||
#ifndef VibrancyManager_h
|
#ifndef VibrancyManager_h
|
||||||
#define VibrancyManager_h
|
#define VibrancyManager_h
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/EnumeratedArray.h"
|
||||||
#include "nsClassHashtable.h"
|
#include "Units.h"
|
||||||
#include "nsRegion.h"
|
|
||||||
#include "nsTArray.h"
|
|
||||||
#include "ViewRegion.h"
|
|
||||||
|
|
||||||
#import <Foundation/NSGeometry.h>
|
|
||||||
|
|
||||||
@class NSColor;
|
|
||||||
@class NSView;
|
@class NSView;
|
||||||
class nsChildView;
|
class nsChildView;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
class ViewRegion;
|
||||||
|
|
||||||
enum class VibrancyType {
|
enum class VibrancyType {
|
||||||
TOOLTIP,
|
// Add new values here, or update MaxEnumValue below if you add them after.
|
||||||
MENU,
|
Titlebar,
|
||||||
TITLEBAR,
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct MaxContiguousEnumValue<VibrancyType> {
|
||||||
|
static constexpr auto value = VibrancyType::Titlebar;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,9 +51,9 @@ class VibrancyManager {
|
||||||
* NSVisualEffectViews which will be created for vibrant regions.
|
* NSVisualEffectViews which will be created for vibrant regions.
|
||||||
*/
|
*/
|
||||||
VibrancyManager(const nsChildView& aCoordinateConverter,
|
VibrancyManager(const nsChildView& aCoordinateConverter,
|
||||||
NSView* aContainerView)
|
NSView* aContainerView);
|
||||||
: mCoordinateConverter(aCoordinateConverter),
|
|
||||||
mContainerView(aContainerView) {}
|
~VibrancyManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the placement of the NSVisualEffectViews inside the container
|
* Update the placement of the NSVisualEffectViews inside the container
|
||||||
|
|
@ -66,26 +66,10 @@ class VibrancyManager {
|
||||||
bool UpdateVibrantRegion(VibrancyType aType,
|
bool UpdateVibrantRegion(VibrancyType aType,
|
||||||
const LayoutDeviceIntRegion& aRegion);
|
const LayoutDeviceIntRegion& aRegion);
|
||||||
|
|
||||||
bool HasVibrantRegions() { return !mVibrantRegions.IsEmpty(); }
|
|
||||||
|
|
||||||
LayoutDeviceIntRegion GetUnionOfVibrantRegions() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an NSVisualEffectView for the specified vibrancy type. The return
|
|
||||||
* value is not autoreleased. We return an object of type NSView* because we
|
|
||||||
* compile with an SDK that does not contain a definition for
|
|
||||||
* NSVisualEffectView.
|
|
||||||
* @param aIsContainer Whether this NSView will have child views. This value
|
|
||||||
* affects hit testing: Container views will pass through
|
|
||||||
* hit testing requests to their children, and leaf views
|
|
||||||
* will be transparent to hit testing.
|
|
||||||
*/
|
|
||||||
static NSView* CreateEffectView(VibrancyType aType, BOOL aIsContainer = NO);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const nsChildView& mCoordinateConverter;
|
const nsChildView& mCoordinateConverter;
|
||||||
NSView* mContainerView;
|
NSView* mContainerView;
|
||||||
nsClassHashtable<nsUint32HashKey, ViewRegion> mVibrantRegions;
|
EnumeratedArray<VibrancyType, UniquePtr<ViewRegion>> mVibrantRegions;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "VibrancyManager.h"
|
#include "VibrancyManager.h"
|
||||||
|
#include "ViewRegion.h"
|
||||||
|
#include "nsRegion.h"
|
||||||
|
#include "ViewRegion.h"
|
||||||
|
|
||||||
#import <objc/message.h>
|
#import <objc/message.h>
|
||||||
|
|
||||||
|
|
@ -20,18 +23,10 @@ using namespace mozilla;
|
||||||
vibrancyType:(VibrancyType)aVibrancyType;
|
vibrancyType:(VibrancyType)aVibrancyType;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface MOZVibrantLeafView : MOZVibrantView
|
|
||||||
@end
|
|
||||||
|
|
||||||
static NSVisualEffectState VisualEffectStateForVibrancyType(
|
static NSVisualEffectState VisualEffectStateForVibrancyType(
|
||||||
VibrancyType aType) {
|
VibrancyType aType) {
|
||||||
switch (aType) {
|
switch (aType) {
|
||||||
case VibrancyType::TOOLTIP:
|
case VibrancyType::Titlebar:
|
||||||
case VibrancyType::MENU:
|
|
||||||
// Tooltip and menu windows are never "key", so we need to tell the
|
|
||||||
// vibrancy effect to look active regardless of window state.
|
|
||||||
return NSVisualEffectStateActive;
|
|
||||||
case VibrancyType::TITLEBAR:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return NSVisualEffectStateFollowsWindowActiveState;
|
return NSVisualEffectStateFollowsWindowActiveState;
|
||||||
|
|
@ -40,11 +35,7 @@ static NSVisualEffectState VisualEffectStateForVibrancyType(
|
||||||
static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType(
|
static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType(
|
||||||
VibrancyType aType) {
|
VibrancyType aType) {
|
||||||
switch (aType) {
|
switch (aType) {
|
||||||
case VibrancyType::TOOLTIP:
|
case VibrancyType::Titlebar:
|
||||||
return (NSVisualEffectMaterial)NSVisualEffectMaterialToolTip;
|
|
||||||
case VibrancyType::MENU:
|
|
||||||
return NSVisualEffectMaterialMenu;
|
|
||||||
case VibrancyType::TITLEBAR:
|
|
||||||
return NSVisualEffectMaterialTitlebar;
|
return NSVisualEffectMaterialTitlebar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,10 +43,7 @@ static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType(
|
||||||
static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType(
|
static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType(
|
||||||
VibrancyType aType) {
|
VibrancyType aType) {
|
||||||
switch (aType) {
|
switch (aType) {
|
||||||
case VibrancyType::TOOLTIP:
|
case VibrancyType::Titlebar:
|
||||||
case VibrancyType::MENU:
|
|
||||||
return NSVisualEffectBlendingModeBehindWindow;
|
|
||||||
case VibrancyType::TITLEBAR:
|
|
||||||
return StaticPrefs::widget_macos_titlebar_blend_mode_behind_window()
|
return StaticPrefs::widget_macos_titlebar_blend_mode_behind_window()
|
||||||
? NSVisualEffectBlendingModeBehindWindow
|
? NSVisualEffectBlendingModeBehindWindow
|
||||||
: NSVisualEffectBlendingModeWithinWindow;
|
: NSVisualEffectBlendingModeWithinWindow;
|
||||||
|
|
@ -63,7 +51,6 @@ static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType(
|
||||||
}
|
}
|
||||||
|
|
||||||
@implementation MOZVibrantView
|
@implementation MOZVibrantView
|
||||||
|
|
||||||
- (instancetype)initWithFrame:(NSRect)aRect vibrancyType:(VibrancyType)aType {
|
- (instancetype)initWithFrame:(NSRect)aRect vibrancyType:(VibrancyType)aType {
|
||||||
self = [super initWithFrame:aRect];
|
self = [super initWithFrame:aRect];
|
||||||
mType = aType;
|
mType = aType;
|
||||||
|
|
@ -76,50 +63,31 @@ static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType(
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't override allowsVibrancy here, because this view may have subviews, and
|
|
||||||
// returning YES from allowsVibrancy forces on foreground vibrancy for all
|
|
||||||
// descendant views, which can have unintended effects.
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MOZVibrantLeafView
|
|
||||||
|
|
||||||
- (NSView*)hitTest:(NSPoint)aPoint {
|
- (NSView*)hitTest:(NSPoint)aPoint {
|
||||||
// This view must be transparent to mouse events.
|
// This view must be transparent to mouse events.
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MOZVibrantLeafView does not have subviews, so we can return YES here without
|
|
||||||
// having unintended effects on other contents of the window.
|
|
||||||
- (BOOL)allowsVibrancy {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
VibrancyManager::VibrancyManager(const nsChildView& aCoordinateConverter,
|
||||||
|
NSView* aContainerView)
|
||||||
|
: mCoordinateConverter(aCoordinateConverter),
|
||||||
|
mContainerView(aContainerView) {}
|
||||||
|
|
||||||
|
VibrancyManager::~VibrancyManager() = default;
|
||||||
|
|
||||||
bool VibrancyManager::UpdateVibrantRegion(
|
bool VibrancyManager::UpdateVibrantRegion(
|
||||||
VibrancyType aType, const LayoutDeviceIntRegion& aRegion) {
|
VibrancyType aType, const LayoutDeviceIntRegion& aRegion) {
|
||||||
|
auto& slot = mVibrantRegions[aType];
|
||||||
if (aRegion.IsEmpty()) {
|
if (aRegion.IsEmpty()) {
|
||||||
return mVibrantRegions.Remove(uint32_t(aType));
|
bool hadRegion = !!slot;
|
||||||
|
slot = nullptr;
|
||||||
|
return hadRegion;
|
||||||
}
|
}
|
||||||
auto& vr = *mVibrantRegions.GetOrInsertNew(uint32_t(aType));
|
if (!slot) {
|
||||||
return vr.UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() {
|
slot = MakeUnique<ViewRegion>();
|
||||||
return CreateEffectView(aType);
|
}
|
||||||
|
return slot->UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() {
|
||||||
|
return [[MOZVibrantView alloc] initWithFrame:NSZeroRect vibrancyType:aType];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutDeviceIntRegion VibrancyManager::GetUnionOfVibrantRegions() const {
|
|
||||||
LayoutDeviceIntRegion result;
|
|
||||||
for (const auto& region : mVibrantRegions.Values()) {
|
|
||||||
result.OrWith(region->Region());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ NSView* VibrancyManager::CreateEffectView(VibrancyType aType,
|
|
||||||
BOOL aIsContainer) {
|
|
||||||
return aIsContainer ? [[MOZVibrantView alloc] initWithFrame:NSZeroRect
|
|
||||||
vibrancyType:aType]
|
|
||||||
: [[MOZVibrantLeafView alloc] initWithFrame:NSZeroRect
|
|
||||||
vibrancyType:aType];
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#define ViewRegion_h
|
#define ViewRegion_h
|
||||||
|
|
||||||
#include "Units.h"
|
#include "Units.h"
|
||||||
|
#include "nsRegion.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
|
|
||||||
class nsChildView;
|
class nsChildView;
|
||||||
|
|
|
||||||
|
|
@ -1726,32 +1726,52 @@ static Maybe<VibrancyType> ThemeGeometryTypeToVibrancyType(
|
||||||
nsITheme::ThemeGeometryType aThemeGeometryType) {
|
nsITheme::ThemeGeometryType aThemeGeometryType) {
|
||||||
switch (aThemeGeometryType) {
|
switch (aThemeGeometryType) {
|
||||||
case eThemeGeometryTypeTitlebar:
|
case eThemeGeometryTypeTitlebar:
|
||||||
return Some(VibrancyType::TITLEBAR);
|
return Some(VibrancyType::Titlebar);
|
||||||
default:
|
default:
|
||||||
return Nothing();
|
return Nothing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static LayoutDeviceIntRegion GatherVibrantRegion(
|
static EnumeratedArray<VibrancyType, LayoutDeviceIntRegion>
|
||||||
const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
|
GatherVibrantRegions(Span<const nsIWidget::ThemeGeometry> aThemeGeometries) {
|
||||||
VibrancyType aVibrancyType) {
|
EnumeratedArray<VibrancyType, LayoutDeviceIntRegion> regions;
|
||||||
LayoutDeviceIntRegion region;
|
|
||||||
for (const auto& geometry : aThemeGeometries) {
|
for (const auto& geometry : aThemeGeometries) {
|
||||||
if (ThemeGeometryTypeToVibrancyType(geometry.mType) ==
|
auto vibrancyType = ThemeGeometryTypeToVibrancyType(geometry.mType);
|
||||||
Some(aVibrancyType)) {
|
if (!vibrancyType) {
|
||||||
region.OrWith(geometry.mRect);
|
continue;
|
||||||
}
|
}
|
||||||
|
regions[*vibrancyType].OrWith(geometry.mRect);
|
||||||
|
}
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtracts parts from regions in such a way that they don't have any overlap.
|
||||||
|
// Each region in the argument list will have the union of all the regions
|
||||||
|
// *following* it subtracted from itself. In other words, the arguments are
|
||||||
|
// treated as low priority to high priority.
|
||||||
|
static void MakeRegionsNonOverlapping(Span<LayoutDeviceIntRegion> aRegions) {
|
||||||
|
LayoutDeviceIntRegion unionOfAll;
|
||||||
|
for (auto& region : aRegions) {
|
||||||
|
region.SubOut(unionOfAll);
|
||||||
|
unionOfAll.OrWith(region);
|
||||||
}
|
}
|
||||||
return region;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsChildView::UpdateVibrancy(
|
void nsChildView::UpdateVibrancy(
|
||||||
const nsTArray<ThemeGeometry>& aThemeGeometries) {
|
const nsTArray<ThemeGeometry>& aThemeGeometries) {
|
||||||
LayoutDeviceIntRegion titlebarRegion =
|
auto regions = GatherVibrantRegions(aThemeGeometries);
|
||||||
GatherVibrantRegion(aThemeGeometries, VibrancyType::TITLEBAR);
|
MakeRegionsNonOverlapping(Span(regions.begin(), regions.end()));
|
||||||
|
|
||||||
auto& vm = EnsureVibrancyManager();
|
auto& vm = EnsureVibrancyManager();
|
||||||
bool changed = vm.UpdateVibrantRegion(VibrancyType::TITLEBAR, titlebarRegion);
|
bool changed = false;
|
||||||
|
|
||||||
|
// EnumeratedArray doesn't have an iterator that also yields the enum type,
|
||||||
|
// but we rely on VibrancyType being contiguous and starting at 0, so we can
|
||||||
|
// do that manually.
|
||||||
|
size_t i = 0;
|
||||||
|
for (const auto& region : regions) {
|
||||||
|
changed |= vm.UpdateVibrantRegion(VibrancyType(i++), region);
|
||||||
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
SuspendAsyncCATransactions();
|
SuspendAsyncCATransactions();
|
||||||
|
|
|
||||||
|
|
@ -3071,10 +3071,6 @@ static NSMutableSet* gSwizzledFrameViewClasses = nil;
|
||||||
- (void)_setNeedsDisplayInRect:(NSRect)aRect;
|
- (void)_setNeedsDisplayInRect:(NSRect)aRect;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface NSView (NSVisualEffectViewSetMaskImage)
|
|
||||||
- (void)setMaskImage:(NSImage*)image;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface BaseWindow (Private)
|
@interface BaseWindow (Private)
|
||||||
- (void)removeTrackingArea;
|
- (void)removeTrackingArea;
|
||||||
- (void)cursorUpdated:(NSEvent*)aEvent;
|
- (void)cursorUpdated:(NSEvent*)aEvent;
|
||||||
|
|
@ -3174,35 +3170,38 @@ static NSImage* GetMenuMaskImage() {
|
||||||
return maskImage;
|
return maskImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)swapOutChildViewWrapper:(NSView*)aNewWrapper {
|
// Add an effect view wrapper if needed so that the OS draws the appropriate
|
||||||
aNewWrapper.frame = self.contentView.frame;
|
// vibrancy effect and window border.
|
||||||
|
- (void)setEffectViewWrapperForStyle:(WindowShadow)aStyle {
|
||||||
|
NSView* wrapper = [&]() -> NSView* {
|
||||||
|
if (aStyle == WindowShadow::Menu || aStyle == WindowShadow::Tooltip) {
|
||||||
|
const bool isMenu = aStyle == WindowShadow::Menu;
|
||||||
|
auto* effectView =
|
||||||
|
[[NSVisualEffectView alloc] initWithFrame:self.contentView.frame];
|
||||||
|
effectView.material =
|
||||||
|
isMenu ? NSVisualEffectMaterialMenu : NSVisualEffectMaterialToolTip;
|
||||||
|
// Tooltip and menu windows are never "key", so we need to tell the
|
||||||
|
// vibrancy effect to look active regardless of window state.
|
||||||
|
effectView.state = NSVisualEffectStateActive;
|
||||||
|
effectView.blendingMode = NSVisualEffectBlendingModeBehindWindow;
|
||||||
|
if (isMenu) {
|
||||||
|
// Turn on rounded corner masking.
|
||||||
|
effectView.maskImage = GetMenuMaskImage();
|
||||||
|
}
|
||||||
|
return effectView;
|
||||||
|
}
|
||||||
|
return [[NSView alloc] initWithFrame:self.contentView.frame];
|
||||||
|
}();
|
||||||
|
|
||||||
|
wrapper.wantsLayer = YES;
|
||||||
|
// Swap out our content view by the new view. Setting .contentView releases
|
||||||
|
// the old view.
|
||||||
NSView* childView = [self.mainChildView retain];
|
NSView* childView = [self.mainChildView retain];
|
||||||
[childView removeFromSuperview];
|
[childView removeFromSuperview];
|
||||||
[aNewWrapper addSubview:childView];
|
[wrapper addSubview:childView];
|
||||||
[childView release];
|
[childView release];
|
||||||
[super setContentView:aNewWrapper];
|
super.contentView = wrapper;
|
||||||
}
|
[wrapper release];
|
||||||
|
|
||||||
- (void)setEffectViewWrapperForStyle:(WindowShadow)aStyle {
|
|
||||||
if (aStyle == WindowShadow::Menu || aStyle == WindowShadow::Tooltip) {
|
|
||||||
// Add an effect view wrapper so that the OS draws the appropriate
|
|
||||||
// vibrancy effect and window border.
|
|
||||||
BOOL isMenu = aStyle == WindowShadow::Menu;
|
|
||||||
NSView* effectView = VibrancyManager::CreateEffectView(
|
|
||||||
isMenu ? VibrancyType::MENU : VibrancyType::TOOLTIP, YES);
|
|
||||||
if (isMenu) {
|
|
||||||
// Turn on rounded corner masking.
|
|
||||||
[effectView setMaskImage:GetMenuMaskImage()];
|
|
||||||
}
|
|
||||||
[self swapOutChildViewWrapper:effectView];
|
|
||||||
[effectView release];
|
|
||||||
} else {
|
|
||||||
// Remove the existing wrapper.
|
|
||||||
NSView* wrapper = [[NSView alloc] initWithFrame:NSZeroRect];
|
|
||||||
[wrapper setWantsLayer:YES];
|
|
||||||
[self swapOutChildViewWrapper:wrapper];
|
|
||||||
[wrapper release];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSTouchBar*)makeTouchBar {
|
- (NSTouchBar*)makeTouchBar {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue