mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 676241 part 6/9/11: Correct titlebar drawing. r=bgirard, r=smichaud, r=mattwoodrow
This commit is contained in:
parent
0a085125ca
commit
e3ca87e242
@ -279,6 +279,10 @@ typedef NSInteger NSEventGestureAxis;
|
||||
|
||||
// Whether this uses off-main-thread compositing.
|
||||
BOOL mUsingOMTCompositor;
|
||||
|
||||
// The mask image that's used when painting into the titlebar using basic
|
||||
// CGContext painting (i.e. non-accelerated).
|
||||
CGImageRef mTopLeftCornerMask;
|
||||
}
|
||||
|
||||
// class initialization
|
||||
@ -316,6 +320,8 @@ typedef NSInteger NSEventGestureAxis;
|
||||
- (void)setGLContext:(NSOpenGLContext *)aGLContext;
|
||||
- (void)preRender:(NSOpenGLContext *)aGLContext;
|
||||
|
||||
- (BOOL)isCoveringTitlebar;
|
||||
|
||||
// Simple gestures support
|
||||
//
|
||||
// XXX - The swipeWithEvent, beginGestureWithEvent, magnifyWithEvent,
|
||||
@ -538,6 +544,8 @@ public:
|
||||
return mTextInputHandler;
|
||||
}
|
||||
|
||||
void NotifyDirtyRegion(const nsIntRegion& aDirtyRegion);
|
||||
|
||||
// unit conversion convenience functions
|
||||
int32_t CocoaPointsToDevPixels(CGFloat aPts) {
|
||||
return nsCocoaUtils::CocoaPointsToDevPixels(aPts, BackingScaleFactor());
|
||||
@ -573,8 +581,20 @@ protected:
|
||||
return widget.forget();
|
||||
}
|
||||
|
||||
void MaybeDrawResizeIndicator(mozilla::layers::GLManager* aManager, nsIntRect aRect);
|
||||
void MaybeDrawRoundedBottomCorners(mozilla::layers::GLManager* aManager, nsIntRect aRect);
|
||||
// Overlay drawing functions for OpenGL drawing
|
||||
void MaybeDrawResizeIndicator(mozilla::layers::GLManager* aManager, const nsIntRect& aRect);
|
||||
void MaybeDrawRoundedCorners(mozilla::layers::GLManager* aManager, const nsIntRect& aRect);
|
||||
void MaybeDrawTitlebar(mozilla::layers::GLManager* aManager, const nsIntRect& aRect);
|
||||
|
||||
// Redraw the contents of mTitlebarImageBuffer on the main thread, as
|
||||
// determined by mDirtyTitlebarRegion.
|
||||
void UpdateTitlebarImageBuffer();
|
||||
|
||||
// Upload the contents of mTitlebarImageBuffer to mTitlebarImage on the
|
||||
// compositor thread, as determined by mUpdatedTitlebarRegion.
|
||||
void UpdateTitlebarImage(mozilla::layers::GLManager* aManager, const nsIntRect& aRect);
|
||||
|
||||
nsIntRect RectContainingTitlebarControls();
|
||||
|
||||
nsIWidget* GetWidgetForListenerEvents();
|
||||
|
||||
@ -604,12 +624,25 @@ protected:
|
||||
nsIntRect mResizeIndicatorRect;
|
||||
bool mHasRoundedBottomCorners;
|
||||
int mDevPixelCornerRadius;
|
||||
bool mIsCoveringTitlebar;
|
||||
nsIntRect mTitlebarRect;
|
||||
|
||||
// The area of mTitlebarImageBuffer that needs to be redrawn during the next
|
||||
// transaction. Accessed from any thread, protected by mEffectsLock.
|
||||
nsIntRegion mUpdatedTitlebarRegion;
|
||||
|
||||
nsRefPtr<gfxQuartzSurface> mTitlebarImageBuffer;
|
||||
|
||||
// Compositor thread only
|
||||
bool mFailedResizerImage;
|
||||
bool mFailedCornerMaskImage;
|
||||
nsRefPtr<mozilla::gl::TextureImage> mResizerImage;
|
||||
nsRefPtr<mozilla::gl::TextureImage> mCornerMaskImage;
|
||||
nsRefPtr<mozilla::gl::TextureImage> mTitlebarImage;
|
||||
|
||||
// The area of mTitlebarImageBuffer that has changed and needs to be
|
||||
// uploaded to to mTitlebarImage. Main thread only.
|
||||
nsIntRegion mDirtyTitlebarRegion;
|
||||
|
||||
// Cached value of [mView backingScaleFactor], to avoid sending two obj-c
|
||||
// messages (respondsToSelector, backingScaleFactor) every time we need to
|
||||
|
@ -48,6 +48,7 @@
|
||||
|
||||
#include "gfxContext.h"
|
||||
#include "gfxQuartzSurface.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "nsRegion.h"
|
||||
#include "Layers.h"
|
||||
#include "LayerManagerOGL.h"
|
||||
@ -146,8 +147,12 @@ uint32_t nsChildView::sLastInputEventCount = 0;
|
||||
- (void)drawUsingOpenGLCallback;
|
||||
|
||||
- (BOOL)hasRoundedBottomCorners;
|
||||
- (CGFloat)bottomCornerRadius;
|
||||
- (void)maybeClearBottomCorners;
|
||||
- (CGFloat)cornerRadius;
|
||||
- (void)clearCorners;
|
||||
|
||||
// Overlay drawing functions for traditional CGContext drawing
|
||||
- (void)drawTitlebarHighlight;
|
||||
- (void)maskTopCornersInContext:(CGContextRef)aContext;
|
||||
|
||||
// Called using performSelector:withObject:afterDelay:0 to release
|
||||
// aWidgetArray (and its contents) the next time through the run loop.
|
||||
@ -246,6 +251,7 @@ nsChildView::nsChildView() : nsBaseWidget()
|
||||
, mEffectsLock("WidgetEffects")
|
||||
, mShowsResizeIndicator(false)
|
||||
, mHasRoundedBottomCorners(false)
|
||||
, mIsCoveringTitlebar(false)
|
||||
, mFailedResizerImage(false)
|
||||
, mFailedCornerMaskImage(false)
|
||||
, mBackingScaleFactor(0.0)
|
||||
@ -1815,15 +1821,45 @@ nsChildView::GetThebesSurface()
|
||||
return mTempThebesSurface;
|
||||
}
|
||||
|
||||
void
|
||||
nsChildView::NotifyDirtyRegion(const nsIntRegion& aDirtyRegion)
|
||||
{
|
||||
if ([(ChildView*)mView isCoveringTitlebar]) {
|
||||
// We store the dirty region so that we know what to repaint in the titlebar.
|
||||
mDirtyTitlebarRegion.Or(mDirtyTitlebarRegion, aDirtyRegion);
|
||||
mDirtyTitlebarRegion.And(mDirtyTitlebarRegion, RectContainingTitlebarControls());
|
||||
}
|
||||
}
|
||||
|
||||
static const CGFloat kTitlebarHighlightHeight = 6;
|
||||
|
||||
nsIntRect
|
||||
nsChildView::RectContainingTitlebarControls()
|
||||
{
|
||||
// Start with a 6px high strip at the top of the window for the highlight line.
|
||||
NSRect rect = NSMakeRect(0, 0, [mView bounds].size.width,
|
||||
kTitlebarHighlightHeight);
|
||||
|
||||
// Add the rects of the titlebar controls.
|
||||
for (id view in [(BaseWindow*)[mView window] titlebarControls]) {
|
||||
rect = NSUnionRect(rect, [mView convertRect:[view bounds] fromView:view]);
|
||||
}
|
||||
return CocoaPointsToDevPixels(rect);
|
||||
}
|
||||
|
||||
void
|
||||
nsChildView::PrepareWindowEffects()
|
||||
{
|
||||
MutexAutoLock lock(mEffectsLock);
|
||||
mShowsResizeIndicator = ShowsResizeIndicator(&mResizeIndicatorRect);
|
||||
mHasRoundedBottomCorners = [mView isKindOfClass:[ChildView class]] &&
|
||||
[(ChildView*)mView hasRoundedBottomCorners];
|
||||
CGFloat cornerRadius = [(ChildView*)mView bottomCornerRadius];
|
||||
mHasRoundedBottomCorners = [(ChildView*)mView hasRoundedBottomCorners];
|
||||
CGFloat cornerRadius = [(ChildView*)mView cornerRadius];
|
||||
mDevPixelCornerRadius = cornerRadius * BackingScaleFactor();
|
||||
mIsCoveringTitlebar = [(ChildView*)mView isCoveringTitlebar];
|
||||
if (mIsCoveringTitlebar) {
|
||||
mTitlebarRect = RectContainingTitlebarControls();
|
||||
UpdateTitlebarImageBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1831,6 +1867,7 @@ nsChildView::CleanupWindowEffects()
|
||||
{
|
||||
mResizerImage = nullptr;
|
||||
mCornerMaskImage = nullptr;
|
||||
mTitlebarImage = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1852,8 +1889,22 @@ nsChildView::DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect)
|
||||
return;
|
||||
}
|
||||
|
||||
manager->gl()->PushScissorRect(aRect);
|
||||
|
||||
MaybeDrawTitlebar(manager, aRect);
|
||||
MaybeDrawResizeIndicator(manager, aRect);
|
||||
MaybeDrawRoundedBottomCorners(manager, aRect);
|
||||
MaybeDrawRoundedCorners(manager, aRect);
|
||||
|
||||
manager->gl()->PopScissorRect();
|
||||
}
|
||||
|
||||
static void
|
||||
ClearRegion(gfxASurface* aSurface, nsIntRegion aRegion)
|
||||
{
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(aSurface);
|
||||
gfxUtils::ClipToRegion(ctx, aRegion);
|
||||
ctx->SetOperator(gfxContext::OPERATOR_CLEAR);
|
||||
ctx->Paint();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1890,14 +1941,14 @@ DrawResizer(CGContextRef aCtx)
|
||||
}
|
||||
|
||||
void
|
||||
nsChildView::MaybeDrawResizeIndicator(GLManager* aManager, nsIntRect aRect)
|
||||
nsChildView::MaybeDrawResizeIndicator(GLManager* aManager, const nsIntRect& aRect)
|
||||
{
|
||||
MutexAutoLock lock(mEffectsLock);
|
||||
if (!mShowsResizeIndicator || mFailedResizerImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mResizerImage) {
|
||||
MutexAutoLock lock(mEffectsLock);
|
||||
mResizerImage =
|
||||
aManager->gl()->CreateTextureImage(nsIntSize(mResizeIndicatorRect.width,
|
||||
mResizeIndicatorRect.height),
|
||||
@ -1916,17 +1967,18 @@ nsChildView::MaybeDrawResizeIndicator(GLManager* aManager, nsIntRect aRect)
|
||||
return;
|
||||
}
|
||||
|
||||
// is the CairoSurface of a non QuartzSurface usable in the gfxQuartzSurface constructor ?
|
||||
// answer seems to be NO (see comments on bug 675410
|
||||
// this should not work for instance: new gfxQuartzSurface(asurf->CairoSurface(), false))
|
||||
// We need a Quartz surface because DrawResizer wants a CGContext.
|
||||
if (asurf->GetType() != gfxASurface::SurfaceTypeQuartz) {
|
||||
NS_WARN_IF_FALSE(FALSE, "mResizerImage's surface is not Quartz");
|
||||
mResizerImage->EndUpdate();
|
||||
mResizerImage = nullptr;
|
||||
mFailedResizerImage = true;
|
||||
return;
|
||||
}
|
||||
nsRefPtr<gfxQuartzSurface> image = static_cast<gfxQuartzSurface*>(asurf);
|
||||
|
||||
ClearRegion(asurf, update);
|
||||
|
||||
nsRefPtr<gfxQuartzSurface> image = static_cast<gfxQuartzSurface*>(asurf);
|
||||
DrawResizer(image->GetCGContext());
|
||||
|
||||
mResizerImage->EndUpdate();
|
||||
@ -1934,8 +1986,8 @@ nsChildView::MaybeDrawResizeIndicator(GLManager* aManager, nsIntRect aRect)
|
||||
|
||||
NS_ABORT_IF_FALSE(mResizerImage, "Must have a texture allocated by now!");
|
||||
|
||||
float bottomX = aRect.x + aRect.width;
|
||||
float bottomY = aRect.y + aRect.height;
|
||||
float bottomX = aRect.XMost();
|
||||
float bottomY = aRect.YMost();
|
||||
|
||||
TextureImage::ScopedBindTexture texBind(mResizerImage, LOCAL_GL_TEXTURE0);
|
||||
|
||||
@ -1954,6 +2006,195 @@ nsChildView::MaybeDrawResizeIndicator(GLManager* aManager, nsIntRect aRect)
|
||||
aManager->BindAndDrawQuad(program);
|
||||
}
|
||||
|
||||
// Draw the highlight line at the top of the titlebar.
|
||||
// This function draws into the current NSGraphicsContext and assumes flippedness.
|
||||
static void
|
||||
DrawTitlebarHighlight(NSSize aWindowSize, CGFloat aRadius, CGFloat aDevicePixelWidth)
|
||||
{
|
||||
[NSGraphicsContext saveGraphicsState];
|
||||
|
||||
// Set up the clip path. We start with the outer rectangle and cut out a
|
||||
// slightly smaller inner rectangle with rounded corners.
|
||||
// The outer corners of the resulting path will be square, but they will be
|
||||
// masked away in a later step.
|
||||
NSBezierPath* path = [NSBezierPath bezierPath];
|
||||
[path setWindingRule:NSEvenOddWindingRule];
|
||||
NSRect pathRect = NSMakeRect(0, 0, aWindowSize.width, kTitlebarHighlightHeight + 2);
|
||||
[path appendBezierPathWithRect:pathRect];
|
||||
pathRect = NSInsetRect(pathRect, aDevicePixelWidth, aDevicePixelWidth);
|
||||
CGFloat innerRadius = aRadius - aDevicePixelWidth;
|
||||
[path appendBezierPathWithRoundedRect:pathRect xRadius:innerRadius yRadius:innerRadius];
|
||||
[path addClip];
|
||||
|
||||
// Now we fill the path with a subtle highlight gradient.
|
||||
NSColor* topColor = [NSColor colorWithDeviceWhite:1.0 alpha:0.4];
|
||||
NSColor* bottomColor = [NSColor colorWithDeviceWhite:1.0 alpha:0.0];
|
||||
NSGradient* gradient = [[NSGradient alloc] initWithStartingColor:topColor endingColor:bottomColor];
|
||||
[gradient drawInRect:NSMakeRect(0, 0, aWindowSize.width, kTitlebarHighlightHeight) angle:90];
|
||||
[gradient release];
|
||||
|
||||
[NSGraphicsContext restoreGraphicsState];
|
||||
}
|
||||
|
||||
// When this method is entered, mEffectsLock is already being held.
|
||||
void
|
||||
nsChildView::UpdateTitlebarImageBuffer()
|
||||
{
|
||||
nsIntRegion dirtyTitlebarRegion = mDirtyTitlebarRegion;
|
||||
mDirtyTitlebarRegion.SetEmpty();
|
||||
|
||||
if (!mTitlebarImageBuffer ||
|
||||
mTitlebarImageBuffer->GetSize() != mTitlebarRect.Size()) {
|
||||
dirtyTitlebarRegion = mTitlebarRect;
|
||||
|
||||
mTitlebarImageBuffer = new gfxQuartzSurface(mTitlebarRect.Size(),
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
}
|
||||
|
||||
if (dirtyTitlebarRegion.IsEmpty())
|
||||
return;
|
||||
|
||||
ClearRegion(mTitlebarImageBuffer, dirtyTitlebarRegion);
|
||||
|
||||
CGContextRef ctx = mTitlebarImageBuffer->GetCGContext();
|
||||
|
||||
double scale = BackingScaleFactor();
|
||||
CGContextScaleCTM(ctx, scale, scale);
|
||||
NSGraphicsContext* oldContext = [NSGraphicsContext currentContext];
|
||||
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]];
|
||||
|
||||
CGContextSaveGState(ctx);
|
||||
|
||||
BaseWindow* window = (BaseWindow*)[mView window];
|
||||
NSView* frameView = [[window contentView] superview];
|
||||
if (![frameView isFlipped]) {
|
||||
CGContextTranslateCTM(ctx, 0, [frameView bounds].size.height);
|
||||
CGContextScaleCTM(ctx, 1, -1);
|
||||
}
|
||||
|
||||
// Draw the titlebar controls into the titlebar image.
|
||||
for (id view in [window titlebarControls]) {
|
||||
NSRect viewFrame = [view frame];
|
||||
nsIntRect viewRect = CocoaPointsToDevPixels([mView convertRect:viewFrame fromView:frameView]);
|
||||
nsIntRegion intersection;
|
||||
intersection.And(dirtyTitlebarRegion, viewRect);
|
||||
if (intersection.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// All of the titlebar controls we're interested in are subclasses of
|
||||
// NSButton.
|
||||
if (![view isKindOfClass:[NSButton class]]) {
|
||||
continue;
|
||||
}
|
||||
NSButton *button = (NSButton *) view;
|
||||
id cellObject = [button cell];
|
||||
if (![cellObject isKindOfClass:[NSCell class]]) {
|
||||
continue;
|
||||
}
|
||||
NSCell *cell = (NSCell *) cellObject;
|
||||
|
||||
CGContextSaveGState(ctx);
|
||||
CGContextTranslateCTM(ctx, viewFrame.origin.x, viewFrame.origin.y);
|
||||
|
||||
if ([view isFlipped]) {
|
||||
CGContextTranslateCTM(ctx, 0, viewFrame.size.height);
|
||||
CGContextScaleCTM(ctx, 1, -1);
|
||||
}
|
||||
|
||||
NSRect intersectRect = DevPixelsToCocoaPoints(intersection.GetBounds());
|
||||
[cell drawWithFrame:[view convertRect:intersectRect fromView:mView] inView:button];
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
CGContextRestoreGState(ctx);
|
||||
|
||||
DrawTitlebarHighlight([frameView bounds].size, [(ChildView*)mView cornerRadius],
|
||||
DevPixelsToCocoaPoints(1));
|
||||
|
||||
[NSGraphicsContext setCurrentContext:oldContext];
|
||||
|
||||
mUpdatedTitlebarRegion.Or(mUpdatedTitlebarRegion, dirtyTitlebarRegion);
|
||||
}
|
||||
|
||||
// When this method is entered, mEffectsLock is already being held.
|
||||
void
|
||||
nsChildView::UpdateTitlebarImage(GLManager* aManager, const nsIntRect& aRect)
|
||||
{
|
||||
nsIntRegion updatedTitlebarRegion;
|
||||
updatedTitlebarRegion.And(mUpdatedTitlebarRegion, mTitlebarRect);
|
||||
mUpdatedTitlebarRegion.SetEmpty();
|
||||
|
||||
if (!mTitlebarImage || mTitlebarImage->GetSize() != mTitlebarRect.Size()) {
|
||||
updatedTitlebarRegion = mTitlebarRect;
|
||||
|
||||
mTitlebarImage =
|
||||
aManager->gl()->CreateTextureImage(mTitlebarRect.Size(),
|
||||
gfxASurface::CONTENT_COLOR_ALPHA,
|
||||
LOCAL_GL_CLAMP_TO_EDGE,
|
||||
TextureImage::UseNearestFilter);
|
||||
|
||||
// Creation of texture images can fail.
|
||||
if (!mTitlebarImage)
|
||||
return;
|
||||
}
|
||||
|
||||
if (updatedTitlebarRegion.IsEmpty())
|
||||
return;
|
||||
|
||||
gfxASurface *asurf = mTitlebarImage->BeginUpdate(updatedTitlebarRegion);
|
||||
if (!asurf) {
|
||||
mTitlebarImage = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(asurf);
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->SetSource(mTitlebarImageBuffer);
|
||||
ctx->Paint();
|
||||
|
||||
mTitlebarImage->EndUpdate();
|
||||
}
|
||||
|
||||
// This method draws an overlay in the top of the window which contains the
|
||||
// titlebar controls (e.g. close, min, zoom, fullscreen) and the titlebar
|
||||
// highlight effect.
|
||||
// This is necessary because the real titlebar controls are covered by our
|
||||
// OpenGL context. Note that in terms of the NSView hierarchy, our ChildView
|
||||
// is actually below the titlebar controls - that's why hovering and clicking
|
||||
// them works as expected - but their visual representation is only drawn into
|
||||
// the normal window buffer, and the window buffer surface lies below the
|
||||
// GLContext surface. In order to make the titlebar controls visible, we have
|
||||
// to redraw them inside the OpenGL context surface.
|
||||
void
|
||||
nsChildView::MaybeDrawTitlebar(GLManager* aManager, const nsIntRect& aRect)
|
||||
{
|
||||
MutexAutoLock lock(mEffectsLock);
|
||||
if (!mIsCoveringTitlebar) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateTitlebarImage(aManager, aRect);
|
||||
|
||||
if (!mTitlebarImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextureImage::ScopedBindTexture texBind(mTitlebarImage, LOCAL_GL_TEXTURE0);
|
||||
|
||||
ShaderProgramOGL *program =
|
||||
aManager->GetProgram(mTitlebarImage->GetShaderProgramType());
|
||||
program->Activate();
|
||||
program->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0),
|
||||
mTitlebarImage->GetSize()));
|
||||
program->SetLayerTransform(gfx3DMatrix());
|
||||
program->SetLayerOpacity(1.0);
|
||||
program->SetRenderOffset(nsIntPoint(0,0));
|
||||
program->SetTextureUnit(0);
|
||||
|
||||
aManager->BindAndDrawQuad(program);
|
||||
}
|
||||
|
||||
static void
|
||||
DrawTopLeftCornerMask(CGContextRef aCtx, int aRadius)
|
||||
{
|
||||
@ -1962,14 +2203,14 @@ DrawTopLeftCornerMask(CGContextRef aCtx, int aRadius)
|
||||
}
|
||||
|
||||
void
|
||||
nsChildView::MaybeDrawRoundedBottomCorners(GLManager* aManager, nsIntRect aRect)
|
||||
nsChildView::MaybeDrawRoundedCorners(GLManager* aManager, const nsIntRect& aRect)
|
||||
{
|
||||
if (!mHasRoundedBottomCorners ||
|
||||
mFailedCornerMaskImage)
|
||||
return;
|
||||
|
||||
MutexAutoLock lock(mEffectsLock);
|
||||
|
||||
if (mFailedCornerMaskImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mCornerMaskImage) {
|
||||
mCornerMaskImage =
|
||||
aManager->gl()->CreateTextureImage(nsIntSize(mDevPixelCornerRadius,
|
||||
@ -1988,20 +2229,20 @@ nsChildView::MaybeDrawRoundedBottomCorners(GLManager* aManager, nsIntRect aRect)
|
||||
mCornerMaskImage = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// is the CairoSurface of a non QuartzSurface usable in the gfxQuartzSurface constructor ?
|
||||
// answer seems to be NO (see comments on bug 675410
|
||||
// this should not work for instance: new gfxQuartzSurface(asurf->CairoSurface(), false))
|
||||
|
||||
if (asurf->GetType() != gfxASurface::SurfaceTypeQuartz) {
|
||||
NS_WARN_IF_FALSE(FALSE, "mCornerMaskImage's surface is not Quartz");
|
||||
NS_WARNING("mCornerMaskImage's surface is not Quartz");
|
||||
mCornerMaskImage->EndUpdate();
|
||||
mCornerMaskImage = nullptr;
|
||||
mFailedCornerMaskImage = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ClearRegion(asurf, update);
|
||||
|
||||
nsRefPtr<gfxQuartzSurface> image = static_cast<gfxQuartzSurface*>(asurf);
|
||||
|
||||
DrawTopLeftCornerMask(image->GetCGContext(), mDevPixelCornerRadius);
|
||||
|
||||
|
||||
mCornerMaskImage->EndUpdate();
|
||||
}
|
||||
|
||||
@ -2011,25 +2252,36 @@ nsChildView::MaybeDrawRoundedBottomCorners(GLManager* aManager, nsIntRect aRect)
|
||||
|
||||
ShaderProgramOGL *program = aManager->GetProgram(mCornerMaskImage->GetShaderProgramType());
|
||||
program->Activate();
|
||||
program->SetLayerQuadRect(nsIntRect(0, 0, // aRect.x, aRect.y,
|
||||
mDevPixelCornerRadius,
|
||||
mDevPixelCornerRadius));
|
||||
program->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0),
|
||||
mCornerMaskImage->GetSize()));
|
||||
program->SetLayerOpacity(1.0);
|
||||
program->SetRenderOffset(nsIntPoint(0,0));
|
||||
program->SetTextureUnit(0);
|
||||
|
||||
|
||||
// Use operator destination in: multiply all 4 channels with source alpha.
|
||||
aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA,
|
||||
LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA);
|
||||
|
||||
// Draw; first bottom left, then bottom right.
|
||||
program->SetLayerTransform(gfx3DMatrix::ScalingMatrix(1, -1, 1) *
|
||||
gfx3DMatrix::Translation(0, aRect.height, 0));
|
||||
aManager->BindAndDrawQuad(program);
|
||||
program->SetLayerTransform(gfx3DMatrix::ScalingMatrix(-1, -1, 1) *
|
||||
gfx3DMatrix::Translation(aRect.width, aRect.height, 0));
|
||||
aManager->BindAndDrawQuad(program);
|
||||
|
||||
if (mIsCoveringTitlebar) {
|
||||
// Mask the top corners.
|
||||
program->SetLayerTransform(gfx3DMatrix::ScalingMatrix(1, 1, 1) *
|
||||
gfx3DMatrix::Translation(0, 0, 0));
|
||||
aManager->BindAndDrawQuad(program);
|
||||
program->SetLayerTransform(gfx3DMatrix::ScalingMatrix(-1, 1, 1) *
|
||||
gfx3DMatrix::Translation(aRect.width, 0, 0));
|
||||
aManager->BindAndDrawQuad(program);
|
||||
}
|
||||
|
||||
if (mHasRoundedBottomCorners) {
|
||||
// Mask the bottom corners.
|
||||
program->SetLayerTransform(gfx3DMatrix::ScalingMatrix(1, -1, 1) *
|
||||
gfx3DMatrix::Translation(0, aRect.height, 0));
|
||||
aManager->BindAndDrawQuad(program);
|
||||
program->SetLayerTransform(gfx3DMatrix::ScalingMatrix(-1, -1, 1) *
|
||||
gfx3DMatrix::Translation(aRect.width, aRect.height, 0));
|
||||
aManager->BindAndDrawQuad(program);
|
||||
}
|
||||
|
||||
// Reset blend mode.
|
||||
aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
|
||||
LOCAL_GL_ONE, LOCAL_GL_ONE);
|
||||
@ -2235,6 +2487,8 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
mCancelSwipeAnimation = nil;
|
||||
mCurrentSwipeDir = 0;
|
||||
#endif
|
||||
|
||||
mTopLeftCornerMask = NULL;
|
||||
}
|
||||
|
||||
// register for things we'll take from other applications
|
||||
@ -2335,6 +2589,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
[mPendingDirtyRects release];
|
||||
[mLastMouseDownEvent release];
|
||||
[mClickThroughMouseDownEvent release];
|
||||
CGImageRelease(mTopLeftCornerMask);
|
||||
ChildViewMouseTracker::OnDestroyView(self);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
@ -2688,6 +2943,13 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isCoveringTitlebar
|
||||
{
|
||||
return [[self window] isKindOfClass:[BaseWindow class]] &&
|
||||
[(BaseWindow*)[self window] mainChildView] == self &&
|
||||
[(BaseWindow*)[self window] drawsContentsIntoWindowFrame];
|
||||
}
|
||||
|
||||
// The display system has told us that a portion of our view is dirty. Tell
|
||||
// gecko to paint it
|
||||
- (void)drawRect:(NSRect)aRect
|
||||
@ -2724,31 +2986,14 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
fprintf (stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty);
|
||||
#endif
|
||||
|
||||
if ([self isUsingOpenGL]) {
|
||||
// We usually don't get here for Gecko-initiated repaints. Instead, those
|
||||
// eventually call drawUsingOpenGL, and don't go through drawRect.
|
||||
// Paints that come through here are triggered by something that Cocoa
|
||||
// controls, for example by window resizing or window focus changes.
|
||||
|
||||
// Since our window 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 bottom
|
||||
// corners - the rest of the window is covered by our OpenGL surface.
|
||||
// So we need to clear the pixel buffer contents in the corners.
|
||||
[self maybeClearBottomCorners];
|
||||
|
||||
// Do GL composition and return.
|
||||
[self drawUsingOpenGL];
|
||||
return;
|
||||
}
|
||||
|
||||
PROFILER_LABEL("widget", "ChildView::drawRect");
|
||||
|
||||
nsIntRegion region;
|
||||
nsIntRect boundingRect = mGeckoChild->CocoaPointsToDevPixels(aRect);
|
||||
const NSRect *rects;
|
||||
NSInteger count, i;
|
||||
[[NSView focusView] getRectsBeingDrawn:&rects count:&count];
|
||||
|
||||
CGContextClipToRects(aContext, (CGRect*)rects, count);
|
||||
|
||||
if (count < MAX_RECTS_IN_REGION) {
|
||||
for (i = 0; i < count; ++i) {
|
||||
// Add the rect to the region.
|
||||
@ -2760,26 +3005,53 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
region = boundingRect;
|
||||
}
|
||||
|
||||
// Create Cairo objects.
|
||||
if ([self isUsingOpenGL]) {
|
||||
// For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is
|
||||
// directly called from a delayed perform callback - without going through
|
||||
// drawRect.
|
||||
// Paints that come through here are triggered by something that Cocoa
|
||||
// controls, for example by window resizing or window focus changes.
|
||||
|
||||
// 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.
|
||||
[self clearCorners];
|
||||
|
||||
// When our view covers the titlebar, we need to repaint the titlebar
|
||||
// texture buffer when, for example, the window buttons are hovered.
|
||||
// So we notify our nsChildView about any areas needing repainting.
|
||||
mGeckoChild->NotifyDirtyRegion(region);
|
||||
|
||||
// Do GL composition and return.
|
||||
[self drawUsingOpenGL];
|
||||
return;
|
||||
}
|
||||
|
||||
PROFILER_LABEL("widget", "ChildView::drawRect");
|
||||
|
||||
// The CGContext that drawRect supplies us with comes with a transform that
|
||||
// scales one user space unit to one Cocoa point, which can consist of
|
||||
// multiple dev pixels. But Gecko expects its supplied context to be scaled
|
||||
// to device pixels, so we need to reverse the scaling.
|
||||
double scale = mGeckoChild->BackingScaleFactor();
|
||||
CGContextSaveGState(aContext);
|
||||
CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale);
|
||||
|
||||
NSSize viewSize = [self bounds].size;
|
||||
nsIntSize backingSize(viewSize.width * scale, viewSize.height * scale);
|
||||
|
||||
CGContextSaveGState(aContext);
|
||||
|
||||
// Create Cairo objects.
|
||||
nsRefPtr<gfxQuartzSurface> targetSurface =
|
||||
new gfxQuartzSurface(aContext, backingSize);
|
||||
targetSurface->SetAllowUseAsSource(false);
|
||||
|
||||
nsRefPtr<gfxContext> targetContext = new gfxContext(targetSurface);
|
||||
|
||||
gfxContextMatrixAutoSaveRestore save(targetContext);
|
||||
|
||||
// Set up the clip region.
|
||||
nsIntRegionRectIterator iter(region);
|
||||
targetContext->NewPath();
|
||||
@ -2806,6 +3078,15 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
painted = mGeckoChild->PaintWindow(region);
|
||||
}
|
||||
|
||||
targetContext = nullptr;
|
||||
targetSurface = nullptr;
|
||||
|
||||
CGContextRestoreGState(aContext);
|
||||
|
||||
// Undo the scale transform so that from now on the context is in
|
||||
// CocoaPoints again.
|
||||
CGContextRestoreGState(aContext);
|
||||
|
||||
if (!painted && [self isOpaque]) {
|
||||
// Gecko refused to draw, but we've claimed to be opaque, so we have to
|
||||
// draw something--fill with white.
|
||||
@ -2813,9 +3094,10 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
CGContextFillRect(aContext, NSRectToCGRect(aRect));
|
||||
}
|
||||
|
||||
// note that the cairo surface *MUST* be destroyed at this point,
|
||||
// or bad things will happen (since we can't keep the cgContext around
|
||||
// beyond this drawRect message handler)
|
||||
if ([self isCoveringTitlebar]) {
|
||||
[self drawTitlebarHighlight];
|
||||
[self maskTopCornersInContext:aContext];
|
||||
}
|
||||
|
||||
#ifdef DEBUG_UPDATE
|
||||
fprintf (stderr, "---- update done ----\n");
|
||||
@ -2900,39 +3182,92 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
[[self window] bottomCornerRounded];
|
||||
}
|
||||
|
||||
- (CGFloat)bottomCornerRadius
|
||||
- (CGFloat)cornerRadius
|
||||
{
|
||||
if (![self hasRoundedBottomCorners])
|
||||
return 0.0f;
|
||||
NSView* borderView = [[[self window] contentView] superview];
|
||||
if (!borderView || ![borderView respondsToSelector:@selector(roundedCornerRadius)])
|
||||
NSView* frameView = [[[self window] contentView] superview];
|
||||
if (!frameView || ![frameView respondsToSelector:@selector(roundedCornerRadius)])
|
||||
return 4.0f;
|
||||
return [borderView roundedCornerRadius];
|
||||
return [frameView roundedCornerRadius];
|
||||
}
|
||||
|
||||
// Accelerated windows have two NSSurfaces:
|
||||
// (1) The window's pixel buffer in the back and
|
||||
// (2) the OpenGL view in the front.
|
||||
// These two surfaces are composited by the window manager. Drawing with the
|
||||
// usual CGContext functions ends up in (1).
|
||||
// When our window has rounded bottom corners, the OpenGL view has transparent
|
||||
// pixels in the corners. In these places the contents of the window's pixel
|
||||
// buffer can show through. So we need to make sure that the pixel buffer is
|
||||
// These two surfaces are composited by the window manager. Drawing into the
|
||||
// CGContext which is provided by drawRect ends up in (1).
|
||||
// When our window has rounded corners, the OpenGL view has transparent pixels
|
||||
// in the corners. In these places the contents of the window's pixel buffer
|
||||
// can show through. So we need to make sure that the pixel buffer is
|
||||
// transparent in the corners so that no garbage reaches the screen.
|
||||
// The contents of the pixel buffer in the rest of the window don't matter
|
||||
// because they're covered by the OpenGL view.
|
||||
// Making the bottom corners transparent works even though our window is
|
||||
// because they're covered by opaque pixels of the OpenGL context.
|
||||
// Making the corners transparent works even though our window is
|
||||
// declared "opaque" (in the NSWindow's isOpaque method).
|
||||
- (void)maybeClearBottomCorners
|
||||
- (void)clearCorners
|
||||
{
|
||||
if (![self hasRoundedBottomCorners])
|
||||
return;
|
||||
|
||||
int radius = [self bottomCornerRadius];
|
||||
int w = [self bounds].size.width, h = [self bounds].size.height;
|
||||
CGFloat radius = [self cornerRadius];
|
||||
CGFloat w = [self bounds].size.width, h = [self bounds].size.height;
|
||||
[[NSColor clearColor] set];
|
||||
NSRectFill(NSMakeRect(0, h - radius, radius, radius));
|
||||
NSRectFill(NSMakeRect(w - radius, h - radius, radius, radius));
|
||||
|
||||
if ([self isCoveringTitlebar]) {
|
||||
NSRectFill(NSMakeRect(0, 0, radius, radius));
|
||||
NSRectFill(NSMakeRect(w - radius, 0, radius, radius));
|
||||
}
|
||||
|
||||
if ([self hasRoundedBottomCorners]) {
|
||||
NSRectFill(NSMakeRect(0, h - radius, radius, radius));
|
||||
NSRectFill(NSMakeRect(w - radius, h - radius, radius, radius));
|
||||
}
|
||||
}
|
||||
|
||||
// This is the analog of nsChildView::MaybeDrawRoundedCorners for CGContexts.
|
||||
// We only need to mask the top corners here because Cocoa does the masking
|
||||
// for the window's bottom corners automatically (starting with 10.7).
|
||||
- (void)maskTopCornersInContext:(CGContextRef)aContext
|
||||
{
|
||||
CGFloat radius = [self cornerRadius];
|
||||
int32_t devPixelCornerRadius = mGeckoChild->CocoaPointsToDevPixels(radius);
|
||||
|
||||
// First make sure that mTopLeftCornerMask is set up.
|
||||
if (!mTopLeftCornerMask ||
|
||||
int32_t(CGImageGetWidth(mTopLeftCornerMask)) != devPixelCornerRadius) {
|
||||
CGImageRelease(mTopLeftCornerMask);
|
||||
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef imgCtx = CGBitmapContextCreate(NULL,
|
||||
devPixelCornerRadius,
|
||||
devPixelCornerRadius,
|
||||
8, devPixelCornerRadius * 4,
|
||||
rgb, kCGImageAlphaPremultipliedFirst);
|
||||
CGColorSpaceRelease(rgb);
|
||||
DrawTopLeftCornerMask(imgCtx, devPixelCornerRadius);
|
||||
mTopLeftCornerMask = CGBitmapContextCreateImage(imgCtx);
|
||||
CGContextRelease(imgCtx);
|
||||
}
|
||||
|
||||
// kCGBlendModeDestinationIn is the secret sauce which allows us to erase
|
||||
// already painted pixels. It's defined as R = D * Sa: multiply all channels
|
||||
// of the destination pixel with the alpha of the source pixel. In our case,
|
||||
// the source is mTopLeftCornerMask.
|
||||
CGContextSaveGState(aContext);
|
||||
CGContextSetBlendMode(aContext, kCGBlendModeDestinationIn);
|
||||
|
||||
CGRect destRect = CGRectMake(0, 0, radius, radius);
|
||||
|
||||
// Erase the top left corner...
|
||||
CGContextDrawImage(aContext, destRect, mTopLeftCornerMask);
|
||||
|
||||
// ... and the top right corner.
|
||||
CGContextTranslateCTM(aContext, [self bounds].size.width, 0);
|
||||
CGContextScaleCTM(aContext, -1, 1);
|
||||
CGContextDrawImage(aContext, destRect, mTopLeftCornerMask);
|
||||
|
||||
CGContextRestoreGState(aContext);
|
||||
}
|
||||
|
||||
- (void)drawTitlebarHighlight
|
||||
{
|
||||
DrawTitlebarHighlight([self bounds].size, [self cornerRadius],
|
||||
mGeckoChild->DevPixelsToCocoaPoints(1));
|
||||
}
|
||||
|
||||
- (void)releaseWidgets:(NSArray*)aWidgetArray
|
||||
|
@ -97,6 +97,8 @@ typedef struct _nsCocoaWindowList {
|
||||
|
||||
- (ChildView*)mainChildView;
|
||||
|
||||
- (NSArray*)titlebarControls;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSWindow (Undocumented)
|
||||
|
@ -2716,6 +2716,15 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
||||
[frameView addSubview:aView positioned:NSWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
- (NSArray*)titlebarControls
|
||||
{
|
||||
// Return all subviews of the frameView which are not the content view.
|
||||
NSView* frameView = [[self contentView] superview];
|
||||
NSMutableArray* array = [[[frameView subviews] mutableCopy] autorelease];
|
||||
[array removeObject:[self contentView]];
|
||||
return array;
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
||||
{
|
||||
// Claim the window doesn't respond to this so that the system
|
||||
|
Loading…
Reference in New Issue
Block a user