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

Side by Side Diff: ui/views/cocoa/bridged_native_widget.mm

Issue 1747803003: MacViews: Implement Tab Dragging (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix tests and review issues. Created 4 years, 8 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ui/views/cocoa/bridged_native_widget.h" 5 #import "ui/views/cocoa/bridged_native_widget.h"
6 6
7 #import <objc/runtime.h> 7 #import <objc/runtime.h>
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <stdint.h> 9 #include <stdint.h>
10 10
(...skipping 17 matching lines...) Expand all
28 #include "ui/views/cocoa/tooltip_manager_mac.h" 28 #include "ui/views/cocoa/tooltip_manager_mac.h"
29 #import "ui/views/cocoa/views_nswindow_delegate.h" 29 #import "ui/views/cocoa/views_nswindow_delegate.h"
30 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" 30 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h"
31 #include "ui/views/view.h" 31 #include "ui/views/view.h"
32 #include "ui/views/views_delegate.h" 32 #include "ui/views/views_delegate.h"
33 #include "ui/views/widget/native_widget_mac.h" 33 #include "ui/views/widget/native_widget_mac.h"
34 #include "ui/views/widget/widget.h" 34 #include "ui/views/widget/widget.h"
35 #include "ui/views/widget/widget_aura_utils.h" 35 #include "ui/views/widget/widget_aura_utils.h"
36 #include "ui/views/widget/widget_delegate.h" 36 #include "ui/views/widget/widget_delegate.h"
37 37
38 extern "C" {
39
40 typedef int32_t CGSConnection;
41 CGSConnection _CGSDefaultConnection();
42 CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection,
43 NSInteger windowNumber,
44 int radius);
45
46 }
47
48 // The NSView that hosts the composited CALayer drawing the UI. It fills the 38 // The NSView that hosts the composited CALayer drawing the UI. It fills the
49 // window but is not hittable so that accessibility hit tests always go to the 39 // window but is not hittable so that accessibility hit tests always go to the
50 // BridgedContentView. 40 // BridgedContentView.
51 @interface ViewsCompositorSuperview : NSView 41 @interface ViewsCompositorSuperview : NSView
52 @end 42 @end
53 43
54 @implementation ViewsCompositorSuperview 44 @implementation ViewsCompositorSuperview
55 - (NSView*)hitTest:(NSPoint)aPoint { 45 - (NSView*)hitTest:(NSPoint)aPoint {
56 return nil; 46 return nil;
57 } 47 }
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 const CGFloat kYosemiteMenuOpacity = 194.0 / 255.0; 89 const CGFloat kYosemiteMenuOpacity = 194.0 / 255.0;
100 // const int kYosemiteMenuBlur = 80; 90 // const int kYosemiteMenuBlur = 80;
101 91
102 // Margin at edge and corners of the window that trigger resizing. These match 92 // Margin at edge and corners of the window that trigger resizing. These match
103 // actual Cocoa resize margins. 93 // actual Cocoa resize margins.
104 const int kResizeAreaEdgeSize = 3; 94 const int kResizeAreaEdgeSize = 3;
105 const int kResizeAreaCornerSize = 12; 95 const int kResizeAreaCornerSize = 12;
106 96
107 int kWindowPropertiesKey; 97 int kWindowPropertiesKey;
108 98
99 bool g_ignore_next_mouse_down_for_draggable_regions = false;
100
109 float GetDeviceScaleFactorFromView(NSView* view) { 101 float GetDeviceScaleFactorFromView(NSView* view) {
110 gfx::Display display = 102 gfx::Display display =
111 gfx::Screen::GetScreen()->GetDisplayNearestWindow(view); 103 gfx::Screen::GetScreen()->GetDisplayNearestWindow(view);
112 DCHECK(display.is_valid()); 104 DCHECK(display.is_valid());
113 return display.device_scale_factor(); 105 return display.device_scale_factor();
114 } 106 }
115 107
116 // Returns true if bounds passed to window in SetBounds should be treated as 108 // Returns true if bounds passed to window in SetBounds should be treated as
117 // though they are in screen coordinates. 109 // though they are in screen coordinates.
118 bool PositionWindowInScreenCoordinates(views::Widget* widget, 110 bool PositionWindowInScreenCoordinates(views::Widget* widget,
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
196 NSInteger event_number = [ns_event eventNumber]; 188 NSInteger event_number = [ns_event eventNumber];
197 189
198 // The logic here is a bit convoluted because we want to mitigate race 190 // The logic here is a bit convoluted because we want to mitigate race
199 // conditions if somehow a different mouse-down occurs between reposts. 191 // conditions if somehow a different mouse-down occurs between reposts.
200 // Specifically, we want to avoid: 192 // Specifically, we want to avoid:
201 // - BridgedNativeWidget's draggability getting out of sync (e.g. if it is 193 // - BridgedNativeWidget's draggability getting out of sync (e.g. if it is
202 // draggable outside of a repost cycle), 194 // draggable outside of a repost cycle),
203 // - any repost loop. 195 // - any repost loop.
204 196
205 if (repost_state == NONE) { 197 if (repost_state == NONE) {
198 if (g_ignore_next_mouse_down_for_draggable_regions) {
tapted 2016/04/13 08:28:12 Is it possible to scrap g_ignore_next_mouse_down_f
themblsha 2016/04/18 09:30:01 Yep, kCocoaWindowMoveLoopSimulatedEventUserData wo
199 g_ignore_next_mouse_down_for_draggable_regions = false;
200 return ns_event;
201 }
202
206 if (WindowWantsMouseDownReposted(ns_event)) { 203 if (WindowWantsMouseDownReposted(ns_event)) {
207 repost_state = EXPECTING_REPOST; 204 repost_state = EXPECTING_REPOST;
208 reposted_event_number = event_number; 205 reposted_event_number = event_number;
209 CGEventPost(kCGSessionEventTap, [ns_event CGEvent]); 206 CGEventPost(kCGSessionEventTap, [ns_event CGEvent]);
210 return nil; 207 return nil;
211 } 208 }
212 209
213 return ns_event; 210 return ns_event;
214 } 211 }
215 212
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
304 return left_rank->second < right_rank->second ? NSOrderedAscending 301 return left_rank->second < right_rank->second ? NSOrderedAscending
305 : NSOrderedDescending; 302 : NSOrderedDescending;
306 } 303 }
307 304
308 // If both are unassociated, consider that order is not important 305 // If both are unassociated, consider that order is not important
309 return NSOrderedSame; 306 return NSOrderedSame;
310 } 307 }
311 308
312 } // namespace 309 } // namespace
313 310
311 extern "C" {
tapted 2016/04/13 08:28:12 keep this where it was?
themblsha 2016/04/18 09:30:01 Done.
312
313 typedef int32_t CGSConnection;
314 CGSConnection _CGSDefaultConnection();
315 CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection,
316 NSInteger windowNumber,
317 int radius);
318
319 }
320
314 namespace views { 321 namespace views {
315 322
316 // static 323 // static
317 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( 324 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize(
318 NSWindow* window, 325 NSWindow* window,
319 const gfx::Size& content_size) { 326 const gfx::Size& content_size) {
320 NSRect content_rect = 327 NSRect content_rect =
321 NSMakeRect(0, 0, content_size.width(), content_size.height()); 328 NSMakeRect(0, 0, content_size.width(), content_size.height());
322 NSRect frame_rect = [window frameRectForContentRect:content_rect]; 329 NSRect frame_rect = [window frameRectForContentRect:content_rect];
323 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); 330 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect));
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after
608 } 615 }
609 616
610 void BridgedNativeWidget::ReleaseCapture() { 617 void BridgedNativeWidget::ReleaseCapture() {
611 mouse_capture_.reset(); 618 mouse_capture_.reset();
612 } 619 }
613 620
614 bool BridgedNativeWidget::HasCapture() { 621 bool BridgedNativeWidget::HasCapture() {
615 return mouse_capture_ && mouse_capture_->IsActive(); 622 return mouse_capture_ && mouse_capture_->IsActive();
616 } 623 }
617 624
625 Widget::MoveLoopResult BridgedNativeWidget::RunMoveLoop(
626 const gfx::Vector2d& drag_offset) {
627 DCHECK(!HasCapture());
628 DCHECK(!window_move_loop_);
629
630 // First, position the window in the right place. The point |drag_offset|
631 // away from the top-left corner needs to be positioned under the mouse.
632 // TODO(tapted): Figure out why the toolkit-views drag controller doesn't get
633 // this right when it first initializes the Widget.
634 gfx::Point mouse_in_screen = gfx::Screen::GetScreen()->GetCursorScreenPoint();
635
636 // We won't be able to simulate MouseDown events on top of menu bar, so
637 // constrain the initial dragging.
638 const gfx::Display display =
639 gfx::Screen::GetScreen()->GetDisplayNearestPoint(mouse_in_screen);
640 mouse_in_screen =
641 gfx::ConstrainToEnclosingRect(display.work_area(), mouse_in_screen);
tapted 2016/04/13 08:28:12 This isn't right. Only the menu bar affects this c
themblsha 2016/04/18 09:30:01 Tests still pass after these changes, so it's good
642
643 gfx::Rect frame = gfx::ScreenRectFromNSRect([window_ frame]);
644 frame.set_x(mouse_in_screen.x() - drag_offset.x());
645 frame.set_y(mouse_in_screen.y() - drag_offset.y());
646
647 // We won't be able to simulate MouseDown events on top of menu bar, so
648 // constrain the initial dragging. Don't use AdjustToFit(display.work_area())
649 // as it's totally fine to move beyond the other borders.
650 // DetachToBrowserTabDragControllerTest.MacDetachesWindowOnTopOfMacMenuBar
651 // will fail without this code.
652 if (frame.y() < display.work_area().y()) {
653 frame.set_y(display.work_area().y());
654 }
655
656 // After setting the frame to correct the initial offset, the drag controller
657 // may immediately want to quit when it's notified of the new bounds. So the
658 // MoveLoop must be set up before the call to setFrame.
659 window_move_loop_.reset(new CocoaWindowMoveLoop(this, mouse_in_screen));
660
661 // Animating may provide a less janky UX, but something custom would be
tapted 2016/04/13 08:28:12 This comment can go - I've changed my mind since I
themblsha 2016/04/18 09:30:01 Done.
662 // required so that it follows updates to the mouse position.
663 const NSRect ns_frame = gfx::ScreenRectToNSRect(frame);
664 [window_ setFrame:ns_frame display:YES animate:NO];
665
666 // Setting the frame will call OnWidgetBoundsChanged(), which could result in
667 // a call to EndMoveLoop().
668 if (!window_move_loop_)
669 return Widget::MOVE_LOOP_SUCCESSFUL;
670
671 // Make sure WindowServer has caught up with moving the window, this is
672 // required in order to send the MouseDown click to the intended position.
673 // I was able to always replicate old window coordinates from
tapted 2016/04/13 08:28:12 Shouldn't use "I " in comments (typically not "we"
themblsha 2016/04/18 09:30:01 Thanks :)
674 // CGSGetWindowBounds() on El Capitan.
675 // DetachToBrowserTabDragControllerTest.MacDetachesAndReattachesSecondTab will
676 // fail without the following NSRunLoop pass.
677 [[NSRunLoop currentRunLoop]
678 runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]];
679 DCHECK(NSEqualRects(ns_frame, gfx::ScreenRectToNSRect(
680 native_widget_mac()->WindowServerFrame())));
681
682 return window_move_loop_->Run();
683
684 // |this| may be destroyed during the RunLoop, causing it to exit early.
685 // Even if that doesn't happen, CocoaWindowMoveLoop will clean itself up by
686 // calling EndMoveLoop(). So window_move_loop_ will always be null before the
687 // function returns. But don't DCHECK since |this| might not be valid.
688 }
689
690 void BridgedNativeWidget::EndMoveLoop() {
691 DCHECK(window_move_loop_);
692 window_move_loop_->End();
693 window_move_loop_.reset();
694 }
695
696 bool BridgedNativeWidget::IsRunMoveLoopActive() const {
697 return window_move_loop_.get();
698 }
699
618 void BridgedNativeWidget::SetNativeWindowProperty(const char* name, 700 void BridgedNativeWidget::SetNativeWindowProperty(const char* name,
619 void* value) { 701 void* value) {
620 NSString* key = [NSString stringWithUTF8String:name]; 702 NSString* key = [NSString stringWithUTF8String:name];
621 if (value) { 703 if (value) {
622 [GetWindowProperties() setObject:[NSValue valueWithPointer:value] 704 [GetWindowProperties() setObject:[NSValue valueWithPointer:value]
623 forKey:key]; 705 forKey:key];
624 } else { 706 } else {
625 [GetWindowProperties() removeObjectForKey:key]; 707 [GetWindowProperties() removeObjectForKey:key];
626 } 708 }
627 } 709 }
628 710
629 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { 711 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const {
630 NSString* key = [NSString stringWithUTF8String:name]; 712 NSString* key = [NSString stringWithUTF8String:name];
631 return [[GetWindowProperties() objectForKey:key] pointerValue]; 713 return [[GetWindowProperties() objectForKey:key] pointerValue];
632 } 714 }
633 715
634 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { 716 void BridgedNativeWidget::SetCursor(NSCursor* cursor) {
635 [window_delegate_ setCursor:cursor]; 717 [window_delegate_ setCursor:cursor];
636 } 718 }
637 719
638 void BridgedNativeWidget::OnWindowWillClose() { 720 void BridgedNativeWidget::OnWindowWillClose() {
721 DCHECK(!drag_run_loop_);
639 if (parent_) { 722 if (parent_) {
640 parent_->RemoveChildWindow(this); 723 parent_->RemoveChildWindow(this);
641 parent_ = nullptr; 724 parent_ = nullptr;
642 } 725 }
643 [window_ setDelegate:nil]; 726 [window_ setDelegate:nil];
644 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; 727 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_];
645 native_widget_mac_->OnWindowWillClose(); 728 native_widget_mac_->OnWindowWillClose();
646 } 729 }
647 730
648 void BridgedNativeWidget::OnFullscreenTransitionStart( 731 void BridgedNativeWidget::OnFullscreenTransitionStart(
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
731 814
732 // 10.9 is unable to generate a window shadow from the composited CALayer, so 815 // 10.9 is unable to generate a window shadow from the composited CALayer, so
733 // use Quartz. 816 // use Quartz.
734 // We don't update the window mask during a live resize, instead it is done 817 // We don't update the window mask during a live resize, instead it is done
735 // after the resize is completed in viewDidEndLiveResize: in 818 // after the resize is completed in viewDidEndLiveResize: in
736 // BridgedContentView. 819 // BridgedContentView.
737 if (base::mac::IsOSMavericksOrEarlier() && ![window_ inLiveResize]) 820 if (base::mac::IsOSMavericksOrEarlier() && ![window_ inLiveResize])
738 [bridged_view_ updateWindowMask]; 821 [bridged_view_ updateWindowMask];
739 } 822 }
740 823
824 void BridgedNativeWidget::OnPositionChanged() {
825 native_widget_mac_->GetWidget()->OnNativeWidgetMove();
826 }
827
741 void BridgedNativeWidget::OnVisibilityChanged() { 828 void BridgedNativeWidget::OnVisibilityChanged() {
742 OnVisibilityChangedTo([window_ isVisible]); 829 OnVisibilityChangedTo([window_ isVisible]);
743 } 830 }
744 831
745 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { 832 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) {
746 if (window_visible_ == new_visibility) 833 if (window_visible_ == new_visibility)
747 return; 834 return;
748 835
749 window_visible_ = new_visibility; 836 window_visible_ = new_visibility;
750 837
(...skipping 556 matching lines...) Expand 10 before | Expand all | Expand 10 after
1307 void BridgedNativeWidget::SetDraggable(bool draggable) { 1394 void BridgedNativeWidget::SetDraggable(bool draggable) {
1308 [bridged_view_ setMouseDownCanMoveWindow:draggable]; 1395 [bridged_view_ setMouseDownCanMoveWindow:draggable];
1309 // AppKit will not update its cache of mouseDownCanMoveWindow unless something 1396 // AppKit will not update its cache of mouseDownCanMoveWindow unless something
1310 // changes. Previously we tried adding an NSView and removing it, but for some 1397 // changes. Previously we tried adding an NSView and removing it, but for some
1311 // reason it required reposting the mouse-down event, and didn't always work. 1398 // reason it required reposting the mouse-down event, and didn't always work.
1312 // Calling the below seems to be an effective solution. 1399 // Calling the below seems to be an effective solution.
1313 [window_ setMovableByWindowBackground:NO]; 1400 [window_ setMovableByWindowBackground:NO];
1314 [window_ setMovableByWindowBackground:YES]; 1401 [window_ setMovableByWindowBackground:YES];
1315 } 1402 }
1316 1403
1404 // static
1405 void BridgedNativeWidget::IgnoreNextMouseDownForDraggableRegions() {
1406 g_ignore_next_mouse_down_for_draggable_regions = true;
1407 }
1408
1317 } // namespace views 1409 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698