Bug 1070710 - Use ViewRegion for window dragging. r?spohl
MozReview-Commit-ID: 5x2XHl20P6a
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -23,16 +23,17 @@
#include "GLContextTypes.h"
#include "mozilla/Mutex.h"
#include "nsRegion.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/UniquePtr.h"
#include "nsString.h"
#include "nsIDragService.h"
+#include "ViewRegion.h"
#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#import <AppKit/NSOpenGL.h>
class nsChildView;
class nsCocoaWindow;
@@ -439,17 +440,17 @@ public:
virtual bool PreRender(LayerManagerComposite* aManager) override;
virtual void PostRender(LayerManagerComposite* aManager) override;
virtual void DrawWindowOverlay(LayerManagerComposite* aManager,
LayoutDeviceIntRect aRect) override;
virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override;
virtual void UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) override;
- const LayoutDeviceIntRegion& GetDraggableRegion() { return mDraggableRegion; }
+ LayoutDeviceIntRegion GetNonDraggableRegion() { return mNonDraggableRegion.Region(); }
virtual void ReportSwipeStarted(uint64_t aInputBlockId, bool aStartSwipe) override;
virtual void LookUpDictionary(
const nsAString& aText,
const nsTArray<mozilla::FontRange>& aFontRangeArray,
const bool aIsVertical,
const LayoutDeviceIntPoint& aPoint) override;
@@ -605,17 +606,17 @@ protected:
mozilla::UniquePtr<mozilla::widget::RectTextureImage> mCornerMaskImage;
mozilla::UniquePtr<mozilla::widget::RectTextureImage> mTitlebarImage;
mozilla::UniquePtr<mozilla::widget::RectTextureImage> mBasicCompositorImage;
// The area of mTitlebarCGContext that has changed and needs to be
// uploaded to to mTitlebarImage. Main thread only.
nsIntRegion mDirtyTitlebarRegion;
- LayoutDeviceIntRegion mDraggableRegion;
+ mozilla::ViewRegion mNonDraggableRegion;
// Cached value of [mView backingScaleFactor], to avoid sending two obj-c
// messages (respondsToSelector, backingScaleFactor) every time we need to
// use it.
// ** We'll need to reinitialize this if the backing resolution changes. **
mutable CGFloat mBackingScaleFactor;
bool mVisible;
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2724,22 +2724,51 @@ nsChildView::DoRemoteComposition(const L
// anything during the basic compositor transaction. Draw the overlay now.
DrawWindowOverlay(mGLPresenter, aRenderRect);
mGLPresenter->EndFrame();
[(ChildView*)mView postRender:mGLPresenter->GetNSOpenGLContext()];
}
+@interface NonDraggableView : NSView
+@end
+
+@implementation NonDraggableView
+- (BOOL)mouseDownCanMoveWindow { return NO; }
+- (NSView*)hitTest:(NSPoint)aPoint { return nil; }
+@end
+
void
nsChildView::UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion)
{
- if (mDraggableRegion != aRegion) {
- mDraggableRegion = aRegion;
- [(ChildView*)mView updateWindowDraggableState];
+ // mView returns YES from mouseDownCanMoveWindow, so we need to put NSViews
+ // that return NO from mouseDownCanMoveWindow in the places that shouldn't
+ // be draggable. We can't do it the other way round because returning
+ // YES from mouseDownCanMoveWindow doesn't have any effect if there's a
+ // superview that returns NO.
+ LayoutDeviceIntRegion nonDraggable;
+ nonDraggable.Sub(LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height), aRegion);
+
+ __block bool changed = false;
+
+ // Suppress calls to setNeedsDisplay during NSView geometry changes.
+ ManipulateViewWithoutNeedingDisplay(mView, ^() {
+ changed = mNonDraggableRegion.UpdateRegion(nonDraggable, *this, mView, ^() {
+ return [[NonDraggableView alloc] initWithFrame:NSZeroRect];
+ });
+ });
+
+ if (changed) {
+ // Trigger an update to the window server. This will call
+ // mouseDownCanMoveWindow.
+ // Doing this manually is only necessary because we're suppressing
+ // setNeedsDisplay calls above.
+ [[mView window] setMovableByWindowBackground:NO];
+ [[mView window] setMovableByWindowBackground:YES];
}
}
void
nsChildView::ReportSwipeStarted(uint64_t aInputBlockId,
bool aStartSwipe)
{
if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == aInputBlockId) {
@@ -3497,18 +3526,20 @@ NSEvent* gLastDragMouseDownEvent = nil;
}
[super scrollRect:aRect by:offset];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (BOOL)mouseDownCanMoveWindow
{
- // Return YES so that _regionForOpaqueDescendants gets called, where the
- // actual draggable region will be assembled.
+ // Return YES so that parts of this view can be draggable. The non-draggable
+ // parts will be covered by NSViews that return NO from
+ // mouseDownCanMoveWindow and thus override draggability from the inside.
+ // These views are assembled in nsChildView::UpdateWindowDraggingRegion.
return YES;
}
-(void)updateGLContext
{
[mGLContext setView:self];
[mGLContext update];
}
@@ -4441,17 +4472,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
// This might destroy our widget (and null out mGeckoChild).
bool defaultPrevented =
(mGeckoChild->DispatchInputEvent(&geckoEvent) == nsEventStatus_eConsumeNoDefault);
// Check to see if we are double-clicking in the titlebar.
CGFloat locationInTitlebar = [[self window] frame].size.height - [theEvent locationInWindow].y;
LayoutDeviceIntPoint pos = geckoEvent.mRefPoint;
if (!defaultPrevented && [theEvent clickCount] == 2 &&
- mGeckoChild->GetDraggableRegion().Contains(pos.x, pos.y) &&
+ !mGeckoChild->GetNonDraggableRegion().Contains(pos.x, pos.y) &&
[[self window] isKindOfClass:[ToolbarWindow class]] &&
(locationInTitlebar < [(ToolbarWindow*)[self window] titlebarHeight] ||
locationInTitlebar < [(ToolbarWindow*)[self window] unifiedToolbarHeight])) {
if ([self shouldZoomOnDoubleClick]) {
[[self window] performZoom:nil];
} else if ([self shouldMinimizeOnTitlebarDoubleClick]) {
NSButton *minimizeButton = [[self window] standardWindowButton:NSWindowMiniaturizeButton];
[minimizeButton performClick:self];
@@ -4476,85 +4507,16 @@ NSEvent* gLastDragMouseDownEvent = nil;
event.mRefPoint = mGeckoChild->CocoaPointsToDevPixels(localEventLocation);
event.mExitFrom = aExitFrom;
nsEventStatus status; // ignored
mGeckoChild->DispatchEvent(&event, status);
}
-- (void)updateWindowDraggableState
-{
- // Trigger update to the window server.
- [[self window] setMovableByWindowBackground:NO];
- [[self window] setMovableByWindowBackground:YES];
-}
-
-// aRect is in view coordinates relative to this NSView.
-- (CGRect)convertToFlippedWindowCoordinates:(NSRect)aRect
-{
- // First, convert the rect to regular window coordinates...
- NSRect inWindowCoords = [self convertRect:aRect toView:nil];
- // ... and then flip it again because window coordinates have their origin
- // in the bottom left corner, and we need it to be in the top left corner.
- inWindowCoords.origin.y = [[self window] frame].size.height - NSMaxY(inWindowCoords);
- return NSRectToCGRect(inWindowCoords);
-}
-
-static CGSRegionObj
-NewCGSRegionFromRegion(const LayoutDeviceIntRegion& aRegion,
- CGRect (^aRectConverter)(const LayoutDeviceIntRect&))
-{
- nsTArray<CGRect> rects;
- for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
- rects.AppendElement(aRectConverter(iter.Get()));
- }
-
- CGSRegionObj region;
- CGSNewRegionWithRectList(rects.Elements(), rects.Length(), ®ion);
- return region;
-}
-
-// This function is called with forMove:YES to calculate the draggable region
-// of the window which will be submitted to the window server. Window dragging
-// is handled on the window server without calling back into our process, so it
-// also works while our app is unresponsive.
-- (CGSRegionObj)_regionForOpaqueDescendants:(NSRect)aRect forMove:(BOOL)aForMove
-{
- if (!aForMove || !mGeckoChild) {
- return [super _regionForOpaqueDescendants:aRect forMove:aForMove];
- }
-
- LayoutDeviceIntRect boundingRect = mGeckoChild->CocoaPointsToDevPixels(aRect);
-
- LayoutDeviceIntRegion opaqueRegion;
- opaqueRegion.Sub(boundingRect, mGeckoChild->GetDraggableRegion());
-
- return NewCGSRegionFromRegion(opaqueRegion, ^(const LayoutDeviceIntRect& r) {
- return [self convertToFlippedWindowCoordinates:mGeckoChild->DevPixelsToCocoaPoints(r)];
- });
-}
-
-// Starting with 10.10, in addition to the traditional
-// -[NSView _regionForOpaqueDescendants:forMove:] method, there's a new form with
-// an additional forUnderTitlebar argument, which is sometimes called instead of
-// the old form. We need to override the new variant as well.
-- (CGSRegionObj)_regionForOpaqueDescendants:(NSRect)aRect
- forMove:(BOOL)aForMove
- forUnderTitlebar:(BOOL)aForUnderTitlebar
-{
- if (!aForMove || !mGeckoChild) {
- return [super _regionForOpaqueDescendants:aRect
- forMove:aForMove
- forUnderTitlebar:aForUnderTitlebar];
- }
-
- return [self _regionForOpaqueDescendants:aRect forMove:aForMove];
-}
-
- (void)handleMouseMoved:(NSEvent*)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
if (!mGeckoChild)
return;
if (mTextInputHandler->OnHandleEvent(theEvent)) {
return;