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

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

Issue 1146873002: [MacViews] Enable dragging a window by its caption/draggable areas. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address comments Created 5 years, 7 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 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
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 // twice. It doesn't seem to work the first time it's reposted.
92 NSEvent* DoubleRepostEventIfHandledByWindow(NSEvent* ns_event) {
93 // Which repost we're expecting to receive.
94 static int expected_repost_count = 0;
95 // The event number of the reposted event. This let's us track whether an
96 // event is actually a repost since user-generated events have increasing
97 // event numbers.
98 static NSInteger reposted_event_number = -1;
99
100 CGEventRef cg_event = [ns_event CGEvent];
101 NSInteger event_number = [ns_event eventNumber];
102
103 // The logic here is a bit convoluted because we want to mitigate race
104 // conditions if somehow a different mousedown occurs between reposts.
105 // Specifically, we want to avoid:
106 // - BridgedNativeWidget's draggability getting out of sync (e.g. it's
107 // draggable outside of a repost cycle),
108 // - any repost loop.
109 if (reposted_event_number == event_number) {
110 // This is a reposted event.
111 if (expected_repost_count == 0) {
112 // Reposting was cancelled, now that we've received it, we don't expect
113 // to see it again.
114 reposted_event_number = -1;
115 } else if (expected_repost_count == 1) {
116 // There has been no intermediate mouse-down, repost the event again.
117 expected_repost_count = 2;
118 CGEventPost(kCGSessionEventTap, cg_event);
119 } else if (expected_repost_count == 2) {
120 expected_repost_count = 0;
121 reposted_event_number = -1;
122 // This is the final repost, call through to make the window
123 // non-draggable.
124 WindowWantsMouseDownReposted(ns_event);
125 } else {
126 NOTREACHED();
127 }
128 return nil; // Ignore the event.
129 }
130
131 // This is a new event.
132 if (expected_repost_count > 0) {
133 // We were expecting a repost, but since this is a new mouse-down, cancel
134 // reposting and allow event to continue as usual.
135 expected_repost_count = 0;
136 // Call through so that the window is made non-draggable again.
137 WindowWantsMouseDownReposted(ns_event);
138 return ns_event;
139 }
140
141 // We're not in the middle of reposting, process this new event.
142 if (WindowWantsMouseDownReposted(ns_event)) {
143 expected_repost_count = 1;
144 reposted_event_number = [ns_event eventNumber];
145 CGEventPost(kCGSessionEventTap, cg_event);
146 return nil;
147 }
148
149 return ns_event;
150 }
151
152 // Support window caption/draggable regions.
153 // In AppKit, non-client regions are set by overriding
154 // -[NSView mouseDownCanMoveWindow]. NSApplication caches this area as views are
155 // installed and performs window moving when mouse-downs land in the area.
156 // In Views, non-client regions are determined via hit-tests when the event
157 // occurs.
158 // To bridge the two models, we monitor mouse-downs with
159 // +[NSEvent addLocalMonitorForEventsMatchingMask:handler:]. This receives
160 // events after window dragging is handled, so for mouse-downs that land on a
161 // draggable point, we cancel the event and repost it at the CGSessionEventTap
162 // level so that window dragging will be handled again.
163 void SetupDragEventMonitor() {
164 static id monitor = nil;
165 if (monitor)
166 return;
167
168 monitor = [NSEvent
169 addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask
170 handler:^NSEvent*(NSEvent* ns_event) {
171 return DoubleRepostEventIfHandledByWindow(ns_event);
172 }];
173 }
174
80 } // namespace 175 } // namespace
81 176
82 namespace views { 177 namespace views {
83 178
84 // static 179 // static
85 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( 180 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize(
86 NSWindow* window, 181 NSWindow* window,
87 const gfx::Size& content_size) { 182 const gfx::Size& content_size) {
88 NSRect content_rect = 183 NSRect content_rect =
89 NSMakeRect(0, 0, content_size.width(), content_size.height()); 184 NSMakeRect(0, 0, content_size.width(), content_size.height());
90 NSRect frame_rect = [window frameRectForContentRect:content_rect]; 185 NSRect frame_rect = [window frameRectForContentRect:content_rect];
91 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); 186 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect));
92 } 187 }
93 188
94 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) 189 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
95 : native_widget_mac_(parent), 190 : native_widget_mac_(parent),
96 focus_manager_(nullptr), 191 focus_manager_(nullptr),
97 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). 192 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init().
98 parent_(nullptr), 193 parent_(nullptr),
99 target_fullscreen_state_(false), 194 target_fullscreen_state_(false),
100 in_fullscreen_transition_(false), 195 in_fullscreen_transition_(false),
101 window_visible_(false), 196 window_visible_(false),
102 wants_to_be_visible_(false) { 197 wants_to_be_visible_(false) {
198 SetupDragEventMonitor();
103 DCHECK(parent); 199 DCHECK(parent);
104 window_delegate_.reset( 200 window_delegate_.reset(
105 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); 201 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
106 } 202 }
107 203
108 BridgedNativeWidget::~BridgedNativeWidget() { 204 BridgedNativeWidget::~BridgedNativeWidget() {
109 RemoveOrDestroyChildren(); 205 RemoveOrDestroyChildren();
110 DCHECK(child_windows_.empty()); 206 DCHECK(child_windows_.empty());
111 SetFocusManager(NULL); 207 SetFocusManager(NULL);
112 SetRootView(NULL); 208 SetRootView(NULL);
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after
525 if (is_key) { 621 if (is_key) {
526 widget->OnNativeFocus(); 622 widget->OnNativeFocus();
527 widget->GetFocusManager()->RestoreFocusedView(); 623 widget->GetFocusManager()->RestoreFocusedView();
528 } else { 624 } else {
529 widget->OnNativeBlur(); 625 widget->OnNativeBlur();
530 widget->GetFocusManager()->StoreFocusedView(true); 626 widget->GetFocusManager()->StoreFocusedView(true);
531 } 627 }
532 } 628 }
533 } 629 }
534 630
631 bool BridgedNativeWidget::ShouldRepostPendingLeftMouseDown(
632 NSPoint location_in_window) {
633 if (!bridged_view_)
634 return false;
635
636 if ([bridged_view_ mouseDownCanMoveWindow]) {
637 // This is a re-post, the movement has already started, so we can make the
638 // window non-draggable again.
639 SetDraggable(false);
640 return false;
641 }
642
643 gfx::Point point(location_in_window.x,
644 NSHeight([window_ frame]) - location_in_window.y);
645 bool should_move_window =
646 native_widget_mac()->GetWidget()->GetNonClientComponent(point) ==
647 HTCAPTION;
648
649 if (!should_move_window)
650 return false;
651
652 // Make the window draggable, then return true to repost the event.
653 SetDraggable(true);
654 return true;
655 }
656
535 void BridgedNativeWidget::OnSizeConstraintsChanged() { 657 void BridgedNativeWidget::OnSizeConstraintsChanged() {
536 // Don't modify the size constraints or fullscreen collection behavior while 658 // Don't modify the size constraints or fullscreen collection behavior while
537 // in fullscreen or during a transition. OnFullscreenTransitionComplete will 659 // in fullscreen or during a transition. OnFullscreenTransitionComplete will
538 // reset these after leaving fullscreen. 660 // reset these after leaving fullscreen.
539 if (target_fullscreen_state_ || in_fullscreen_transition_) 661 if (target_fullscreen_state_ || in_fullscreen_transition_)
540 return; 662 return;
541 663
542 Widget* widget = native_widget_mac()->GetWidget(); 664 Widget* widget = native_widget_mac()->GetWidget();
543 gfx::Size min_size = widget->GetMinimumSize(); 665 gfx::Size min_size = widget->GetMinimumSize();
544 gfx::Size max_size = widget->GetMaximumSize(); 666 gfx::Size max_size = widget->GetMaximumSize();
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after
863 NSMutableDictionary* properties = objc_getAssociatedObject( 985 NSMutableDictionary* properties = objc_getAssociatedObject(
864 window_, &kWindowPropertiesKey); 986 window_, &kWindowPropertiesKey);
865 if (!properties) { 987 if (!properties) {
866 properties = [NSMutableDictionary dictionary]; 988 properties = [NSMutableDictionary dictionary];
867 objc_setAssociatedObject(window_, &kWindowPropertiesKey, 989 objc_setAssociatedObject(window_, &kWindowPropertiesKey,
868 properties, OBJC_ASSOCIATION_RETAIN); 990 properties, OBJC_ASSOCIATION_RETAIN);
869 } 991 }
870 return properties; 992 return properties;
871 } 993 }
872 994
995 void BridgedNativeWidget::SetDraggable(bool draggable) {
996 [bridged_view_ setMouseDownCanMoveWindow:draggable];
997 // AppKit will not update its cache of mouseDownCanMoveWindow unless something
998 // changes.
999 base::scoped_nsobject<NSView> temp_view(
1000 [[NSView alloc] initWithFrame:[bridged_view_ bounds]]);
1001 [bridged_view_ addSubview:temp_view];
1002 [temp_view removeFromSuperview];
1003 }
1004
873 } // namespace views 1005 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698