Index: ui/views/cocoa/bridged_native_widget.mm |
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm |
index 0ccff9d15a0fd3402ed9c1e613f6552cd7de2f62..d701b765ab7878552b4ede6103174715ab7bd77f 100644 |
--- a/ui/views/cocoa/bridged_native_widget.mm |
+++ b/ui/views/cocoa/bridged_native_widget.mm |
@@ -37,8 +37,12 @@ |
extern "C" { |
+typedef int32_t CGSWindow; |
typedef int32_t CGSConnection; |
CGSConnection _CGSDefaultConnection(); |
+OSStatus CGSGetWindowBounds(CGSConnection connection, |
+ CGSWindow window, |
+ CGRect* bounds); |
CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection, |
NSInteger windowNumber, |
int radius); |
@@ -203,6 +207,13 @@ NSEvent* RepostEventIfHandledByWindow(NSEvent* ns_event) { |
// - any repost loop. |
if (repost_state == NONE) { |
+ if (CGEventGetIntegerValueField([ns_event CGEvent], |
+ kCGEventSourceUserData) == |
+ views::CocoaWindowMoveLoop:: |
+ kCocoaWindowMoveLoopSimulatedEventUserData) { |
+ return ns_event; |
+ } |
+ |
if (WindowWantsMouseDownReposted(ns_event)) { |
repost_state = EXPECTING_REPOST; |
reposted_event_number = event_number; |
@@ -615,6 +626,76 @@ bool BridgedNativeWidget::HasCapture() { |
return mouse_capture_ && mouse_capture_->IsActive(); |
} |
+Widget::MoveLoopResult BridgedNativeWidget::RunMoveLoop( |
+ const gfx::Vector2d& drag_offset) { |
+ DCHECK(!HasCapture()); |
+ DCHECK(!window_move_loop_); |
+ |
+ // First, position the window in the right place. The point |drag_offset| |
+ // away from the top-left corner needs to be positioned under the mouse. |
+ // TODO(tapted): Figure out why the toolkit-views drag controller doesn't get |
+ // this right when it first initializes the Widget. |
+ gfx::Point mouse_in_screen = gfx::Screen::GetScreen()->GetCursorScreenPoint(); |
+ |
+ // A window can't be moved vertically up out of the work area. Treat this case |
+ // as if the mouse location is at the point it would be when the window first |
+ // stopped moving. That is, vertically down such that the top edge of the |
+ // window touches the menubar (or top of the screen in a dual-screen setup). |
+ // Note on Mac we can assume that the y-coordinate of the work area origin is |
+ // the bottom of the menu bar and not the Dock which doesn't affect window |
+ // movement, but can reduce the work area on the bottom, left and right. |
+ const gfx::Display display = |
+ gfx::Screen::GetScreen()->GetDisplayNearestPoint(mouse_in_screen); |
+ const int min_y = display.work_area().y() + drag_offset.y(); |
+ if (mouse_in_screen.y() < min_y) |
+ mouse_in_screen.set_y(min_y); |
+ |
+ gfx::Rect frame = gfx::ScreenRectFromNSRect([window_ frame]); |
+ frame.set_x(mouse_in_screen.x() - drag_offset.x()); |
+ frame.set_y(mouse_in_screen.y() - drag_offset.y()); |
+ DCHECK_GE(frame.y(), display.work_area().y()); |
+ |
+ // After setting the frame to correct the initial offset, the drag controller |
+ // may immediately want to quit when it's notified of the new bounds. So the |
+ // MoveLoop must be set up before the call to setFrame. |
+ window_move_loop_.reset(new CocoaWindowMoveLoop(this, mouse_in_screen)); |
+ |
+ const NSRect ns_frame = gfx::ScreenRectToNSRect(frame); |
+ [window_ setFrame:ns_frame display:YES animate:NO]; |
+ |
+ // Setting the frame will call OnWidgetBoundsChanged(), which could result in |
+ // a call to EndMoveLoop(). |
+ if (!window_move_loop_) |
+ return Widget::MOVE_LOOP_SUCCESSFUL; |
+ |
+ // Make sure WindowServer has caught up with moving the window, this is |
+ // required in order to send the MouseDown click to the intended position. |
+ // El Capitan would always replicate the old window coordinates when calling |
+ // CGSGetWindowBounds(). |
+ // DetachToBrowserTabDragControllerTest.MacDetachesAndReattachesSecondTab will |
+ // fail without the following NSRunLoop pass. |
+ [[NSRunLoop currentRunLoop] |
+ runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]]; |
+ DCHECK(NSEqualRects(ns_frame, WindowServerFrame())); |
+ |
+ return window_move_loop_->Run(); |
+ |
+ // |this| may be destroyed during the RunLoop, causing it to exit early. |
+ // Even if that doesn't happen, CocoaWindowMoveLoop will clean itself up by |
+ // calling EndMoveLoop(). So window_move_loop_ will always be null before the |
+ // function returns. But don't DCHECK since |this| might not be valid. |
+} |
+ |
+void BridgedNativeWidget::EndMoveLoop() { |
+ DCHECK(window_move_loop_); |
+ window_move_loop_->End(); |
+ window_move_loop_.reset(); |
+} |
+ |
+bool BridgedNativeWidget::IsRunMoveLoopActive() const { |
+ return window_move_loop_.get(); |
+} |
+ |
void BridgedNativeWidget::SetNativeWindowProperty(const char* name, |
void* value) { |
NSString* key = [NSString stringWithUTF8String:name]; |
@@ -636,6 +717,7 @@ void BridgedNativeWidget::SetCursor(NSCursor* cursor) { |
} |
void BridgedNativeWidget::OnWindowWillClose() { |
+ DCHECK(!drag_run_loop_); |
if (parent_) { |
parent_->RemoveChildWindow(this); |
parent_ = nullptr; |
@@ -733,6 +815,10 @@ void BridgedNativeWidget::OnSizeChanged() { |
[bridged_view_ updateWindowMask]; |
} |
+void BridgedNativeWidget::OnPositionChanged() { |
+ native_widget_mac_->GetWidget()->OnNativeWidgetMove(); |
+} |
+ |
void BridgedNativeWidget::OnVisibilityChanged() { |
OnVisibilityChangedTo([window_ isVisible]); |
} |
@@ -886,6 +972,15 @@ gfx::Rect BridgedNativeWidget::GetRestoredBounds() const { |
return gfx::ScreenRectFromNSRect([window_ frame]); |
} |
+NSRect BridgedNativeWidget::WindowServerFrame() const { |
+ CGRect bounds = NSZeroRect; |
+ CGSGetWindowBounds(_CGSDefaultConnection(), [window_ windowNumber], |
+ &bounds); |
+ NSRect rect = ScreenRectToNSRect(gfx::Rect(bounds)); |
+ rect.size = [window_ frame].size; |
+ return rect; |
+} |
+ |
void BridgedNativeWidget::CreateLayer(ui::LayerType layer_type, |
bool translucent) { |
DCHECK(bridged_view_); |