| 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 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 162 if (can_resize_x && can_resize_y && | 162 if (can_resize_x && can_resize_y && |
| 163 (point.x < kResizeAreaCornerSize || | 163 (point.x < kResizeAreaCornerSize || |
| 164 point.x >= window_size.width - kResizeAreaCornerSize) && | 164 point.x >= window_size.width - kResizeAreaCornerSize) && |
| 165 (point.y < kResizeAreaCornerSize || | 165 (point.y < kResizeAreaCornerSize || |
| 166 point.y >= window_size.height - kResizeAreaCornerSize)) | 166 point.y >= window_size.height - kResizeAreaCornerSize)) |
| 167 return true; | 167 return true; |
| 168 | 168 |
| 169 return false; | 169 return false; |
| 170 } | 170 } |
| 171 | 171 |
| 172 // Routes the |ns_event| to the corresponding BridgedNativeWidget and queries |
| 173 // whether the event should be reposted. |
| 172 BOOL WindowWantsMouseDownReposted(NSEvent* ns_event) { | 174 BOOL WindowWantsMouseDownReposted(NSEvent* ns_event) { |
| 173 id delegate = [[ns_event window] delegate]; | 175 DCHECK(views::BridgedNativeWidget::ShouldUseDragEventMonitor()); |
| 174 return | 176 |
| 175 [delegate | 177 views::BridgedNativeWidget* bridge = |
| 176 respondsToSelector:@selector(shouldRepostPendingLeftMouseDown:)] && | 178 views::NativeWidgetMac::GetBridgeForNativeWindow([ns_event window]); |
| 177 [delegate shouldRepostPendingLeftMouseDown:[ns_event locationInWindow]]; | 179 return bridge && bridge->ShouldRepostPendingLeftMouseDown(ns_event); |
| 178 } | 180 } |
| 179 | 181 |
| 180 // Check if a mouse-down event should drag the window. If so, repost the event. | 182 // Check if a mouse-down event should drag the window. If so, repost the event. |
| 181 NSEvent* RepostEventIfHandledByWindow(NSEvent* ns_event) { | 183 NSEvent* RepostEventIfHandledByWindow(NSEvent* ns_event) { |
| 184 DCHECK(views::BridgedNativeWidget::ShouldUseDragEventMonitor()); |
| 185 |
| 182 enum RepostState { | 186 enum RepostState { |
| 183 // Nothing reposted: hit-test new mouse-downs to see if they need to be | 187 // Nothing reposted: hit-test new mouse-downs to see if they need to be |
| 184 // ignored and reposted after changing draggability. | 188 // ignored and reposted after changing draggability. |
| 185 NONE, | 189 NONE, |
| 186 // Expecting the next event to be the reposted event: let it go through. | 190 // Expecting the next event to be the reposted event: let it go through. |
| 187 EXPECTING_REPOST, | 191 EXPECTING_REPOST, |
| 188 // If, while reposting, another mousedown was received: when the reposted | 192 // If, while reposting, another mousedown was received: when the reposted |
| 189 // event is seen, ignore it. | 193 // event is seen, ignore it. |
| 190 REPOST_CANCELLED, | 194 REPOST_CANCELLED, |
| 191 }; | 195 }; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 | 250 |
| 247 // Support window caption/draggable regions. | 251 // Support window caption/draggable regions. |
| 248 // In AppKit, non-client regions are set by overriding | 252 // In AppKit, non-client regions are set by overriding |
| 249 // -[NSView mouseDownCanMoveWindow]. NSApplication caches this area as views are | 253 // -[NSView mouseDownCanMoveWindow]. NSApplication caches this area as views are |
| 250 // installed and performs window moving when mouse-downs land in the area. | 254 // installed and performs window moving when mouse-downs land in the area. |
| 251 // In Views, non-client regions are determined via hit-tests when the event | 255 // In Views, non-client regions are determined via hit-tests when the event |
| 252 // occurs. | 256 // occurs. |
| 253 // To bridge the two models, we monitor mouse-downs with | 257 // To bridge the two models, we monitor mouse-downs with |
| 254 // +[NSEvent addLocalMonitorForEventsMatchingMask:handler:]. This receives | 258 // +[NSEvent addLocalMonitorForEventsMatchingMask:handler:]. This receives |
| 255 // events after window dragging is handled, so for mouse-downs that land on a | 259 // events after window dragging is handled, so for mouse-downs that land on a |
| 256 // draggable point, we cancel the event and repost it at the CGSessionEventTap | 260 // draggable point, we cancel the event, make the window draggable and repost it |
| 257 // level so that window dragging will be handled again. | 261 // at the CGSessionEventTap level so that window dragging will be handled again. |
| 262 // On Mac OS > 10.10, we don't use an event monitor. Instead, we use [NSWindow |
| 263 // performWindowDragWithEvent:]. See [NativeWidgetMacNSWindow sendEvent:]. |
| 258 void SetupDragEventMonitor() { | 264 void SetupDragEventMonitor() { |
| 265 DCHECK(views::BridgedNativeWidget::ShouldUseDragEventMonitor()); |
| 259 static id monitor = nil; | 266 static id monitor = nil; |
| 260 if (monitor) | 267 if (monitor) |
| 261 return; | 268 return; |
| 262 | 269 |
| 263 monitor = [NSEvent | 270 monitor = [NSEvent |
| 264 addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask | 271 addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask |
| 265 handler:^NSEvent*(NSEvent* ns_event) { | 272 handler:^NSEvent*(NSEvent* ns_event) { |
| 266 return RepostEventIfHandledByWindow(ns_event); | 273 return RepostEventIfHandledByWindow(ns_event); |
| 267 }]; | 274 }]; |
| 268 } | 275 } |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 331 // static | 338 // static |
| 332 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( | 339 gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( |
| 333 NSWindow* window, | 340 NSWindow* window, |
| 334 const gfx::Size& content_size) { | 341 const gfx::Size& content_size) { |
| 335 NSRect content_rect = | 342 NSRect content_rect = |
| 336 NSMakeRect(0, 0, content_size.width(), content_size.height()); | 343 NSMakeRect(0, 0, content_size.width(), content_size.height()); |
| 337 NSRect frame_rect = [window frameRectForContentRect:content_rect]; | 344 NSRect frame_rect = [window frameRectForContentRect:content_rect]; |
| 338 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); | 345 return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); |
| 339 } | 346 } |
| 340 | 347 |
| 348 // static |
| 349 // TODO(karandeepb): Remove usage of drag event monitor once we stop supporting |
| 350 // Mac OS 10.10. |
| 351 bool BridgedNativeWidget::ShouldUseDragEventMonitor() { |
| 352 return ![NSWindow |
| 353 instancesRespondToSelector:@selector(performWindowDragWithEvent:)]; |
| 354 } |
| 355 |
| 341 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) | 356 BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) |
| 342 : native_widget_mac_(parent), | 357 : native_widget_mac_(parent), |
| 343 focus_manager_(nullptr), | 358 focus_manager_(nullptr), |
| 344 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). | 359 widget_type_(Widget::InitParams::TYPE_WINDOW), // Updated in Init(). |
| 345 parent_(nullptr), | 360 parent_(nullptr), |
| 346 target_fullscreen_state_(false), | 361 target_fullscreen_state_(false), |
| 347 in_fullscreen_transition_(false), | 362 in_fullscreen_transition_(false), |
| 348 window_visible_(false), | 363 window_visible_(false), |
| 349 wants_to_be_visible_(false) { | 364 wants_to_be_visible_(false) { |
| 350 SetupDragEventMonitor(); | 365 if (BridgedNativeWidget::ShouldUseDragEventMonitor()) |
| 366 SetupDragEventMonitor(); |
| 367 |
| 351 DCHECK(parent); | 368 DCHECK(parent); |
| 352 window_delegate_.reset( | 369 window_delegate_.reset( |
| 353 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); | 370 [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); |
| 354 } | 371 } |
| 355 | 372 |
| 356 BridgedNativeWidget::~BridgedNativeWidget() { | 373 BridgedNativeWidget::~BridgedNativeWidget() { |
| 357 // The delegate should be cleared already. Note this enforces the precondition | 374 // The delegate should be cleared already. Note this enforces the precondition |
| 358 // that -[NSWindow close] is invoked on the hosted window before the | 375 // that -[NSWindow close] is invoked on the hosted window before the |
| 359 // destructor is called. | 376 // destructor is called. |
| 360 DCHECK(![window_ delegate]); | 377 DCHECK(![window_ delegate]); |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 554 | 571 |
| 555 if (native_widget_mac_->IsWindowModalSheet()) { | 572 if (native_widget_mac_->IsWindowModalSheet()) { |
| 556 ShowAsModalSheet(); | 573 ShowAsModalSheet(); |
| 557 return; | 574 return; |
| 558 } | 575 } |
| 559 | 576 |
| 560 // Non-modal windows are not animated. Hence opaque non-modal windows can | 577 // Non-modal windows are not animated. Hence opaque non-modal windows can |
| 561 // appear with a "flash" if they are made visible before the frame from the | 578 // appear with a "flash" if they are made visible before the frame from the |
| 562 // compositor arrives. To get around this, set the alpha value of the window | 579 // compositor arrives. To get around this, set the alpha value of the window |
| 563 // to 0, till we receive the correct frame from the compositor. Also, ignore | 580 // to 0, till we receive the correct frame from the compositor. Also, ignore |
| 564 // mouse clicks till then. | 581 // mouse clicks till then. Also check for an active task runner on the |
| 582 // WindowResizeHelperMac instance to ensure visibility is only suppressed when |
| 583 // there is an active GPU process. |
| 565 // TODO(karandeepb): Investigate whether similar technique is needed for other | 584 // TODO(karandeepb): Investigate whether similar technique is needed for other |
| 566 // dialog types. | 585 // dialog types. |
| 567 if (layer() && [window_ isOpaque] && !window_visible_ && | 586 if (layer() && [window_ isOpaque] && !window_visible_ && |
| 568 !native_widget_mac_->GetWidget()->IsModal()) { | 587 !native_widget_mac_->GetWidget()->IsModal() && |
| 588 ui::WindowResizeHelperMac::Get()->task_runner()) { |
| 569 initial_visibility_suppressed_ = true; | 589 initial_visibility_suppressed_ = true; |
| 570 [window_ setAlphaValue:0.0]; | 590 [window_ setAlphaValue:0.0]; |
| 571 [window_ setIgnoresMouseEvents:YES]; | 591 [window_ setIgnoresMouseEvents:YES]; |
| 572 } | 592 } |
| 573 | 593 |
| 574 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { | 594 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { |
| 575 [window_ makeKeyAndOrderFront:nil]; | 595 [window_ makeKeyAndOrderFront:nil]; |
| 576 [NSApp activateIgnoringOtherApps:YES]; | 596 [NSApp activateIgnoringOtherApps:YES]; |
| 577 } else { | 597 } else { |
| 578 // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a | 598 // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 869 // window status. | 889 // window status. |
| 870 [bridged_view_ updateFullKeyboardAccess]; | 890 [bridged_view_ updateFullKeyboardAccess]; |
| 871 widget->GetFocusManager()->RestoreFocusedView(); | 891 widget->GetFocusManager()->RestoreFocusedView(); |
| 872 } else { | 892 } else { |
| 873 widget->OnNativeBlur(); | 893 widget->OnNativeBlur(); |
| 874 widget->GetFocusManager()->StoreFocusedView(true); | 894 widget->GetFocusManager()->StoreFocusedView(true); |
| 875 } | 895 } |
| 876 } | 896 } |
| 877 } | 897 } |
| 878 | 898 |
| 879 bool BridgedNativeWidget::ShouldRepostPendingLeftMouseDown( | 899 bool BridgedNativeWidget::ShouldDragWindow(NSEvent* event) { |
| 880 NSPoint location_in_window) { | 900 if (!bridged_view_ || [event type] != NSLeftMouseDown) |
| 881 if (!bridged_view_) | |
| 882 return false; | 901 return false; |
| 883 | 902 |
| 884 if ([bridged_view_ mouseDownCanMoveWindow]) { | 903 NSPoint location_in_window = [event locationInWindow]; |
| 885 // This is a re-post, the movement has already started, so we can make the | |
| 886 // window non-draggable again. | |
| 887 SetDraggable(false); | |
| 888 return false; | |
| 889 } | |
| 890 | |
| 891 if (IsPointInResizeArea(location_in_window, window_)) | 904 if (IsPointInResizeArea(location_in_window, window_)) |
| 892 return false; | 905 return false; |
| 893 | 906 |
| 894 gfx::Point point(location_in_window.x, | 907 gfx::Point point(location_in_window.x, |
| 895 NSHeight([window_ frame]) - location_in_window.y); | 908 NSHeight([window_ frame]) - location_in_window.y); |
| 896 bool should_move_window = | 909 |
| 897 native_widget_mac()->GetWidget()->GetNonClientComponent(point) == | 910 if (native_widget_mac()->GetWidget()->GetNonClientComponent(point) != |
| 898 HTCAPTION; | 911 HTCAPTION) |
| 912 return false; |
| 899 | 913 |
| 900 // Check that the point is not obscured by non-content NSViews. | 914 // Check that the point is not obscured by non-content NSViews. |
| 901 for (NSView* subview : [[bridged_view_ superview] subviews]) { | 915 for (NSView* subview : [[bridged_view_ superview] subviews]) { |
| 902 if (subview == bridged_view_.get()) | 916 if (subview == bridged_view_.get()) |
| 903 continue; | 917 continue; |
| 904 | 918 |
| 905 if (![subview mouseDownCanMoveWindow] && | 919 if (![subview mouseDownCanMoveWindow] && |
| 906 NSPointInRect(location_in_window, [subview frame])) { | 920 NSPointInRect(location_in_window, [subview frame])) |
| 907 should_move_window = false; | 921 return false; |
| 908 break; | |
| 909 } | |
| 910 } | 922 } |
| 911 | 923 |
| 912 if (!should_move_window) | 924 return true; |
| 925 } |
| 926 |
| 927 bool BridgedNativeWidget::ShouldRepostPendingLeftMouseDown(NSEvent* event) { |
| 928 DCHECK(BridgedNativeWidget::ShouldUseDragEventMonitor()); |
| 929 DCHECK_EQ(NSLeftMouseDown, [event type]); |
| 930 |
| 931 if (!bridged_view_) |
| 932 return false; |
| 933 |
| 934 if ([bridged_view_ mouseDownCanMoveWindow]) { |
| 935 // This is a re-post, the movement has already started, so we can make the |
| 936 // window non-draggable again. |
| 937 SetDraggable(false); |
| 938 return false; |
| 939 } |
| 940 |
| 941 if (!ShouldDragWindow(event)) |
| 913 return false; | 942 return false; |
| 914 | 943 |
| 915 // Make the window draggable, then return true to repost the event. | 944 // Make the window draggable, then return true to repost the event. |
| 916 SetDraggable(true); | 945 SetDraggable(true); |
| 917 return true; | 946 return true; |
| 918 } | 947 } |
| 919 | 948 |
| 920 void BridgedNativeWidget::OnSizeConstraintsChanged() { | 949 void BridgedNativeWidget::OnSizeConstraintsChanged() { |
| 921 // Don't modify the size constraints or fullscreen collection behavior while | 950 // Don't modify the size constraints or fullscreen collection behavior while |
| 922 // in fullscreen or during a transition. OnFullscreenTransitionComplete will | 951 // in fullscreen or during a transition. OnFullscreenTransitionComplete will |
| (...skipping 445 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1368 window_, &kWindowPropertiesKey); | 1397 window_, &kWindowPropertiesKey); |
| 1369 if (!properties) { | 1398 if (!properties) { |
| 1370 properties = [NSMutableDictionary dictionary]; | 1399 properties = [NSMutableDictionary dictionary]; |
| 1371 objc_setAssociatedObject(window_, &kWindowPropertiesKey, | 1400 objc_setAssociatedObject(window_, &kWindowPropertiesKey, |
| 1372 properties, OBJC_ASSOCIATION_RETAIN); | 1401 properties, OBJC_ASSOCIATION_RETAIN); |
| 1373 } | 1402 } |
| 1374 return properties; | 1403 return properties; |
| 1375 } | 1404 } |
| 1376 | 1405 |
| 1377 void BridgedNativeWidget::SetDraggable(bool draggable) { | 1406 void BridgedNativeWidget::SetDraggable(bool draggable) { |
| 1407 DCHECK(BridgedNativeWidget::ShouldUseDragEventMonitor()); |
| 1408 |
| 1378 [bridged_view_ setMouseDownCanMoveWindow:draggable]; | 1409 [bridged_view_ setMouseDownCanMoveWindow:draggable]; |
| 1379 // AppKit will not update its cache of mouseDownCanMoveWindow unless something | 1410 // AppKit will not update its cache of mouseDownCanMoveWindow unless something |
| 1380 // changes. Previously we tried adding an NSView and removing it, but for some | 1411 // changes. Previously we tried adding an NSView and removing it, but for some |
| 1381 // reason it required reposting the mouse-down event, and didn't always work. | 1412 // reason it required reposting the mouse-down event, and didn't always work. |
| 1382 // Calling the below seems to be an effective solution. | 1413 // Calling the below seems to be an effective solution. |
| 1383 [window_ setMovableByWindowBackground:NO]; | 1414 [window_ setMovableByWindowBackground:NO]; |
| 1384 [window_ setMovableByWindowBackground:YES]; | 1415 [window_ setMovableByWindowBackground:YES]; |
| 1385 } | 1416 } |
| 1386 | 1417 |
| 1387 } // namespace views | 1418 } // namespace views |
| OLD | NEW |