Commit f39c780f authored by Markus Stange's avatar Markus Stange Committed by Georg Koppen
Browse files

Bug 1070710 - Use ViewRegion for window dragging. r=spohl

MozReview-Commit-ID: 5x2XHl20P6a

--HG--
extra : histedit_source : 56b671bffe9e6cd497ade61ff9beed2e3bf98e14
parent dcb07018
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@

#include "nsString.h"
#include "nsIDragService.h"
#include "ViewRegion.h"

#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
@@ -493,7 +494,7 @@ public:
  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;

@@ -659,7 +660,7 @@ protected:
  // 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
+37 −80
Original line number Diff line number Diff line
@@ -2720,12 +2720,41 @@ nsChildView::DoRemoteComposition(const LayoutDeviceIntRect& aRenderRect)
  [(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];
  }
}

@@ -3566,8 +3595,10 @@ NSEvent* gLastDragMouseDownEvent = nil;

- (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;
}

@@ -4572,7 +4603,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
  CGFloat locationInTitlebar = [[self window] frame].size.height - [theEvent locationInWindow].y;
  LayoutDeviceIntPoint pos = geckoEvent.refPoint;
  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])) {
@@ -4607,80 +4638,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
  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;
  LayoutDeviceIntRegion::RectIterator iter(aRegion);
  for (;;) {
    const LayoutDeviceIntRect* r = iter.Next();
    if (!r) {
      break;
    }
    rects.AppendElement(aRectConverter(*r));
  }

  CGSRegionObj region;
  CGSNewRegionWithRectList(rects.Elements(), rects.Length(), &region);
  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;