OLD | NEW |
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 Loading... |
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 Loading... |
99 const CGFloat kYosemiteMenuOpacity = 177.0 / 255.0; | 89 const CGFloat kYosemiteMenuOpacity = 177.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 Loading... |
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) { |
| 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 Loading... |
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" { |
| 312 |
| 313 typedef int32_t CGSConnection; |
| 314 CGSConnection _CGSDefaultConnection(); |
| 315 CGError CGSSetWindowBackgroundBlurRadius(CGSConnection connection, |
| 316 NSInteger windowNumber, |
| 317 int radius); |
| 318 } |
| 319 |
314 namespace views { | 320 namespace views { |
315 | 321 |
316 // static | 322 // static |
317 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( | 323 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( |
318 NSWindow* window, | 324 NSWindow* window, |
319 const gfx::Size& content_size) { | 325 const gfx::Size& content_size) { |
320 NSRect content_rect = | 326 NSRect content_rect = |
321 NSMakeRect(0, 0, content_size.width(), content_size.height()); | 327 NSMakeRect(0, 0, content_size.width(), content_size.height()); |
322 NSRect frame_rect = [window frameRectForContentRect:content_rect]; | 328 NSRect frame_rect = [window frameRectForContentRect:content_rect]; |
323 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); | 329 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
608 } | 614 } |
609 | 615 |
610 void BridgedNativeWidget::ReleaseCapture() { | 616 void BridgedNativeWidget::ReleaseCapture() { |
611 mouse_capture_.reset(); | 617 mouse_capture_.reset(); |
612 } | 618 } |
613 | 619 |
614 bool BridgedNativeWidget::HasCapture() { | 620 bool BridgedNativeWidget::HasCapture() { |
615 return mouse_capture_ && mouse_capture_->IsActive(); | 621 return mouse_capture_ && mouse_capture_->IsActive(); |
616 } | 622 } |
617 | 623 |
| 624 Widget::MoveLoopResult BridgedNativeWidget::RunMoveLoop( |
| 625 const gfx::Vector2d& drag_offset) { |
| 626 DCHECK(!HasCapture()); |
| 627 DCHECK(!window_move_loop_); |
| 628 |
| 629 // First, position the window in the right place. The point |drag_offset| |
| 630 // away from the top-left corner needs to be positioned under the mouse. |
| 631 // TODO(tapted): Figure out why the toolkit-views drag controller doesn't get |
| 632 // this right when it first initializes the Widget. |
| 633 gfx::Point mouse_in_screen = gfx::Screen::GetScreen()->GetCursorScreenPoint(); |
| 634 |
| 635 // A window can't be moved vertically up out of the work area. Treat this case |
| 636 // as if the mouse location is at the point it would be when the window first |
| 637 // stopped moving. That is, vertically down such that the top edge of the |
| 638 // window touches the menubar (or top of the screen in a dual-screen setup). |
| 639 // Note on Mac we can assume that the y-coordinate of the work area origin is |
| 640 // the bottom of the menu bar and not the Dock which doesn't affect window |
| 641 // movement, but can reduce the work area on the bottom, left and right. |
| 642 const gfx::Display display = |
| 643 gfx::Screen::GetScreen()->GetDisplayNearestPoint(mouse_in_screen); |
| 644 int min_y = display.work_area().y() + drag_offset.y(); |
| 645 if (mouse_in_screen.y() < min_y) |
| 646 mouse_in_screen.set_y(min_y); |
| 647 |
| 648 gfx::Rect frame = gfx::ScreenRectFromNSRect([window_ frame]); |
| 649 frame.set_x(mouse_in_screen.x() - drag_offset.x()); |
| 650 frame.set_y(mouse_in_screen.y() - drag_offset.y()); |
| 651 DCHECK_GE(frame.y(), display.work_area().y()); |
| 652 |
| 653 // After setting the frame to correct the initial offset, the drag controller |
| 654 // may immediately want to quit when it's notified of the new bounds. So the |
| 655 // MoveLoop must be set up before the call to setFrame. |
| 656 window_move_loop_.reset(new CocoaWindowMoveLoop(this, mouse_in_screen)); |
| 657 |
| 658 // Animating may provide a less janky UX, but something custom would be |
| 659 // required so that it follows updates to the mouse position. |
| 660 const NSRect ns_frame = gfx::ScreenRectToNSRect(frame); |
| 661 [window_ setFrame:ns_frame display:YES animate:NO]; |
| 662 |
| 663 // Setting the frame will call OnWidgetBoundsChanged(), which could result in |
| 664 // a call to EndMoveLoop(). |
| 665 if (!window_move_loop_) |
| 666 return Widget::MOVE_LOOP_SUCCESSFUL; |
| 667 |
| 668 // Make sure WindowServer has caught up with moving the window, this is |
| 669 // required in order to send the MouseDown click to the intended position. |
| 670 // I was able to always replicate old window coordinates from |
| 671 // CGSGetWindowBounds() on El Capitan. |
| 672 // DetachToBrowserTabDragControllerTest.MacDetachesAndReattachesSecondTab will |
| 673 // fail without the following NSRunLoop pass. |
| 674 [[NSRunLoop currentRunLoop] |
| 675 runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]]; |
| 676 DCHECK(NSEqualRects(ns_frame, gfx::ScreenRectToNSRect( |
| 677 native_widget_mac()->WindowServerFrame()))); |
| 678 |
| 679 return window_move_loop_->Run(); |
| 680 |
| 681 // |this| may be destroyed during the RunLoop, causing it to exit early. |
| 682 // Even if that doesn't happen, CocoaWindowMoveLoop will clean itself up by |
| 683 // calling EndMoveLoop(). So window_move_loop_ will always be null before the |
| 684 // function returns. But don't DCHECK since |this| might not be valid. |
| 685 } |
| 686 |
| 687 void BridgedNativeWidget::EndMoveLoop() { |
| 688 DCHECK(window_move_loop_); |
| 689 window_move_loop_->End(); |
| 690 window_move_loop_.reset(); |
| 691 } |
| 692 |
| 693 bool BridgedNativeWidget::IsRunMoveLoopActive() const { |
| 694 return window_move_loop_.get(); |
| 695 } |
| 696 |
618 void BridgedNativeWidget::SetNativeWindowProperty(const char* name, | 697 void BridgedNativeWidget::SetNativeWindowProperty(const char* name, |
619 void* value) { | 698 void* value) { |
620 NSString* key = [NSString stringWithUTF8String:name]; | 699 NSString* key = [NSString stringWithUTF8String:name]; |
621 if (value) { | 700 if (value) { |
622 [GetWindowProperties() setObject:[NSValue valueWithPointer:value] | 701 [GetWindowProperties() setObject:[NSValue valueWithPointer:value] |
623 forKey:key]; | 702 forKey:key]; |
624 } else { | 703 } else { |
625 [GetWindowProperties() removeObjectForKey:key]; | 704 [GetWindowProperties() removeObjectForKey:key]; |
626 } | 705 } |
627 } | 706 } |
628 | 707 |
629 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { | 708 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { |
630 NSString* key = [NSString stringWithUTF8String:name]; | 709 NSString* key = [NSString stringWithUTF8String:name]; |
631 return [[GetWindowProperties() objectForKey:key] pointerValue]; | 710 return [[GetWindowProperties() objectForKey:key] pointerValue]; |
632 } | 711 } |
633 | 712 |
634 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { | 713 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { |
635 [window_delegate_ setCursor:cursor]; | 714 [window_delegate_ setCursor:cursor]; |
636 } | 715 } |
637 | 716 |
638 void BridgedNativeWidget::OnWindowWillClose() { | 717 void BridgedNativeWidget::OnWindowWillClose() { |
| 718 DCHECK(!drag_run_loop_); |
639 if (parent_) { | 719 if (parent_) { |
640 parent_->RemoveChildWindow(this); | 720 parent_->RemoveChildWindow(this); |
641 parent_ = nullptr; | 721 parent_ = nullptr; |
642 } | 722 } |
643 [window_ setDelegate:nil]; | 723 [window_ setDelegate:nil]; |
644 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; | 724 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; |
645 native_widget_mac_->OnWindowWillClose(); | 725 native_widget_mac_->OnWindowWillClose(); |
646 } | 726 } |
647 | 727 |
648 void BridgedNativeWidget::OnFullscreenTransitionStart( | 728 void BridgedNativeWidget::OnFullscreenTransitionStart( |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
726 | 806 |
727 // 10.9 is unable to generate a window shadow from the composited CALayer, so | 807 // 10.9 is unable to generate a window shadow from the composited CALayer, so |
728 // use Quartz. | 808 // use Quartz. |
729 // We don't update the window mask during a live resize, instead it is done | 809 // We don't update the window mask during a live resize, instead it is done |
730 // after the resize is completed in viewDidEndLiveResize: in | 810 // after the resize is completed in viewDidEndLiveResize: in |
731 // BridgedContentView. | 811 // BridgedContentView. |
732 if (base::mac::IsOSMavericksOrEarlier() && ![window_ inLiveResize]) | 812 if (base::mac::IsOSMavericksOrEarlier() && ![window_ inLiveResize]) |
733 [bridged_view_ updateWindowMask]; | 813 [bridged_view_ updateWindowMask]; |
734 } | 814 } |
735 | 815 |
| 816 void BridgedNativeWidget::OnPositionChanged() { |
| 817 native_widget_mac_->GetWidget()->OnNativeWidgetMove(); |
| 818 } |
| 819 |
736 void BridgedNativeWidget::OnVisibilityChanged() { | 820 void BridgedNativeWidget::OnVisibilityChanged() { |
737 OnVisibilityChangedTo([window_ isVisible]); | 821 OnVisibilityChangedTo([window_ isVisible]); |
738 } | 822 } |
739 | 823 |
740 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { | 824 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { |
741 if (window_visible_ == new_visibility) | 825 if (window_visible_ == new_visibility) |
742 return; | 826 return; |
743 | 827 |
744 window_visible_ = new_visibility; | 828 window_visible_ = new_visibility; |
745 | 829 |
(...skipping 553 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1299 void BridgedNativeWidget::SetDraggable(bool draggable) { | 1383 void BridgedNativeWidget::SetDraggable(bool draggable) { |
1300 [bridged_view_ setMouseDownCanMoveWindow:draggable]; | 1384 [bridged_view_ setMouseDownCanMoveWindow:draggable]; |
1301 // AppKit will not update its cache of mouseDownCanMoveWindow unless something | 1385 // AppKit will not update its cache of mouseDownCanMoveWindow unless something |
1302 // changes. Previously we tried adding an NSView and removing it, but for some | 1386 // changes. Previously we tried adding an NSView and removing it, but for some |
1303 // reason it required reposting the mouse-down event, and didn't always work. | 1387 // reason it required reposting the mouse-down event, and didn't always work. |
1304 // Calling the below seems to be an effective solution. | 1388 // Calling the below seems to be an effective solution. |
1305 [window_ setMovableByWindowBackground:NO]; | 1389 [window_ setMovableByWindowBackground:NO]; |
1306 [window_ setMovableByWindowBackground:YES]; | 1390 [window_ setMovableByWindowBackground:YES]; |
1307 } | 1391 } |
1308 | 1392 |
| 1393 // static |
| 1394 void BridgedNativeWidget::IgnoreNextMouseDownForDraggableRegions() { |
| 1395 g_ignore_next_mouse_down_for_draggable_regions = true; |
| 1396 } |
| 1397 |
1309 } // namespace views | 1398 } // namespace views |
OLD | NEW |