Bug 579250. Revamp unified-titlebar interface so that we can tell nsNativeThemeCocoa about toolbars even if they're not actually painted. r=mstange

This commit is contained in:
Robert O'Callahan 2010-07-24 21:35:29 +12:00
parent e9176dbe58
commit bc67da304a
8 changed files with 179 additions and 92 deletions

View File

@ -46,6 +46,7 @@
#include "nsColor.h"
struct nsRect;
struct nsIntRect;
struct nsIntSize;
struct nsFont;
struct nsIntMargin;
@ -55,6 +56,7 @@ class nsIDeviceContext;
class nsIFrame;
class nsIContent;
class nsIAtom;
class nsIWidget;
// IID for the nsITheme interface
// {887e8902-db6b-41b4-8481-a80f49c5a93a}
@ -83,12 +85,44 @@ enum nsTransparencyMode {
class nsITheme: public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITHEME_IID)
/**
* Draw the actual theme background.
* @param aContext the context to draw into
* @param aFrame the frame for the widget that we're drawing
* @param aWidgetType the -moz-appearance value to draw
* @param aRect the rectangle defining the area occupied by the widget
* @param aDirtyRect the rectangle that needs to be drawn
*/
NS_IMETHOD DrawWidgetBackground(nsIRenderingContext* aContext,
nsIFrame* aFrame,
PRUint8 aWidgetType,
const nsRect& aRect,
const nsRect& aDirtyRect)=0;
const nsRect& aDirtyRect) = 0;
/**
* Notifies the theme engine that a particular themed widget exists
* at the given rectangle within the window aWindow.
* For certain appearance values (currently only
* NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR and NS_THEME_TOOLBAR) this gets
* called during every paint to a window, for every themed widget of
* the right type within the
* window, except for themed widgets which are transformed or have
* effects applied to them (e.g. CSS opacity or filters).
* Note that a DrawWidgetBackground for the widget might not be called
* during the paint, since ThebesLayers can cache rendered content.
* This could sometimes be called during display list construction
* outside of painting.
* If called during painting, it will be called before we actually
* paint anything.
*
* @param aWidgetType the -moz-appearance value for the themed widget
* @param aRect the device-pixel rect within aWindow for the themed
* widget
*/
virtual void RegisterWidgetGeometry(nsIWidget* aWindow,
PRUint8 aWidgetType,
const nsIntRect& aRect) {}
/**
* Get the computed CSS border for the widget, in pixels.

View File

@ -58,6 +58,7 @@
#endif
#include "nsLayoutUtils.h"
#include "nsIScrollableFrame.h"
#include "nsThemeConstants.h"
#include "imgIContainer.h"
#include "nsIInterfaceRequestorUtils.h"
@ -606,6 +607,51 @@ void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
aCtx->FillRect(mVisibleRect);
}
static void
RegisterThemeWidgetGeometry(nsIFrame* aFrame)
{
nsPresContext* presContext = aFrame->PresContext();
nsITheme* theme = presContext->GetTheme();
if (!theme)
return;
nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
nsIWidget* widget = displayRoot->GetNearestWidget();
// If the display root doesn't have a widget, just bail. Something
// weird is going on, maybe we're printing?
if (!widget)
return;
for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
// Bail out if we're in a transformed subtree
if (f->IsTransformed())
return;
// Bail out if we're not in the displayRoot's document
if (!f->GetParent() && f != displayRoot)
return;
}
nsRect borderBox(aFrame->GetOffsetTo(displayRoot), aFrame->GetSize());
theme->RegisterWidgetGeometry(widget,
aFrame->GetStyleDisplay()->mAppearance,
borderBox.ToNearestPixels(presContext->AppUnitsPerDevPixel()));
}
nsDisplayBackground::nsDisplayBackground(nsIFrame* aFrame)
: nsDisplayItem(aFrame)
{
MOZ_COUNT_CTOR(nsDisplayBackground);
const nsStyleDisplay* disp = mFrame->GetStyleDisplay();
mIsThemed = mFrame->IsThemed(disp, &mThemeTransparency);
// Perform necessary RegisterWidgetGeometry
if (mIsThemed &&
(disp->mAppearance == NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR ||
disp->mAppearance == NS_THEME_TOOLBAR)) {
RegisterThemeWidgetGeometry(aFrame);
}
}
// Returns TRUE if aContainedRect is guaranteed to be contained in
// the rounded rect defined by aRoundedRect and aRadii. Complex cases are
// handled conservatively by returning FALSE in some situations where

View File

@ -1293,10 +1293,7 @@ private:
*/
class nsDisplayBackground : public nsDisplayItem {
public:
nsDisplayBackground(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
mIsThemed = mFrame->IsThemed(&mThemeTransparency);
MOZ_COUNT_CTOR(nsDisplayBackground);
}
nsDisplayBackground(nsIFrame* aFrame);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayBackground() {
MOZ_COUNT_DTOR(nsDisplayBackground);

View File

@ -185,8 +185,8 @@ PRUint32 nsChildView::sLastInputEventCount = 0;
+ (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
- (BOOL)beginMaybeResetUnifiedToolbar:(nsIntRegion*)aRegion context:(CGContextRef)aContext;
- (void)endMaybeResetUnifiedToolbar:(BOOL)aReset;
- (float)beginMaybeResetUnifiedToolbar;
- (void)endMaybeResetUnifiedToolbar:(float)aOldHeight;
#if USE_CLICK_HOLD_CONTEXTMENU
// called on a timer two seconds after a mouse down to see if we should display
@ -2574,40 +2574,25 @@ NSEvent* gLastDragMouseDownEvent = nil;
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// Unified toolbar height resetting
// This fixes the following problem:
// The window gets notified about the height of its unified toolbar when the
// toolbar is drawn. But when the toolbar suddenly vanishes, it's not drawn,
// and the window is never notified about its absence.
// So we bracket drawing operations to the pixel strip under the title bar
// with notifications to the window.
static BOOL DrawingAtWindowTop(CGContextRef aContext)
{
// Ignore all non-trivial transforms.
CGAffineTransform ctm = CGContextGetCTM(aContext);
if (ctm.a != 1.0f || ctm.b != 0.0f || ctm.c != 0.0f || ctm.d != -1.0f)
return NO;
// ctm.ty contains the vertical offset from the window's bottom edge.
return ctm.ty >= [[[[NSView focusView] window] contentView] bounds].size.height;
}
- (BOOL)beginMaybeResetUnifiedToolbar:(nsIntRegion*)aRegion context:(CGContextRef)aContext
// Whenever we paint a toplevel window, we will be notified of any
// unified toolbar in the window via
// nsNativeThemeCocoa::RegisterWidgetGeometry.
- (float)beginMaybeResetUnifiedToolbar
{
if (![[self window] isKindOfClass:[ToolbarWindow class]] ||
!DrawingAtWindowTop(aContext) ||
!aRegion->Contains(nsIntRect(0, 0, (int)[self bounds].size.width, 1)))
return NO;
[self superview] != [[self window] contentView])
return 0.0;
[(ToolbarWindow*)[self window] beginMaybeResetUnifiedToolbar];
return YES;
return [(ToolbarWindow*)[self window] beginMaybeResetUnifiedToolbar];
}
- (void)endMaybeResetUnifiedToolbar:(BOOL)aReset
- (void)endMaybeResetUnifiedToolbar:(float)aOldHeight
{
if (aReset) {
[(ToolbarWindow*)[self window] endMaybeResetUnifiedToolbar];
}
if (![[self window] isKindOfClass:[ToolbarWindow class]] ||
[self superview] != [[self window] contentView])
return;
[(ToolbarWindow*)[self window] endMaybeResetUnifiedToolbar:aOldHeight];
}
-(void)update
@ -2715,8 +2700,7 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
}
targetContext->Clip();
BOOL resetUnifiedToolbar =
[self beginMaybeResetUnifiedToolbar:&paintEvent.region context:aContext];
float oldHeight = [self beginMaybeResetUnifiedToolbar];
nsAutoRetainCocoaObject kungFuDeathGrip(self);
PRBool painted;
@ -2734,7 +2718,7 @@ static BOOL DrawingAtWindowTop(CGContextRef aContext)
aRect.size.width, aRect.size.height));
}
[self endMaybeResetUnifiedToolbar:resetUnifiedToolbar];
[self endMaybeResetUnifiedToolbar:oldHeight];
// note that the cairo surface *MUST* be destroyed at this point,
// or bad things will happen (since we can't keep the cgContext around

View File

@ -172,15 +172,15 @@ struct UnifiedGradientInfo {
{
TitlebarAndBackgroundColor *mColor;
float mUnifiedToolbarHeight;
BOOL mWaitingForUnifiedToolbarHeight;
BOOL mInUnifiedToolbarReset;
NSColor *mBackgroundColor;
}
// Pass nil here to get the default appearance.
- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
- (void)setUnifiedToolbarHeight:(float)aToolbarHeight;
- (void)notifyToolbarAt:(float)aY height:(float)aHeight;
- (float)unifiedToolbarHeight;
- (void)beginMaybeResetUnifiedToolbar;
- (void)endMaybeResetUnifiedToolbar;
- (float)beginMaybeResetUnifiedToolbar;
- (void)endMaybeResetUnifiedToolbar:(float)aOldHeight;
- (float)titlebarHeight;
- (NSRect)titlebarRect;
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync;

View File

@ -1995,10 +1995,12 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
//
// Drawing the unified gradient in the titlebar and the toolbar works like this:
// 1) In the style sheet we set the toolbar's -moz-appearance to -moz-mac-unified-toolbar.
// 2) When the toolbar is drawn, Gecko calls nsNativeThemeCocoa::DrawWidgetBackground
// for the widget type NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
// 3) This calls DrawUnifiedToolbar which finds the toolbar frame's ToolbarWindow
// and passes the toolbar frame's height to setUnifiedToolbarHeight.
// 2) When the toolbar is visible and we paint the application chrome
// window in nsChildView::drawRect, Gecko calls
// nsNativeThemeCocoa::RegisterWidgetGeometry for the widget type
// NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR.
// 3) This finds the toolbar frame's ToolbarWindow and passes the toolbar
// frame's height to setUnifiedToolbarHeight.
// 4) If the toolbar height has changed, a titlebar redraw is triggered by
// [self display] and the upper part of the unified gradient is drawn in the
// titlebar.
@ -2025,7 +2027,7 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
mBackgroundColor = [NSColor whiteColor];
mUnifiedToolbarHeight = 0.0f;
mWaitingForUnifiedToolbarHeight = NO;
mInUnifiedToolbarReset = NO;
// setBottomCornerRounded: is a private API call, so we check to make sure
// we respond to it just in case.
@ -2069,21 +2071,18 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
return mBackgroundColor;
}
// This is called by nsNativeThemeCocoa.mm's DrawUnifiedToolbar.
// This is called by nsNativeThemeCocoa.mm's RegisterWidgetGeometry.
// We need to know the toolbar's height in order to draw the correct
// unified gradient in the titlebar.
- (void)setUnifiedToolbarHeight:(float)aToolbarHeight
- (void)notifyToolbarAt:(float)aY height:(float)aHeight
{
mWaitingForUnifiedToolbarHeight = NO;
if (mUnifiedToolbarHeight == aToolbarHeight)
// Ignore unexpected notifications about the toolbar height
if (!mInUnifiedToolbarReset)
return;
mUnifiedToolbarHeight = aToolbarHeight;
[self setContentBorderThickness:aToolbarHeight forEdge:NSMaxYEdge];
// Since this function is only called inside painting, the repaint needs to
// be synchronous.
[self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:YES];
if (aY <= 0.0 && aY + aHeight > mUnifiedToolbarHeight) {
mUnifiedToolbarHeight = aY + aHeight;
}
}
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect
@ -2126,16 +2125,26 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
return frameRect.size.height - [self contentRectForFrameRect:frameRect].size.height;
}
- (void)beginMaybeResetUnifiedToolbar
- (float)beginMaybeResetUnifiedToolbar
{
mWaitingForUnifiedToolbarHeight = YES;
mInUnifiedToolbarReset = YES;
float old = mUnifiedToolbarHeight;
mUnifiedToolbarHeight = 0.0;
return old;
}
- (void)endMaybeResetUnifiedToolbar
- (void)endMaybeResetUnifiedToolbar:(float)aOldHeight
{
if (mWaitingForUnifiedToolbarHeight) {
// No toolbar was drawn, so set the height to zero.
[self setUnifiedToolbarHeight:0.0f];
if (mInUnifiedToolbarReset) {
mInUnifiedToolbarReset = NO;
if (mUnifiedToolbarHeight == aOldHeight)
return;
[self setContentBorderThickness:mUnifiedToolbarHeight forEdge:NSMaxYEdge];
// Since this function is only called inside painting, the repaint needs to
// be synchronous.
[self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:YES];
}
}

View File

@ -67,6 +67,9 @@ public:
PRUint8 aWidgetType,
const nsRect& aRect,
const nsRect& aDirtyRect);
virtual void RegisterWidgetGeometry(nsIWidget* aWindow,
PRUint8 aWidgetType,
const nsIntRect& aRect);
NS_IMETHOD GetWidgetBorder(nsIDeviceContext* aContext,
nsIFrame* aFrame,
PRUint8 aWidgetType,

View File

@ -1338,24 +1338,18 @@ nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame)
return scrollbarFrame;
}
static BOOL DrawingAtWindowTop(CGContextRef cgContext, float viewHeight, float yPos)
{
// Ignore all non-trivial transforms.
CGAffineTransform ctm = CGContextGetCTM(cgContext);
if (ctm.a != 1.0f || ctm.b != 0.0f || ctm.c != 0.0f || ctm.d != -1.0f)
return NO;
// ctm.ty contains the vertical offset from the window's bottom edge.
return ctm.ty - yPos >= viewHeight;
}
static BOOL
static PRBool
ToolbarCanBeUnified(CGContextRef cgContext, const HIRect& inBoxRect, NSWindow* aWindow)
{
return [aWindow isKindOfClass:[ToolbarWindow class]] &&
![(ToolbarWindow*)aWindow drawsContentsIntoWindowFrame] &&
DrawingAtWindowTop(cgContext, [[aWindow contentView] bounds].size.height,
inBoxRect.origin.y);
if (![aWindow isKindOfClass:[ToolbarWindow class]] ||
[(ToolbarWindow*)aWindow drawsContentsIntoWindowFrame])
return PR_FALSE;
float unifiedToolbarHeight = [(ToolbarWindow*)aWindow unifiedToolbarHeight];
return inBoxRect.origin.x == 0 &&
inBoxRect.size.width == [aWindow frame].size.width &&
inBoxRect.origin.y <= 0.0 &&
inBoxRect.origin.y + inBoxRect.size.height <= unifiedToolbarHeight;
}
void
@ -1364,16 +1358,8 @@ nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inB
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
float titlebarHeight = 0;
float titlebarHeight = [(ToolbarWindow*)aWindow titlebarHeight];
if (ToolbarCanBeUnified(cgContext, inBoxRect, aWindow)) {
// Consider the titlebar height when calculating the gradient.
titlebarHeight = [(ToolbarWindow*)aWindow titlebarHeight];
// Notify the window about the toolbar's height so that it can draw the
// correct gradient in the titlebar.
[(ToolbarWindow*)aWindow setUnifiedToolbarHeight:inBoxRect.size.height];
}
BOOL isMain = [aWindow isMainWindow] || ![NSView focusView];
// Draw the gradient
@ -1493,6 +1479,25 @@ nsNativeThemeCocoa::DrawResizer(CGContextRef cgContext, const HIRect& aRect,
NS_OBJC_END_TRY_ABORT_BLOCK;
}
static PRBool
IsWindowSpanningToolbar(nsIWidget* aWindow,
PRUint8 aWidgetType,
const nsIntRect& aRect,
ToolbarWindow** aCocoaWindow)
{
nsIWidget* topLevelWidget = aWindow->GetTopLevelWidget();
if (!topLevelWidget)
return PR_FALSE;
NSWindow* win = (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
if (!win || ![win isKindOfClass:[ToolbarWindow class]])
return PR_FALSE;
*aCocoaWindow = (ToolbarWindow*)win;
return (aWidgetType == NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR ||
aWidgetType == NS_THEME_TOOLBAR) &&
aRect.x == 0 && aRect.width == [win frame].size.width;
}
NS_IMETHODIMP
nsNativeThemeCocoa::DrawWidgetBackground(nsIRenderingContext* aContext, nsIFrame* aFrame,
PRUint8 aWidgetType, const nsRect& aRect,
@ -1677,9 +1682,6 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsIRenderingContext* aContext, nsIFrame
break;
case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
DrawUnifiedToolbar(cgContext, macRect, NativeWindowForFrame(aFrame));
break;
case NS_THEME_TOOLBAR: {
NSWindow* win = NativeWindowForFrame(aFrame);
if (ToolbarCanBeUnified(cgContext, macRect, win)) {
@ -1924,6 +1926,18 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsIRenderingContext* aContext, nsIFrame
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
void
nsNativeThemeCocoa::RegisterWidgetGeometry(nsIWidget* aWindow,
PRUint8 aWidgetType,
const nsIntRect& aRect)
{
ToolbarWindow* cocoaWindow;
if (IsWindowSpanningToolbar(aWindow, aWidgetType, aRect, &cocoaWindow) &&
![cocoaWindow drawsContentsIntoWindowFrame]) {
[cocoaWindow notifyToolbarAt:aRect.y height:aRect.height];
}
}
nsIntMargin
nsNativeThemeCocoa::RTLAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame)
{