mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1051522 - Create NSVisualEffectViews for vibrant window regions. r=smichaud
This commit is contained in:
parent
c4de2026fb
commit
8a49452f67
103
widget/cocoa/VibrancyManager.h
Normal file
103
widget/cocoa/VibrancyManager.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef VibrancyManager_h
|
||||
#define VibrancyManager_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/TypedEnum.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsRegion.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#import <Foundation/NSGeometry.h>
|
||||
|
||||
@class NSView;
|
||||
class nsChildView;
|
||||
class nsIntRegion;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(VibrancyType)
|
||||
LIGHT,
|
||||
DARK
|
||||
MOZ_END_ENUM_CLASS(VibrancyType)
|
||||
|
||||
/**
|
||||
* VibrancyManager takes care of updating the vibrant regions of a window.
|
||||
* Vibrancy is a visual look that was introduced on OS X starting with 10.10.
|
||||
* An app declares vibrant window regions to the window server, and the window
|
||||
* server will display a blurred rendering of the screen contents from behind
|
||||
* the window in these areas, behind the actual window contents. Consequently,
|
||||
* the effect is only visible in areas where the window contents are not
|
||||
* completely opaque. Usually this is achieved by clearing the background of
|
||||
* the window prior to drawing in the vibrant areas. This is possible even if
|
||||
* the window is declared as opaque.
|
||||
*/
|
||||
class VibrancyManager {
|
||||
public:
|
||||
/**
|
||||
* Create a new VibrancyManager instance and provide it with an NSView
|
||||
* to attach NSVisualEffectViews to.
|
||||
*
|
||||
* @param aCoordinateConverter The nsChildView to use for converting
|
||||
* nsIntRect device pixel coordinates into Cocoa NSRect coordinates. Must
|
||||
* outlive this VibrancyManager instance.
|
||||
* @param aContainerView The view that's going to be the superview of the
|
||||
* NSVisualEffectViews which will be created for vibrant regions.
|
||||
*/
|
||||
VibrancyManager(const nsChildView& aCoordinateConverter,
|
||||
NSView* aContainerView)
|
||||
: mCoordinateConverter(aCoordinateConverter)
|
||||
, mContainerView(aContainerView)
|
||||
{
|
||||
MOZ_ASSERT(SystemSupportsVibrancy(),
|
||||
"Don't instantiate this if !SystemSupportsVibrancy()");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the placement of the NSVisualEffectViews inside the container
|
||||
* NSView so that they cover aRegion, and create new NSVisualEffectViews
|
||||
* or remove existing ones as needed.
|
||||
* @param aType The vibrancy type to use in the region.
|
||||
* @param aRegion The vibrant area, in device pixels.
|
||||
*/
|
||||
void UpdateVibrantRegion(VibrancyType aType, const nsIntRegion& aRegion);
|
||||
|
||||
/**
|
||||
* Clear the vibrant areas that we know about.
|
||||
* The clearing happens in the current NSGraphicsContext. If you call this
|
||||
* from within an -[NSView drawRect:] implementation, the currrent
|
||||
* NSGraphicsContext is already correctly set to the window drawing context.
|
||||
*/
|
||||
void ClearVibrantAreas() const;
|
||||
|
||||
/**
|
||||
* Check whether the operating system supports vibrancy at all.
|
||||
* You may only create a VibrancyManager instance if this returns true.
|
||||
* @return Whether VibrancyManager can be used on this OS.
|
||||
*/
|
||||
static bool SystemSupportsVibrancy();
|
||||
|
||||
// The following are only public because otherwise ClearVibrantRegionFunc
|
||||
// can't see them.
|
||||
struct VibrantRegion {
|
||||
nsIntRegion region;
|
||||
nsTArray<NSView*> effectViews;
|
||||
};
|
||||
void ClearVibrantRegion(const VibrantRegion& aVibrantRegion) const;
|
||||
|
||||
protected:
|
||||
NSView* CreateEffectView(VibrancyType aType, NSRect aRect);
|
||||
|
||||
const nsChildView& mCoordinateConverter;
|
||||
NSView* mContainerView;
|
||||
nsClassHashtable<nsUint32HashKey, VibrantRegion> mVibrantRegions;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // VibrancyManager_h
|
157
widget/cocoa/VibrancyManager.mm
Normal file
157
widget/cocoa/VibrancyManager.mm
Normal file
@ -0,0 +1,157 @@
|
||||
/* -*- 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"
|
||||
#import <objc/message.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
void
|
||||
VibrancyManager::UpdateVibrantRegion(VibrancyType aType, const nsIntRegion& aRegion)
|
||||
{
|
||||
auto& vr = *mVibrantRegions.LookupOrAdd(uint32_t(aType));
|
||||
if (vr.region == aRegion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to construct the required region using as many EffectViews
|
||||
// as necessary. We try to update the geometry of existing views if
|
||||
// possible, or create new ones or remove old ones if the number of
|
||||
// rects in the region has changed.
|
||||
|
||||
nsTArray<NSView*> viewsToRecycle;
|
||||
vr.effectViews.SwapElements(viewsToRecycle);
|
||||
// vr.effectViews is now empty.
|
||||
|
||||
nsIntRegionRectIterator iter(aRegion);
|
||||
const nsIntRect* iterRect = nullptr;
|
||||
for (size_t i = 0; (iterRect = iter.Next()) || i < viewsToRecycle.Length(); ++i) {
|
||||
if (iterRect) {
|
||||
NSView* view = nil;
|
||||
NSRect rect = mCoordinateConverter.DevPixelsToCocoaPoints(*iterRect);
|
||||
if (i < viewsToRecycle.Length()) {
|
||||
view = viewsToRecycle[i];
|
||||
[view setFrame:rect];
|
||||
} else {
|
||||
view = CreateEffectView(aType, rect);
|
||||
[mContainerView addSubview:view];
|
||||
|
||||
// Now that the view is in the view hierarchy, it'll be kept alive by
|
||||
// its superview, so we can drop our reference.
|
||||
[view release];
|
||||
}
|
||||
vr.effectViews.AppendElement(view);
|
||||
} else {
|
||||
// Our new region is made of less rects than the old region, so we can
|
||||
// remove this view. We only have a weak reference to it, so removing it
|
||||
// from the view hierarchy will release it.
|
||||
[viewsToRecycle[i] removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
vr.region = aRegion;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
ClearVibrantRegionFunc(const uint32_t& aVibrancyType,
|
||||
VibrancyManager::VibrantRegion* aVibrantRegion,
|
||||
void* aVM)
|
||||
{
|
||||
static_cast<VibrancyManager*>(aVM)->ClearVibrantRegion(*aVibrantRegion);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
VibrancyManager::ClearVibrantAreas() const
|
||||
{
|
||||
mVibrantRegions.EnumerateRead(ClearVibrantRegionFunc,
|
||||
const_cast<VibrancyManager*>(this));
|
||||
}
|
||||
|
||||
void
|
||||
VibrancyManager::ClearVibrantRegion(const VibrantRegion& aVibrantRegion) const
|
||||
{
|
||||
[[NSColor clearColor] set];
|
||||
|
||||
nsIntRegionRectIterator iter(aVibrantRegion.region);
|
||||
while (const nsIntRect* rect = iter.Next()) {
|
||||
NSRectFill(mCoordinateConverter.DevPixelsToCocoaPoints(*rect));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
DrawRectNothing(id self, SEL _cmd, NSRect aRect)
|
||||
{
|
||||
// The super implementation would clear the background.
|
||||
// That's fine for views that are placed below their content, but our
|
||||
// setup is different: Our drawn content is drawn to mContainerView, which
|
||||
// sits below this EffectView. So we must not clear the background here,
|
||||
// because we'd erase that drawn content.
|
||||
// Of course the regular content drawing still needs to clear the background
|
||||
// behind vibrant areas. This is taken care of by having nsNativeThemeCocoa
|
||||
// return true from NeedToClearBackgroundBehindWidget for vibrant widgets.
|
||||
}
|
||||
|
||||
static NSView*
|
||||
HitTestNil(id self, SEL _cmd, NSPoint aPoint)
|
||||
{
|
||||
// This view must be transparent to mouse events.
|
||||
return nil;
|
||||
}
|
||||
|
||||
static Class
|
||||
CreateEffectViewClass()
|
||||
{
|
||||
// Create a class called EffectView that inherits from NSVisualEffectView
|
||||
// and overrides the methods -[NSVisualEffectView drawRect:] and
|
||||
// -[NSView hitTest:].
|
||||
Class NSVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView");
|
||||
Class EffectViewClass = objc_allocateClassPair(NSVisualEffectViewClass, "EffectView", 0);
|
||||
class_addMethod(EffectViewClass, @selector(drawRect:), (IMP)DrawRectNothing,
|
||||
"v@:{CGRect={CGPoint=dd}{CGSize=dd}}");
|
||||
class_addMethod(EffectViewClass, @selector(hitTest:), (IMP)HitTestNil,
|
||||
"@@:{CGPoint=dd}");
|
||||
return EffectViewClass;
|
||||
}
|
||||
|
||||
static id
|
||||
AppearanceForVibrancyType(VibrancyType aType)
|
||||
{
|
||||
Class NSAppearanceClass = NSClassFromString(@"NSAppearance");
|
||||
switch (aType) {
|
||||
case VibrancyType::LIGHT:
|
||||
return [NSAppearanceClass performSelector:@selector(appearanceNamed:)
|
||||
withObject:@"NSAppearanceNameVibrantLight"];
|
||||
case VibrancyType::DARK:
|
||||
return [NSAppearanceClass performSelector:@selector(appearanceNamed:)
|
||||
withObject:@"NSAppearanceNameVibrantDark"];
|
||||
}
|
||||
}
|
||||
|
||||
NSView*
|
||||
VibrancyManager::CreateEffectView(VibrancyType aType, NSRect aRect)
|
||||
{
|
||||
static Class EffectViewClass = CreateEffectViewClass();
|
||||
NSView* effectView = [[EffectViewClass alloc] initWithFrame:aRect];
|
||||
[effectView performSelector:@selector(setAppearance:)
|
||||
withObject:AppearanceForVibrancyType(aType)];
|
||||
return effectView;
|
||||
}
|
||||
|
||||
static bool
|
||||
ComputeSystemSupportsVibrancy()
|
||||
{
|
||||
return NSClassFromString(@"NSAppearance") &&
|
||||
NSClassFromString(@"NSVisualEffectView");
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
VibrancyManager::SystemSupportsVibrancy()
|
||||
{
|
||||
static bool supportsVibrancy = ComputeSystemSupportsVibrancy();
|
||||
return supportsVibrancy;
|
||||
}
|
@ -53,6 +53,7 @@ UNIFIED_SOURCES += [
|
||||
'nsWidgetFactory.mm',
|
||||
'nsWindowMap.mm',
|
||||
'OSXNotificationCenter.mm',
|
||||
'VibrancyManager.mm',
|
||||
'WidgetTraceEvent.mm',
|
||||
]
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsRegion.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsIDragService.h"
|
||||
@ -93,6 +94,7 @@ class RectTextureImage;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
class VibrancyManager;
|
||||
namespace layers {
|
||||
class GLManager;
|
||||
class APZCTreeManager;
|
||||
@ -569,6 +571,7 @@ public:
|
||||
}
|
||||
|
||||
void NotifyDirtyRegion(const nsIntRegion& aDirtyRegion);
|
||||
void ClearVibrantAreas();
|
||||
|
||||
// unit conversion convenience functions
|
||||
int32_t CocoaPointsToDevPixels(CGFloat aPts) const {
|
||||
@ -625,6 +628,8 @@ protected:
|
||||
void UpdateTitlebarCGContext();
|
||||
|
||||
nsIntRect RectContainingTitlebarControls();
|
||||
void UpdateVibrancy(const nsTArray<ThemeGeometry>& aThemeGeometries);
|
||||
mozilla::VibrancyManager& EnsureVibrancyManager();
|
||||
|
||||
nsIWidget* GetWidgetForListenerEvents();
|
||||
|
||||
@ -694,6 +699,8 @@ protected:
|
||||
|
||||
nsRefPtr<APZCTreeManager> mAPZCTreeManager;
|
||||
|
||||
mozilla::UniquePtr<mozilla::VibrancyManager> mVibrancyManager;
|
||||
|
||||
static uint32_t sLastInputEventCount;
|
||||
|
||||
void ReleaseTitlebarCGContext();
|
||||
|
@ -94,6 +94,7 @@
|
||||
#include "mozilla/layers/APZCCallbackHelper.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "InputData.h"
|
||||
#include "VibrancyManager.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::layers;
|
||||
@ -2595,7 +2596,12 @@ FindFirstRectOfType(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
|
||||
void
|
||||
nsChildView::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries)
|
||||
{
|
||||
if (![mView window] || ![[mView window] isKindOfClass:[ToolbarWindow class]])
|
||||
if (![mView window])
|
||||
return;
|
||||
|
||||
UpdateVibrancy(aThemeGeometries);
|
||||
|
||||
if (![[mView window] isKindOfClass:[ToolbarWindow class]])
|
||||
return;
|
||||
|
||||
// Update unified toolbar height.
|
||||
@ -2618,6 +2624,58 @@ nsChildView::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometri
|
||||
[win placeFullScreenButton:[mView convertRect:DevPixelsToCocoaPoints(fullScreenButtonRect) toView:nil]];
|
||||
}
|
||||
|
||||
static nsIntRegion
|
||||
GatherThemeGeometryRegion(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries,
|
||||
uint8_t aWidgetType)
|
||||
{
|
||||
nsIntRegion region;
|
||||
for (size_t i = 0; i < aThemeGeometries.Length(); ++i) {
|
||||
const nsIWidget::ThemeGeometry& g = aThemeGeometries[i];
|
||||
if (g.mWidgetType == aWidgetType) {
|
||||
region.OrWith(g.mRect);
|
||||
}
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
void
|
||||
nsChildView::UpdateVibrancy(const nsTArray<ThemeGeometry>& aThemeGeometries)
|
||||
{
|
||||
if (!VibrancyManager::SystemSupportsVibrancy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIntRegion vibrantLightRegion =
|
||||
GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_MAC_VIBRANCY_LIGHT);
|
||||
nsIntRegion vibrantDarkRegion =
|
||||
GatherThemeGeometryRegion(aThemeGeometries, NS_THEME_MAC_VIBRANCY_DARK);
|
||||
|
||||
// Make light win over dark in disputed areas.
|
||||
vibrantDarkRegion.SubOut(vibrantLightRegion);
|
||||
|
||||
auto& vm = EnsureVibrancyManager();
|
||||
vm.UpdateVibrantRegion(VibrancyType::LIGHT, vibrantLightRegion);
|
||||
vm.UpdateVibrantRegion(VibrancyType::DARK, vibrantDarkRegion);
|
||||
}
|
||||
|
||||
void
|
||||
nsChildView::ClearVibrantAreas()
|
||||
{
|
||||
if (VibrancyManager::SystemSupportsVibrancy()) {
|
||||
EnsureVibrancyManager().ClearVibrantAreas();
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::VibrancyManager&
|
||||
nsChildView::EnsureVibrancyManager()
|
||||
{
|
||||
MOZ_ASSERT(mView, "Only call this once we have a view!");
|
||||
if (!mVibrancyManager) {
|
||||
mVibrancyManager = MakeUnique<VibrancyManager>(*this, mView);
|
||||
}
|
||||
return *mVibrancyManager;
|
||||
}
|
||||
|
||||
TemporaryRef<gfx::DrawTarget>
|
||||
nsChildView::StartRemoteDrawing()
|
||||
{
|
||||
@ -3645,9 +3703,10 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
// Since this view is usually declared as opaque, the window's pixel
|
||||
// buffer may now contain garbage which we need to prevent from reaching
|
||||
// the screen. The only place where garbage can show is in the window
|
||||
// corners - the rest of the window is covered by opaque content in our
|
||||
// OpenGL surface.
|
||||
// So we need to clear the pixel buffer contents in the corners.
|
||||
// corners and the vibrant regions of the window - the rest of the window
|
||||
// is covered by opaque content in our OpenGL surface.
|
||||
// So we need to clear the pixel buffer contents in these areas.
|
||||
mGeckoChild->ClearVibrantAreas();
|
||||
[self clearCorners];
|
||||
|
||||
// Do GL composition and return.
|
||||
|
@ -62,6 +62,7 @@ public:
|
||||
bool ThemeDrawsFocusForWidget(uint8_t aWidgetType) MOZ_OVERRIDE;
|
||||
bool ThemeNeedsComboboxDropmarker();
|
||||
virtual bool WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType) MOZ_OVERRIDE;
|
||||
virtual bool NeedToClearBackgroundBehindWidget(uint8_t aWidgetType) MOZ_OVERRIDE;
|
||||
virtual Transparency GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType);
|
||||
|
||||
void DrawProgress(CGContextRef context, const HIRect& inBoxRect,
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLMeterElement.h"
|
||||
#include "nsLookAndFeel.h"
|
||||
#include "VibrancyManager.h"
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxQuartzSurface.h"
|
||||
@ -3530,6 +3531,10 @@ nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* a
|
||||
}
|
||||
case NS_THEME_FOCUS_OUTLINE:
|
||||
return true;
|
||||
|
||||
case NS_THEME_MAC_VIBRANCY_LIGHT:
|
||||
case NS_THEME_MAC_VIBRANCY_DARK:
|
||||
return VibrancyManager::SystemSupportsVibrancy();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -3604,6 +3609,18 @@ nsNativeThemeCocoa::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsNativeThemeCocoa::NeedToClearBackgroundBehindWidget(uint8_t aWidgetType)
|
||||
{
|
||||
switch (aWidgetType) {
|
||||
case NS_THEME_MAC_VIBRANCY_LIGHT:
|
||||
case NS_THEME_MAC_VIBRANCY_DARK:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nsITheme::Transparency
|
||||
nsNativeThemeCocoa::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user