Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1100)

Unified Diff: chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm

Issue 23918002: Switch to doing user-resizing via system for panels on Mac (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Patch to reland Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
diff --git a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm b/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
index 6dc5793f97b23189a18662de13d9c51e5327943b..562af194f6e0d58aae1c00f7af5f993b21d27b1b 100644
--- a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
+++ b/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
@@ -28,16 +28,15 @@
#include "chrome/browser/ui/panels/panel_collection.h"
#include "chrome/browser/ui/panels/panel_constants.h"
#include "chrome/browser/ui/panels/panel_manager.h"
+#include "chrome/browser/ui/panels/stacked_panel_collection.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "grit/ui_resources.h"
-#include "third_party/WebKit/public/web/WebCursorInfo.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
-#include "webkit/common/cursors/webcursor.h"
using content::WebContents;
@@ -45,8 +44,8 @@ const int kMinimumWindowSize = 1;
const double kBoundsAnimationSpeedPixelsPerSecond = 1000;
const double kBoundsAnimationMaxDurationSeconds = 0.18;
-// Resize edge thickness, in screen pixels.
-const double kWidthOfMouseResizeArea = 4.0;
+// Edge thickness to trigger user resizing via system, in screen pixels.
+const double kWidthOfMouseResizeArea = 15.0;
@interface PanelWindowControllerCocoa (PanelsCanBecomeKey)
// Internal helper method for extracting the total number of panel windows
@@ -108,253 +107,49 @@ const double kWidthOfMouseResizeArea = 4.0;
return;
[super sendEvent:anEvent];
}
-@end
-
-// Transparent view covering the whole panel in order to intercept mouse
-// messages for custom user resizing. We need custom resizing because panels
-// use their own constrained layout.
-@interface PanelResizeByMouseOverlay : NSView <MouseDragControllerClient> {
- @private
- Panel* panel_;
- base::scoped_nsobject<MouseDragController> dragController_;
- base::scoped_nsobject<NSCursor> dragCursor_;
- base::scoped_nsobject<NSCursor> eastWestCursor_;
- base::scoped_nsobject<NSCursor> northSouthCursor_;
- base::scoped_nsobject<NSCursor> northEastSouthWestCursor_;
- base::scoped_nsobject<NSCursor> northWestSouthEastCursor_;
- NSRect leftCursorRect_;
- NSRect rightCursorRect_;
- NSRect topCursorRect_;
- NSRect bottomCursorRect_;
- NSRect topLeftCursorRect_;
- NSRect topRightCursorRect_;
- NSRect bottomLeftCursorRect_;
- NSRect bottomRightCursorRect_;
-}
-@end
-
-namespace {
-NSCursor* LoadWebKitCursor(WebKit::WebCursorInfo::Type type) {
- return WebCursor(WebCursor::CursorInfo(type)).GetNativeCursor();
-}
-}
-
-@implementation PanelResizeByMouseOverlay
-- (PanelResizeByMouseOverlay*)initWithFrame:(NSRect)frame panel:(Panel*)panel {
- if ((self = [super initWithFrame:frame])) {
- panel_ = panel;
- dragController_.reset([[MouseDragController alloc] initWithClient:self]);
-
- eastWestCursor_.reset(
- [LoadWebKitCursor(WebKit::WebCursorInfo::TypeEastWestResize) retain]);
- northSouthCursor_.reset(
- [LoadWebKitCursor(WebKit::WebCursorInfo::TypeNorthSouthResize) retain]);
- northEastSouthWestCursor_.reset(
- [LoadWebKitCursor(WebKit::WebCursorInfo::TypeNorthEastSouthWestResize)
- retain]);
- northWestSouthEastCursor_.reset(
- [LoadWebKitCursor(WebKit::WebCursorInfo::TypeNorthWestSouthEastResize)
- retain]);
- }
- return self;
-}
-
-- (BOOL)acceptsFirstMouse:(NSEvent*)event {
- return YES;
-}
-
-// |pointInWindow| is in window coordinates.
-- (panel::ResizingSides)edgeHitTest:(NSPoint)pointInWindow {
- panel::Resizability resizability = panel_->CanResizeByMouse();
- DCHECK_NE(panel::NOT_RESIZABLE, resizability);
-
- NSPoint point = [self convertPoint:pointInWindow fromView:nil];
- BOOL flipped = [self isFlipped];
-
- if ((resizability & panel::RESIZABLE_TOP_LEFT) &&
- NSMouseInRect(point, topLeftCursorRect_, flipped)) {
- return panel::RESIZE_TOP_LEFT;
- }
- if ((resizability & panel::RESIZABLE_TOP_RIGHT) &&
- NSMouseInRect(point, topRightCursorRect_, flipped)) {
- return panel::RESIZE_TOP_RIGHT;
- }
- if ((resizability & panel::RESIZABLE_BOTTOM_LEFT) &&
- NSMouseInRect(point, bottomLeftCursorRect_, flipped)) {
- return panel::RESIZE_BOTTOM_LEFT;
- }
- if ((resizability & panel::RESIZABLE_BOTTOM_RIGHT) &&
- NSMouseInRect(point, bottomRightCursorRect_, flipped)) {
- return panel::RESIZE_BOTTOM_RIGHT;
- }
-
- if ((resizability & panel::RESIZABLE_LEFT) &&
- NSMouseInRect(point, leftCursorRect_, flipped)) {
- return panel::RESIZE_LEFT;
- }
- if ((resizability & panel::RESIZABLE_RIGHT) &&
- NSMouseInRect(point, rightCursorRect_, flipped)) {
- return panel::RESIZE_RIGHT;
- }
- if ((resizability & panel::RESIZABLE_TOP) &&
- NSMouseInRect(point, topCursorRect_, flipped)) {
- return panel::RESIZE_TOP;
- }
- if ((resizability & panel::RESIZABLE_BOTTOM) &&
- NSMouseInRect(point, bottomCursorRect_, flipped)) {
- return panel::RESIZE_BOTTOM;
- }
-
- return panel::RESIZE_NONE;
-}
-
-// NSWindow uses this method to figure out if this view is under the mouse
-// and hence the one to handle the incoming mouse event.
-// Since this view covers the whole panel, it is asked first.
-// See if this is the mouse event we are interested in (in the resize areas)
-// and return 'nil' to let NSWindow find another candidate otherwise.
-// |point| is in coordinate system of the parent view.
-- (NSView*)hitTest:(NSPoint)point {
- // If panel is not resizable, let the mouse events fall through.
- if (panel::NOT_RESIZABLE == panel_->CanResizeByMouse())
- return nil;
-
- NSPoint pointInWindow = [[self superview] convertPoint:point toView:nil];
- return [self edgeHitTest:pointInWindow] == panel::RESIZE_NONE ? nil : self;
-}
-
-// Delegate these to MouseDragController, it will call back on
-// MouseDragControllerClient protocol.
-- (void)mouseDown:(NSEvent*)event {
- [dragController_ mouseDown:event];
-}
-
-- (void)mouseDragged:(NSEvent*)event {
- [dragController_ mouseDragged:event];
-}
-
-- (void)mouseUp:(NSEvent*)event {
- [dragController_ mouseUp:event];
-}
-
-// MouseDragControllerClient protocol.
-
-- (void)prepareForDrag {
- // If the panel is not resizable, hitTest should have failed and no mouse
- // events should have come here.
- DCHECK_NE(panel::NOT_RESIZABLE, panel_->CanResizeByMouse());
-
- // Make sure the cursor stays the same during whole resize operation.
- // The cursor rects normally do not guarantee the same cursor, since the
- // mouse may temporarily leave the cursor rect area (or even the window) so
- // the cursor will flicker. Disable cursor rects and grab the current cursor
- // so we can set it on mouseDragged: events to avoid flicker.
- [[self window] disableCursorRects];
- dragCursor_.reset([[NSCursor currentCursor] retain]);
-}
-
-- (void)cleanupAfterDrag {
- [[self window] enableCursorRects];
- dragCursor_.reset();
-}
-
-- (void)dragStarted:(NSPoint)initialMouseLocation {
- NSPoint initialMouseLocationScreen =
- [[self window] convertBaseToScreen:initialMouseLocation];
-
- panel_->manager()->StartResizingByMouse(
- panel_,
- cocoa_utils::ConvertPointFromCocoaCoordinates(initialMouseLocationScreen),
- [self edgeHitTest:initialMouseLocation]);
-}
-
-- (void)dragProgress:(NSPoint)mouseLocation {
- NSPoint mouseLocationScreen =
- [[self window] convertBaseToScreen:mouseLocation];
- panel_->manager()->ResizeByMouse(
- cocoa_utils::ConvertPointFromCocoaCoordinates(mouseLocationScreen));
-
- // Set the resize cursor on every mouse drag event in case the mouse
- // wandered outside the window and was switched to another one.
- // This does not produce flicker, seems the real cursor is updated after
- // mouseDrag is processed.
- [dragCursor_ set];
-}
-
-- (void)dragEnded:(BOOL)cancelled {
- panel_->manager()->EndResizingByMouse(cancelled);
-}
-
-- (void)resetCursorRects {
- panel::Resizability resizability = panel_->CanResizeByMouse();
- if (panel::NOT_RESIZABLE == resizability)
- return;
-
- NSRect bounds = [self bounds];
- // Left vertical edge.
- if (resizability & panel::RESIZABLE_LEFT) {
- leftCursorRect_ = NSMakeRect(
- NSMinX(bounds),
- NSMinY(bounds) + kWidthOfMouseResizeArea,
- kWidthOfMouseResizeArea,
- NSHeight(bounds) - 2 * kWidthOfMouseResizeArea);
- [self addCursorRect:leftCursorRect_ cursor:eastWestCursor_];
- }
-
- // Right vertical edge.
- if (resizability & panel::RESIZABLE_RIGHT) {
- rightCursorRect_ = leftCursorRect_;
- rightCursorRect_.origin.x = NSMaxX(bounds) - kWidthOfMouseResizeArea;
- [self addCursorRect:rightCursorRect_ cursor:eastWestCursor_];
- }
-
- // Top horizontal edge.
- if (resizability & panel::RESIZABLE_TOP) {
- topCursorRect_ = NSMakeRect(NSMinX(bounds) + kWidthOfMouseResizeArea,
- NSMaxY(bounds) - kWidthOfMouseResizeArea,
- NSWidth(bounds) - 2 * kWidthOfMouseResizeArea,
- kWidthOfMouseResizeArea);
- [self addCursorRect:topCursorRect_ cursor:northSouthCursor_];
- }
-
- // Top left corner.
- if (resizability & panel::RESIZABLE_TOP_LEFT) {
- topLeftCursorRect_ = NSMakeRect(NSMinX(bounds),
- NSMaxY(bounds) - kWidthOfMouseResizeArea,
- kWidthOfMouseResizeArea,
- NSMaxY(bounds));
- [self addCursorRect:topLeftCursorRect_ cursor:northWestSouthEastCursor_];
- }
-
- // Top right corner.
- if (resizability & panel::RESIZABLE_TOP_RIGHT) {
- topRightCursorRect_ = topLeftCursorRect_;
- topRightCursorRect_.origin.x = NSMaxX(bounds) - kWidthOfMouseResizeArea;
- [self addCursorRect:topRightCursorRect_ cursor:northEastSouthWestCursor_];
- }
-
- // Bottom horizontal edge.
- if (resizability & panel::RESIZABLE_BOTTOM) {
- bottomCursorRect_ = topCursorRect_;
- bottomCursorRect_.origin.y = NSMinY(bounds);
- [self addCursorRect:bottomCursorRect_ cursor:northSouthCursor_];
- }
+- (void)mouseMoved:(NSEvent*)event {
+ // Cocoa does not support letting the application determine the edges that
+ // can trigger the user resizing. To work around this, we track the mouse
+ // location. When it is close to the edge/corner where the user resizing
+ // is not desired, we force the min and max size of the window to be same
+ // as current window size. For all other cases, we restore the min and max
+ // size.
+ PanelWindowControllerCocoa* controller =
+ base::mac::ObjCCast<PanelWindowControllerCocoa>([self windowController]);
+ NSRect frame = [self frame];
+ if ([controller canResizeByMouseAtCurrentLocation]) {
+ // Mac window server limits window sizes to 10000.
+ NSSize maxSize = NSMakeSize(10000, 10000);
+
+ // If the user is resizing a stacked panel by its bottom edge, make sure its
+ // height cannot grow more than what the panel below it could offer. This is
+ // because growing a stacked panel by y amount will shrink the panel below
+ // it by same amount and we do not want the panel below it being shrunk to
+ // be smaller than the titlebar.
+ Panel* panel = [controller panel];
+ NSPoint point = [NSEvent mouseLocation];
+ if (point.y < NSMinY(frame) + kWidthOfMouseResizeArea && panel->stack()) {
+ Panel* belowPanel = panel->stack()->GetPanelBelow(panel);
+ if (belowPanel && !belowPanel->IsMinimized()) {
+ maxSize.height = panel->GetBounds().height() +
+ belowPanel->GetBounds().height() - panel::kTitlebarHeight;
+ }
+ }
- // Bottom right corner.
- if (resizability & panel::RESIZABLE_BOTTOM_RIGHT) {
- bottomRightCursorRect_ = topRightCursorRect_;
- bottomRightCursorRect_.origin.y = NSMinY(bounds);
- [self addCursorRect:bottomRightCursorRect_
- cursor:northWestSouthEastCursor_];
+ // Enable the user-resizing by setting both min and max size to the right
+ // values.
+ [self setMinSize:NSMakeSize(panel::kPanelMinWidth,
+ panel::kPanelMinHeight)];
+ [self setMaxSize:maxSize];
+ } else {
+ // Disable the user-resizing by setting both min and max size to be same as
+ // current window size.
+ [self setMinSize:frame.size];
+ [self setMaxSize:frame.size];
}
- // Bottom left corner.
- if (resizability & panel::RESIZABLE_BOTTOM_LEFT) {
- bottomLeftCursorRect_ = bottomRightCursorRect_;
- bottomLeftCursorRect_.origin.x = NSMinX(bounds);
- [self addCursorRect:bottomLeftCursorRect_ cursor:northEastSouthWestCursor_];
- }
+ [super mouseMoved:event];
}
@end
@@ -389,6 +184,10 @@ NSCursor* LoadWebKitCursor(WebKit::WebCursorInfo::Type type) {
return self;
}
+- (Panel*)panel {
+ return windowShim_->panel();
+}
+
- (void)awakeFromNib {
NSWindow* window = [self window];
@@ -411,16 +210,13 @@ NSCursor* LoadWebKitCursor(WebKit::WebCursorInfo::Type type) {
frame.size.height = panelBounds.height();
[window setFrame:frame display:NO];
- // Add a transparent overlay on top of the whole window to process mouse
- // events - for example, user-resizing.
- NSView* superview = [[window contentView] superview];
- NSRect bounds = [superview bounds];
- overlayView_.reset(
- [[PanelResizeByMouseOverlay alloc] initWithFrame:bounds
- panel:windowShim_->panel()]);
- // Set autoresizing behavior: glued to edges.
- [overlayView_ setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
- [superview addSubview:overlayView_ positioned:NSWindowAbove relativeTo:nil];
+ // MacOS will turn the user-resizing to the user-dragging if the direction of
+ // the dragging is orthogonal to the direction of the arrow cursor. We do not
+ // want this since it will bypass our dragging logic. The panel window is
+ // still draggable because we track and handle the dragging in our custom way.
+ [[self window] setMovable:NO];
+
+ [self updateTrackingArea];
}
- (void)updateWebContentsViewFrame {
@@ -802,6 +598,10 @@ NSCursor* LoadWebKitCursor(WebKit::WebCursorInfo::Type type) {
}
windowShim_->panel()->OnActiveStateChanged(true);
+
+ // Make the window user-resizable when it gains the focus.
+ [[self window] setStyleMask:
+ [[self window] styleMask] | NSResizableWindowMask];
}
- (void)windowDidResignKey:(NSNotification*)notification {
@@ -813,10 +613,64 @@ NSCursor* LoadWebKitCursor(WebKit::WebCursorInfo::Type type) {
return;
[self onWindowDidResignKey];
+
+ // Make the window not user-resizable when it loses the focus. This is to
+ // solve the problem that the bottom edge of the active panel does not
+ // trigger the user-resizing if this panel stacks with another inactive
+ // panel at the bottom.
+ [[self window] setStyleMask:
+ [[self window] styleMask] & ~NSResizableWindowMask];
+}
+
+- (void)windowWillStartLiveResize:(NSNotification*)notification {
+ // Check if the user-resizing is allowed for the triggering edge/corner.
+ // This is an extra safe guard because we are not able to track the mouse
+ // movement outside the window and Cocoa could trigger the user-resizing
+ // when the mouse moves a bit outside the edge/corner.
+ if (![self canResizeByMouseAtCurrentLocation])
+ return;
+ userResizing_ = YES;
+ windowShim_->panel()->OnPanelStartUserResizing();
+}
+
+- (void)windowDidEndLiveResize:(NSNotification*)notification {
+ if (!userResizing_)
+ return;
+ userResizing_ = NO;
+
+ Panel* panel = windowShim_->panel();
+ panel->OnPanelEndUserResizing();
+
+ gfx::Rect newBounds =
+ cocoa_utils::ConvertRectFromCocoaCoordinates([[self window] frame]);
+ if (windowShim_->panel()->GetBounds() == newBounds)
+ return;
+ windowShim_->set_cached_bounds_directly(newBounds);
+
+ panel->IncreaseMaxSize(newBounds.size());
+ panel->set_full_size(newBounds.size());
+
+ panel->collection()->RefreshLayout();
+}
+
+- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)newSize {
+ // As an extra safe guard, we avoid the user resizing if it is deemed not to
+ // be allowed (see comment in windowWillStartLiveResize).
+ if ([[self window] inLiveResize] && !userResizing_)
+ return [[self window] frame].size;
+ return newSize;
}
- (void)windowDidResize:(NSNotification*)notification {
Panel* panel = windowShim_->panel();
+ if (userResizing_) {
+ panel->collection()->OnPanelResizedByMouse(
+ panel,
+ cocoa_utils::ConvertRectFromCocoaCoordinates([[self window] frame]));
+ }
+
+ [self updateTrackingArea];
+
if (![self isAnimatingBounds] ||
panel->collection()->type() != PanelCollection::DOCKED)
return;
@@ -972,10 +826,20 @@ NSCursor* LoadWebKitCursor(WebKit::WebCursorInfo::Type type) {
[[self window] setCollectionBehavior:collectionBehavior];
}
-- (void)enableResizeByMouse:(BOOL)enable {
- if (![self isWindowLoaded])
- return;
- [[self window] invalidateCursorRectsForView:overlayView_];
+- (void)updateTrackingArea {
+ NSView* superview = [[[self window] contentView] superview];
+
+ if (trackingArea_.get())
+ [superview removeTrackingArea:trackingArea_.get()];
+
+ trackingArea_.reset(
+ [[CrTrackingArea alloc] initWithRect:[superview bounds]
+ options:NSTrackingInVisibleRect |
+ NSTrackingMouseMoved |
+ NSTrackingActiveInKeyWindow
+ owner:superview
+ userInfo:nil]);
+ [superview addTrackingArea:trackingArea_.get()];
}
- (void)showShadow:(BOOL)show {
@@ -992,6 +856,46 @@ NSCursor* LoadWebKitCursor(WebKit::WebCursorInfo::Type type) {
return [[self window] isMiniaturized];
}
+- (BOOL)canResizeByMouseAtCurrentLocation {
+ panel::Resizability resizability = windowShim_->panel()->CanResizeByMouse();
+ NSRect frame = [[self window] frame];
+ NSPoint point = [NSEvent mouseLocation];
+
+ if (point.y < NSMinY(frame) + kWidthOfMouseResizeArea) {
+ if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
+ (resizability & panel::RESIZABLE_BOTTOM_LEFT) == 0) {
+ return NO;
+ }
+ if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
+ (resizability & panel::RESIZABLE_BOTTOM_RIGHT) == 0) {
+ return NO;
+ }
+ if ((resizability & panel::RESIZABLE_BOTTOM) == 0)
+ return NO;
+ } else if (point.y > NSMaxY(frame) - kWidthOfMouseResizeArea) {
+ if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
+ (resizability & panel::RESIZABLE_TOP_LEFT) == 0) {
+ return NO;
+ }
+ if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
+ (resizability & panel::RESIZABLE_TOP_RIGHT) == 0) {
+ return NO;
+ }
+ if ((resizability & panel::RESIZABLE_TOP) == 0)
+ return NO;
+ } else {
+ if (point.x < NSMinX(frame) + kWidthOfMouseResizeArea &&
+ (resizability & panel::RESIZABLE_LEFT) == 0) {
+ return NO;
+ }
+ if (point.x > NSMaxX(frame) - kWidthOfMouseResizeArea &&
+ (resizability & panel::RESIZABLE_RIGHT) == 0) {
+ return NO;
+ }
+ }
+ return YES;
+}
+
// We have custom implementation of these because our titlebar height is custom
// and does not match the standard one.
- (NSRect)frameRectForContentRect:(NSRect)contentRect {
« no previous file with comments | « chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h ('k') | chrome/browser/ui/gtk/panels/panel_gtk.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698