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 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #import "base/mac/foundation_util.h" | |
10 #include "base/mac/mac_util.h" | 11 #include "base/mac/mac_util.h" |
11 #import "base/mac/sdk_forward_declarations.h" | 12 #import "base/mac/sdk_forward_declarations.h" |
12 #include "base/thread_task_runner_handle.h" | 13 #include "base/thread_task_runner_handle.h" |
14 #include "ui/base/hit_test.h" | |
13 #include "ui/base/ime/input_method.h" | 15 #include "ui/base/ime/input_method.h" |
14 #include "ui/base/ime/input_method_factory.h" | 16 #include "ui/base/ime/input_method_factory.h" |
15 #include "ui/base/ui_base_switches_util.h" | 17 #include "ui/base/ui_base_switches_util.h" |
16 #include "ui/gfx/display.h" | 18 #include "ui/gfx/display.h" |
17 #include "ui/gfx/geometry/dip_util.h" | 19 #include "ui/gfx/geometry/dip_util.h" |
18 #import "ui/gfx/mac/coordinate_conversion.h" | 20 #import "ui/gfx/mac/coordinate_conversion.h" |
19 #import "ui/gfx/mac/nswindow_frame_controls.h" | 21 #import "ui/gfx/mac/nswindow_frame_controls.h" |
20 #include "ui/gfx/screen.h" | 22 #include "ui/gfx/screen.h" |
21 #import "ui/views/cocoa/bridged_content_view.h" | 23 #import "ui/views/cocoa/bridged_content_view.h" |
22 #import "ui/views/cocoa/cocoa_mouse_capture.h" | 24 #import "ui/views/cocoa/cocoa_mouse_capture.h" |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
70 gfx::Size GetClientSizeForWindowSize(NSWindow* window, | 72 gfx::Size GetClientSizeForWindowSize(NSWindow* window, |
71 const gfx::Size& window_size) { | 73 const gfx::Size& window_size) { |
72 NSRect frame_rect = | 74 NSRect frame_rect = |
73 NSMakeRect(0, 0, window_size.width(), window_size.height()); | 75 NSMakeRect(0, 0, window_size.width(), window_size.height()); |
74 // Note gfx::Size will prevent dimensions going negative. They are allowed to | 76 // Note gfx::Size will prevent dimensions going negative. They are allowed to |
75 // be zero at this point, because Widget::GetMinimumSize() may later increase | 77 // be zero at this point, because Widget::GetMinimumSize() may later increase |
76 // the size. | 78 // the size. |
77 return gfx::Size([window contentRectForFrameRect:frame_rect].size); | 79 return gfx::Size([window contentRectForFrameRect:frame_rect].size); |
78 } | 80 } |
79 | 81 |
82 BOOL WindowWantsMouseDownReposted(NSEvent* ns_event) { | |
83 id delegate = [[ns_event window] delegate]; | |
84 return | |
85 [delegate | |
86 respondsToSelector:@selector(shouldRepostPendingLeftMouseDown:)] && | |
87 [delegate shouldRepostPendingLeftMouseDown:[ns_event locationInWindow]]; | |
88 } | |
89 | |
90 // Check if a mouse-down event should drag the window. If so, repost the event. | |
91 NSEvent* RepostEventIfHandledByWindow(NSEvent* ns_event) { | |
92 // Which repost we're expecting to receive. | |
93 static bool expecting_repost = false; | |
94 // The event number of the reposted event. This let's us track whether an | |
95 // event is actually a repost since user-generated events have increasing | |
96 // event numbers. | |
97 static NSInteger reposted_event_number = -1; | |
tapted
2015/06/03 00:28:37
did we want to try this out with 0 instead of -1?
jackhou1
2015/06/03 04:04:44
Done.
| |
98 | |
99 CGEventRef cg_event = [ns_event CGEvent]; | |
100 NSInteger event_number = [ns_event eventNumber]; | |
101 | |
102 // The logic here is a bit convoluted because we want to mitigate race | |
103 // conditions if somehow a different mouse-down occurs between reposts. | |
104 // Specifically, we want to avoid: | |
105 // - BridgedNativeWidget's draggability getting out of sync (e.g. it's | |
tapted
2015/06/03 00:28:37
nit: it's -> its
jackhou1
2015/06/03 04:04:44
This is actually an it's. Changed to "if it is".
tapted
2015/06/03 07:04:48
whoops! I must have read as "its draggability outs
| |
106 // draggable outside of a repost cycle), | |
107 // - any repost loop. | |
108 if (reposted_event_number == event_number) { | |
109 // This is a reposted event. | |
110 if (expecting_repost) { | |
111 expecting_repost = false; | |
112 reposted_event_number = -1; | |
113 // This is the final repost, call through to make the window | |
114 // non-draggable. | |
115 WindowWantsMouseDownReposted(ns_event); | |
116 } else { | |
117 // Reposting was cancelled, now that we've received it, we don't expect | |
118 // to see it again. | |
119 reposted_event_number = -1; | |
120 } | |
121 return nil; // Ignore the event. | |
122 } | |
123 | |
124 // This is a new event. | |
125 if (expecting_repost) { | |
126 // We were expecting a repost, but since this is a new mouse-down, cancel | |
127 // reposting and allow event to continue as usual. | |
128 expecting_repost = false; | |
129 // Call through so that the window is made non-draggable again. | |
130 WindowWantsMouseDownReposted(ns_event); | |
131 return ns_event; | |
132 } | |
133 | |
134 // We're not in the middle of reposting, process this new event. | |
135 if (WindowWantsMouseDownReposted(ns_event)) { | |
136 expecting_repost = true; | |
137 reposted_event_number = [ns_event eventNumber]; | |
138 CGEventPost(kCGSessionEventTap, cg_event); | |
139 return nil; | |
140 } | |
141 | |
142 return ns_event; | |
143 } | |
144 | |
145 // Support window caption/draggable regions. | |
146 // In AppKit, non-client regions are set by overriding | |
147 // -[NSView mouseDownCanMoveWindow]. NSApplication caches this area as views are | |
148 // installed and performs window moving when mouse-downs land in the area. | |
149 // In Views, non-client regions are determined via hit-tests when the event | |
150 // occurs. | |
151 // To bridge the two models, we monitor mouse-downs with | |
152 // +[NSEvent addLocalMonitorForEventsMatchingMask:handler:]. This receives | |
153 // events after window dragging is handled, so for mouse-downs that land on a | |
154 // draggable point, we cancel the event and repost it at the CGSessionEventTap | |
155 // level so that window dragging will be handled again. | |
156 void SetupDragEventMonitor() { | |
157 static id monitor = nil; | |
158 if (monitor) | |
159 return; | |
160 | |
161 monitor = [NSEvent | |
162 addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask | |
163 handler:^NSEvent*(NSEvent* ns_event) { | |
164 return RepostEventIfHandledByWindow(ns_event); | |
165 }]; | |
166 } | |
167 | |
80 } // namespace | 168 } // namespace |
81 | 169 |
82 namespace views { | 170 namespace views { |
83 | 171 |
84 // static | 172 // static |
85 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( | 173 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( |
86 NSWindow* window, | 174 NSWindow* window, |
87 const gfx::Size& content_size) { | 175 const gfx::Size& content_size) { |
88 NSRect content_rect = | 176 NSRect content_rect = |
89 NSMakeRect(0, 0, content_size.width(), content_size.height()); | 177 NSMakeRect(0, 0, content_size.width(), content_size.height()); |
90 NSRect frame_rect = [window frameRectForContentRect:content_rect]; | 178 NSRect frame_rect = [window frameRectForContentRect:content_rect]; |
91 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); | 179 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); |
92 } | 180 } |
93 | 181 |
94 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) | 182 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) |
95 : native_widget_mac_(parent), | 183 : native_widget_mac_(parent), |
96 focus_manager_(nullptr), | 184 focus_manager_(nullptr), |
97 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). | 185 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). |
98 parent_(nullptr), | 186 parent_(nullptr), |
99 target_fullscreen_state_(false), | 187 target_fullscreen_state_(false), |
100 in_fullscreen_transition_(false), | 188 in_fullscreen_transition_(false), |
101 window_visible_(false), | 189 window_visible_(false), |
102 wants_to_be_visible_(false) { | 190 wants_to_be_visible_(false) { |
191 SetupDragEventMonitor(); | |
103 DCHECK(parent); | 192 DCHECK(parent); |
104 window_delegate_.reset( | 193 window_delegate_.reset( |
105 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); | 194 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); |
106 } | 195 } |
107 | 196 |
108 BridgedNativeWidget::~BridgedNativeWidget() { | 197 BridgedNativeWidget::~BridgedNativeWidget() { |
109 RemoveOrDestroyChildren(); | 198 RemoveOrDestroyChildren(); |
110 DCHECK(child_windows_.empty()); | 199 DCHECK(child_windows_.empty()); |
111 SetFocusManager(NULL); | 200 SetFocusManager(NULL); |
112 SetRootView(NULL); | 201 SetRootView(NULL); |
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
525 if (is_key) { | 614 if (is_key) { |
526 widget->OnNativeFocus(); | 615 widget->OnNativeFocus(); |
527 widget->GetFocusManager()->RestoreFocusedView(); | 616 widget->GetFocusManager()->RestoreFocusedView(); |
528 } else { | 617 } else { |
529 widget->OnNativeBlur(); | 618 widget->OnNativeBlur(); |
530 widget->GetFocusManager()->StoreFocusedView(true); | 619 widget->GetFocusManager()->StoreFocusedView(true); |
531 } | 620 } |
532 } | 621 } |
533 } | 622 } |
534 | 623 |
624 bool BridgedNativeWidget::ShouldRepostPendingLeftMouseDown( | |
625 NSPoint location_in_window) { | |
626 if (!bridged_view_) | |
627 return false; | |
628 | |
629 if ([bridged_view_ mouseDownCanMoveWindow]) { | |
630 // This is a re-post, the movement has already started, so we can make the | |
631 // window non-draggable again. | |
632 SetDraggable(false); | |
633 return false; | |
634 } | |
635 | |
636 gfx::Point point(location_in_window.x, | |
637 NSHeight([window_ frame]) - location_in_window.y); | |
638 bool should_move_window = | |
639 native_widget_mac()->GetWidget()->GetNonClientComponent(point) == | |
640 HTCAPTION; | |
641 | |
642 if (!should_move_window) | |
643 return false; | |
644 | |
645 // Make the window draggable, then return true to repost the event. | |
646 SetDraggable(true); | |
647 return true; | |
648 } | |
649 | |
535 void BridgedNativeWidget::OnSizeConstraintsChanged() { | 650 void BridgedNativeWidget::OnSizeConstraintsChanged() { |
536 // Don't modify the size constraints or fullscreen collection behavior while | 651 // Don't modify the size constraints or fullscreen collection behavior while |
537 // in fullscreen or during a transition. OnFullscreenTransitionComplete will | 652 // in fullscreen or during a transition. OnFullscreenTransitionComplete will |
538 // reset these after leaving fullscreen. | 653 // reset these after leaving fullscreen. |
539 if (target_fullscreen_state_ || in_fullscreen_transition_) | 654 if (target_fullscreen_state_ || in_fullscreen_transition_) |
540 return; | 655 return; |
541 | 656 |
542 Widget* widget = native_widget_mac()->GetWidget(); | 657 Widget* widget = native_widget_mac()->GetWidget(); |
543 gfx::Size min_size = widget->GetMinimumSize(); | 658 gfx::Size min_size = widget->GetMinimumSize(); |
544 gfx::Size max_size = widget->GetMaximumSize(); | 659 gfx::Size max_size = widget->GetMaximumSize(); |
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
863 NSMutableDictionary* properties = objc_getAssociatedObject( | 978 NSMutableDictionary* properties = objc_getAssociatedObject( |
864 window_, &kWindowPropertiesKey); | 979 window_, &kWindowPropertiesKey); |
865 if (!properties) { | 980 if (!properties) { |
866 properties = [NSMutableDictionary dictionary]; | 981 properties = [NSMutableDictionary dictionary]; |
867 objc_setAssociatedObject(window_, &kWindowPropertiesKey, | 982 objc_setAssociatedObject(window_, &kWindowPropertiesKey, |
868 properties, OBJC_ASSOCIATION_RETAIN); | 983 properties, OBJC_ASSOCIATION_RETAIN); |
869 } | 984 } |
870 return properties; | 985 return properties; |
871 } | 986 } |
872 | 987 |
988 void BridgedNativeWidget::SetDraggable(bool draggable) { | |
989 [bridged_view_ setMouseDownCanMoveWindow:draggable]; | |
990 // AppKit will not update its cache of mouseDownCanMoveWindow unless something | |
991 // changes. Previously we tried adding an NSView and removing it, but for some | |
992 // reason it required reposting the mouse-down event, and didn't always work. | |
993 // Calling the below seems to be an effective solution. | |
994 [window_ setMovableByWindowBackground:NO]; | |
jackhou1
2015/06/02 06:33:09
I'm not sure exactly why it wasn't working for bro
tapted
2015/06/03 00:28:37
Nice!
| |
995 [window_ setMovableByWindowBackground:YES]; | |
996 } | |
997 | |
873 } // namespace views | 998 } // namespace views |
OLD | NEW |