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

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) {
tapted 2015/05/25 05:24:05 I guess this looks alright :p. I don't think clang
jackhou1 2015/05/25 06:26:39 Done.
171 return DoubleRepostEventIfHandledByWindow(
172 ns_event);
173 }];
174 }
175
80 } // namespace 176 } // namespace
81 177
82 namespace views { 178 namespace views {
83 179
84 // static 180 // static
85 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( 181 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize(
86 NSWindow* window, 182 NSWindow* window,
87 const gfx::Size& content_size) { 183 const gfx::Size& content_size) {
88 NSRect content_rect = 184 NSRect content_rect =
89 NSMakeRect(0, 0, content_size.width(), content_size.height()); 185 NSMakeRect(0, 0, content_size.width(), content_size.height());
90 NSRect frame_rect = [window frameRectForContentRect:content_rect]; 186 NSRect frame_rect = [window frameRectForContentRect:content_rect];
91 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); 187 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect));
92 } 188 }
93 189
94 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) 190 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
95 : native_widget_mac_(parent), 191 : native_widget_mac_(parent),
96 focus_manager_(nullptr), 192 focus_manager_(nullptr),
97 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). 193 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init().
98 parent_(nullptr), 194 parent_(nullptr),
99 target_fullscreen_state_(false), 195 target_fullscreen_state_(false),
100 in_fullscreen_transition_(false), 196 in_fullscreen_transition_(false),
101 window_visible_(false), 197 window_visible_(false),
102 wants_to_be_visible_(false) { 198 wants_to_be_visible_(false) {
199 SetupDragEventMonitor();
103 DCHECK(parent); 200 DCHECK(parent);
104 window_delegate_.reset( 201 window_delegate_.reset(
105 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); 202 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
106 } 203 }
107 204
108 BridgedNativeWidget::~BridgedNativeWidget() { 205 BridgedNativeWidget::~BridgedNativeWidget() {
109 RemoveOrDestroyChildren(); 206 RemoveOrDestroyChildren();
110 DCHECK(child_windows_.empty()); 207 DCHECK(child_windows_.empty());
111 SetFocusManager(NULL); 208 SetFocusManager(NULL);
112 SetRootView(NULL); 209 SetRootView(NULL);
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after
525 if (is_key) { 622 if (is_key) {
526 widget->OnNativeFocus(); 623 widget->OnNativeFocus();
527 widget->GetFocusManager()->RestoreFocusedView(); 624 widget->GetFocusManager()->RestoreFocusedView();
528 } else { 625 } else {
529 widget->OnNativeBlur(); 626 widget->OnNativeBlur();
530 widget->GetFocusManager()->StoreFocusedView(true); 627 widget->GetFocusManager()->StoreFocusedView(true);
531 } 628 }
532 } 629 }
533 } 630 }
534 631
632 bool BridgedNativeWidget::ShouldRepostPendingLeftMouseDown(
633 NSPoint location_in_window) {
634 if (!bridged_view_)
635 return false;
636
637 if ([bridged_view_ mouseDownCanMoveWindow]) {
638 // This is a re-post, the movement has already started, so we can make the
639 // window non-draggable again.
640 SetDraggable(false);
641 return false;
642 }
643
644 gfx::Point point(location_in_window.x,
645 NSHeight([window_ frame]) - location_in_window.y);
646 bool should_move_window =
647 native_widget_mac()->GetWidget()->GetNonClientComponent(point) ==
648 HTCAPTION;
649
650 if (!should_move_window)
651 return false;
652
653 // Make the window draggable, then return true to repost the event.
654 SetDraggable(true);
655 return true;
656 }
657
535 void BridgedNativeWidget::OnSizeConstraintsChanged() { 658 void BridgedNativeWidget::OnSizeConstraintsChanged() {
536 // Don't modify the size constraints or fullscreen collection behavior while 659 // Don't modify the size constraints or fullscreen collection behavior while
537 // in fullscreen or during a transition. OnFullscreenTransitionComplete will 660 // in fullscreen or during a transition. OnFullscreenTransitionComplete will
538 // reset these after leaving fullscreen. 661 // reset these after leaving fullscreen.
539 if (target_fullscreen_state_ || in_fullscreen_transition_) 662 if (target_fullscreen_state_ || in_fullscreen_transition_)
540 return; 663 return;
541 664
542 Widget* widget = native_widget_mac()->GetWidget(); 665 Widget* widget = native_widget_mac()->GetWidget();
543 gfx::Size min_size = widget->GetMinimumSize(); 666 gfx::Size min_size = widget->GetMinimumSize();
544 gfx::Size max_size = widget->GetMaximumSize(); 667 gfx::Size max_size = widget->GetMaximumSize();
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after
863 NSMutableDictionary* properties = objc_getAssociatedObject( 986 NSMutableDictionary* properties = objc_getAssociatedObject(
864 window_, &kWindowPropertiesKey); 987 window_, &kWindowPropertiesKey);
865 if (!properties) { 988 if (!properties) {
866 properties = [NSMutableDictionary dictionary]; 989 properties = [NSMutableDictionary dictionary];
867 objc_setAssociatedObject(window_, &kWindowPropertiesKey, 990 objc_setAssociatedObject(window_, &kWindowPropertiesKey,
868 properties, OBJC_ASSOCIATION_RETAIN); 991 properties, OBJC_ASSOCIATION_RETAIN);
869 } 992 }
870 return properties; 993 return properties;
871 } 994 }
872 995
996 void BridgedNativeWidget::SetDraggable(bool draggable) {
997 [bridged_view_ setMouseDownCanMoveWindow:draggable];
998 // AppKit will not update its cache of mouseDownCanMoveWindow unless something
999 // changes.
1000 base::scoped_nsobject<NSView> temp_view(
1001 [[NSView alloc] initWithFrame:[bridged_view_ bounds]]);
1002 [bridged_view_ addSubview:temp_view];
1003 [temp_view removeFromSuperview];
1004 }
1005
873 } // namespace views 1006 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698