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 = 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 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) { | |
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 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" { | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |