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/cocoa_mouse_capture.h" | 23 #import "ui/views/cocoa/cocoa_mouse_capture.h" |
22 #import "ui/views/cocoa/bridged_content_view.h" | 24 #import "ui/views/cocoa/bridged_content_view.h" |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
69 gfx::Size GetClientSizeForWindowSize(NSWindow* window, | 71 gfx::Size GetClientSizeForWindowSize(NSWindow* window, |
70 const gfx::Size& window_size) { | 72 const gfx::Size& window_size) { |
71 NSRect frame_rect = | 73 NSRect frame_rect = |
72 NSMakeRect(0, 0, window_size.width(), window_size.height()); | 74 NSMakeRect(0, 0, window_size.width(), window_size.height()); |
73 // Note gfx::Size will prevent dimensions going negative. They are allowed to | 75 // Note gfx::Size will prevent dimensions going negative. They are allowed to |
74 // be zero at this point, because Widget::GetMinimumSize() may later increase | 76 // be zero at this point, because Widget::GetMinimumSize() may later increase |
75 // the size. | 77 // the size. |
76 return gfx::Size([window contentRectForFrameRect:frame_rect].size); | 78 return gfx::Size([window contentRectForFrameRect:frame_rect].size); |
77 } | 79 } |
78 | 80 |
81 BOOL WindowWantsMouseDownReposted(NSEvent* ns_event) { | |
82 id delegate = [[ns_event window] delegate]; | |
83 return | |
84 [delegate | |
85 respondsToSelector:@selector(shouldRepostPendingLeftMouseDown:)] && | |
86 [delegate shouldRepostPendingLeftMouseDown:[ns_event locationInWindow]]; | |
87 } | |
88 | |
89 // Check if a mouse-down event should drag the window. If so, repost the event | |
90 // twice. It doesn't seem to work the first time it's reposted. | |
91 NSEvent* DoubleRepostEventIfHandledByWindow(NSEvent* ns_event) { | |
92 // Which repost we're expecting to receive. | |
93 static int expected_repost_count = 0; | |
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; | |
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 mousedown occurs between reposts. | |
104 // Specifically, we want to avoid: | |
105 // - BridgedNativeWidget's draggability getting out of sync (e.g. it's | |
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 (expected_repost_count == 1) { | |
111 // There has been no intermediate mouse-down, repost the event again. | |
112 expected_repost_count = 2; | |
113 CGEventPost(kCGSessionEventTap, cg_event); | |
114 } else if (expected_repost_count == 2) { | |
115 expected_repost_count = 0; | |
116 // This is the final repost, call through to make the window | |
117 // non-draggable. | |
118 WindowWantsMouseDownReposted(ns_event); | |
119 } | |
tapted
2015/05/22 04:03:12
else { NOTREACHED(); }? (i.e. we should never see
jackhou1
2015/05/25 04:50:19
In the if (expected_repost_count > 0) block below,
| |
120 return nil; // Ignore the event. | |
121 } else { | |
tapted
2015/05/22 04:03:12
nit: no else after return
jackhou1
2015/05/25 04:50:19
Done.
| |
122 // This is a new event. | |
123 if (expected_repost_count > 0) { | |
124 // We were expecting a repost, but since this is a new mouse-down, cancel | |
125 // reposting and allow event to continue as usual. | |
126 expected_repost_count = 0; | |
127 // Call through so that the window is made non-draggable again. | |
128 WindowWantsMouseDownReposted(ns_event); | |
129 return ns_event; | |
130 } else { | |
tapted
2015/05/22 04:03:12
no else after return
jackhou1
2015/05/25 04:50:19
Done.
| |
131 // We're not in the middle of reposting, process this new event. | |
132 if (WindowWantsMouseDownReposted(ns_event)) { | |
133 expected_repost_count = 1; | |
134 reposted_event_number = [ns_event eventNumber]; | |
135 CGEventPost(kCGSessionEventTap, cg_event); | |
136 return nil; | |
137 } | |
138 return ns_event; | |
139 } | |
140 } | |
141 } | |
142 | |
143 // Support window caption/draggable regions. | |
144 // In AppKit, non-client regions are set by overriding | |
145 // -[NSView mouseDownCanMoveWindow]. NSApplication caches this area as views are | |
146 // installed and performs window moving when mouse-downs land in the area. | |
147 // In Views, non-client regions are determined via hit-tests when the event | |
148 // occurs. | |
149 // To bridge the two models, we monitor mouse-downs with | |
150 // +[NSEvent addLocalMonitorForEventsMatchingMask:handler:]. This receives | |
151 // events after window dragging is handled, so for mouse-downs that land on a | |
152 // draggable point, we cancel the event and repost it at the CGSessionEventTap | |
153 // level so that window dragging will be handled again. | |
154 void SetupDragEventMonitor() { | |
155 static id g_event_monitor = nil; | |
tapted
2015/05/22 04:03:12
nit: no `g_` for local statics. I'd maybe just cal
jackhou1
2015/05/25 04:50:19
Done.
| |
156 if (g_event_monitor) | |
157 return; | |
158 | |
159 NSEvent* (^monitor_callback)(NSEvent* ns_event); | |
tapted
2015/05/22 04:03:12
nit: move this into the function call (just to hid
jackhou1
2015/05/25 04:50:19
Done.
| |
160 monitor_callback = ^NSEvent*(NSEvent* ns_event) { | |
161 return DoubleRepostEventIfHandledByWindow(ns_event); | |
162 }; | |
163 | |
164 g_event_monitor = | |
165 [NSEvent addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask | |
166 handler:monitor_callback]; | |
167 } | |
168 | |
79 } // namespace | 169 } // namespace |
80 | 170 |
81 namespace views { | 171 namespace views { |
82 | 172 |
83 // static | 173 // static |
84 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( | 174 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( |
85 NSWindow* window, | 175 NSWindow* window, |
86 const gfx::Size& content_size) { | 176 const gfx::Size& content_size) { |
87 NSRect content_rect = | 177 NSRect content_rect = |
88 NSMakeRect(0, 0, content_size.width(), content_size.height()); | 178 NSMakeRect(0, 0, content_size.width(), content_size.height()); |
89 NSRect frame_rect = [window frameRectForContentRect:content_rect]; | 179 NSRect frame_rect = [window frameRectForContentRect:content_rect]; |
90 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); | 180 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); |
91 } | 181 } |
92 | 182 |
93 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) | 183 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) |
94 : native_widget_mac_(parent), | 184 : native_widget_mac_(parent), |
95 focus_manager_(nullptr), | 185 focus_manager_(nullptr), |
96 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). | 186 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). |
97 parent_(nullptr), | 187 parent_(nullptr), |
98 target_fullscreen_state_(false), | 188 target_fullscreen_state_(false), |
99 in_fullscreen_transition_(false), | 189 in_fullscreen_transition_(false), |
100 window_visible_(false), | 190 window_visible_(false), |
101 wants_to_be_visible_(false) { | 191 wants_to_be_visible_(false) { |
192 SetupDragEventMonitor(); | |
102 DCHECK(parent); | 193 DCHECK(parent); |
103 window_delegate_.reset( | 194 window_delegate_.reset( |
104 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); | 195 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); |
105 } | 196 } |
106 | 197 |
107 BridgedNativeWidget::~BridgedNativeWidget() { | 198 BridgedNativeWidget::~BridgedNativeWidget() { |
108 RemoveOrDestroyChildren(); | 199 RemoveOrDestroyChildren(); |
109 DCHECK(child_windows_.empty()); | 200 DCHECK(child_windows_.empty()); |
110 SetFocusManager(NULL); | 201 SetFocusManager(NULL); |
111 SetRootView(NULL); | 202 SetRootView(NULL); |
(...skipping 403 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
515 if (is_key) { | 606 if (is_key) { |
516 widget->OnNativeFocus(); | 607 widget->OnNativeFocus(); |
517 widget->GetFocusManager()->RestoreFocusedView(); | 608 widget->GetFocusManager()->RestoreFocusedView(); |
518 } else { | 609 } else { |
519 widget->OnNativeBlur(); | 610 widget->OnNativeBlur(); |
520 widget->GetFocusManager()->StoreFocusedView(true); | 611 widget->GetFocusManager()->StoreFocusedView(true); |
521 } | 612 } |
522 } | 613 } |
523 } | 614 } |
524 | 615 |
616 bool BridgedNativeWidget::ShouldRepostPendingLeftMouseDown( | |
617 NSPoint location_in_window) { | |
618 if (!bridged_view_) | |
619 return false; | |
620 | |
621 if ([bridged_view_ mouseDownCanMoveWindow]) { | |
622 // This is a re-post, the movement has already started, so we can make the | |
623 // window non-draggable again. | |
624 SetDraggable(false); | |
625 return false; | |
626 } | |
627 | |
628 gfx::Point point(location_in_window.x, | |
629 NSHeight([window_ frame]) - location_in_window.y); | |
630 bool should_move_window = | |
631 native_widget_mac()->GetWidget()->GetNonClientComponent(point) == | |
632 HTCAPTION; | |
633 | |
634 if (!should_move_window) | |
635 return false; | |
636 | |
637 // Make the window draggable, then return true to repost the event. | |
638 SetDraggable(true); | |
639 return true; | |
640 } | |
641 | |
525 void BridgedNativeWidget::OnSizeConstraintsChanged() { | 642 void BridgedNativeWidget::OnSizeConstraintsChanged() { |
526 NSWindow* window = ns_window(); | 643 NSWindow* window = ns_window(); |
527 Widget* widget = native_widget_mac()->GetWidget(); | 644 Widget* widget = native_widget_mac()->GetWidget(); |
528 gfx::Size min_size = widget->GetMinimumSize(); | 645 gfx::Size min_size = widget->GetMinimumSize(); |
529 gfx::Size max_size = widget->GetMaximumSize(); | 646 gfx::Size max_size = widget->GetMaximumSize(); |
530 bool is_resizable = widget->widget_delegate()->CanResize(); | 647 bool is_resizable = widget->widget_delegate()->CanResize(); |
531 bool shows_resize_controls = | 648 bool shows_resize_controls = |
532 is_resizable && (min_size.IsEmpty() || min_size != max_size); | 649 is_resizable && (min_size.IsEmpty() || min_size != max_size); |
533 bool shows_fullscreen_controls = | 650 bool shows_fullscreen_controls = |
534 is_resizable && widget->widget_delegate()->CanMaximize(); | 651 is_resizable && widget->widget_delegate()->CanMaximize(); |
(...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
848 NSMutableDictionary* properties = objc_getAssociatedObject( | 965 NSMutableDictionary* properties = objc_getAssociatedObject( |
849 window_, &kWindowPropertiesKey); | 966 window_, &kWindowPropertiesKey); |
850 if (!properties) { | 967 if (!properties) { |
851 properties = [NSMutableDictionary dictionary]; | 968 properties = [NSMutableDictionary dictionary]; |
852 objc_setAssociatedObject(window_, &kWindowPropertiesKey, | 969 objc_setAssociatedObject(window_, &kWindowPropertiesKey, |
853 properties, OBJC_ASSOCIATION_RETAIN); | 970 properties, OBJC_ASSOCIATION_RETAIN); |
854 } | 971 } |
855 return properties; | 972 return properties; |
856 } | 973 } |
857 | 974 |
975 void BridgedNativeWidget::SetDraggable(bool draggable) { | |
976 [bridged_view_ setMouseDownCanMoveWindow:draggable]; | |
977 // AppKit will not update it's cache of mouseDownCanMoveWindow unless | |
tapted
2015/05/22 04:03:12
nit: it's -> its
jackhou1
2015/05/25 04:50:19
Done.
| |
978 // something changes. | |
979 base::scoped_nsobject<NSView> temp_view( | |
980 [[NSView alloc] initWithFrame:[bridged_view_ bounds]]); | |
981 [bridged_view_ addSubview:temp_view]; | |
982 [temp_view removeFromSuperview]; | |
983 } | |
984 | |
858 } // namespace views | 985 } // namespace views |
OLD | NEW |