| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/widget_owner_nswindow_adapter.h" | 5 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" |
| 6 | 6 |
| 7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/mac/sdk_forward_declarations.h" | 10 #include "base/mac/sdk_forward_declarations.h" |
| 11 #include "ui/gfx/geometry/rect.h" | 11 #include "ui/gfx/geometry/rect.h" |
| 12 #include "ui/gfx/geometry/vector2d.h" | 12 #include "ui/gfx/geometry/vector2d.h" |
| 13 #import "ui/gfx/mac/coordinate_conversion.h" | 13 #import "ui/gfx/mac/coordinate_conversion.h" |
| 14 #import "ui/views/cocoa/bridged_native_widget.h" | 14 #import "ui/views/cocoa/bridged_native_widget.h" |
| 15 | 15 |
| 16 // Bridges an AppKit observer to observe when the (non-views) NSWindow owning a | 16 // Bridges an AppKit observer to observe when the (non-views) NSWindow owning a |
| 17 // views::Widget will close. | 17 // views::Widget will close or change occlusion state. |
| 18 @interface WidgetOwnerNSWindowAdapterBridge : NSObject { | 18 @interface WidgetOwnerNSWindowAdapterBridge : NSObject { |
| 19 @private | 19 @private |
| 20 views::WidgetOwnerNSWindowAdapter* adapter_; // Weak. Owns us. | 20 views::WidgetOwnerNSWindowAdapter* adapter_; // Weak. Owns us. |
| 21 } | 21 } |
| 22 - (instancetype)initWithAdapter:(views::WidgetOwnerNSWindowAdapter*)adapter; | 22 - (instancetype)initWithAdapter:(views::WidgetOwnerNSWindowAdapter*)adapter; |
| 23 - (void)windowWillClose:(NSNotification*)notification; | 23 - (void)windowWillClose:(NSNotification*)notification; |
| 24 - (void)windowDidChangeOcclusionState:(NSNotification*)notification; |
| 24 @end | 25 @end |
| 25 | 26 |
| 26 @implementation WidgetOwnerNSWindowAdapterBridge | 27 @implementation WidgetOwnerNSWindowAdapterBridge |
| 27 | 28 |
| 28 - (instancetype)initWithAdapter:(views::WidgetOwnerNSWindowAdapter*)adapter { | 29 - (instancetype)initWithAdapter:(views::WidgetOwnerNSWindowAdapter*)adapter { |
| 29 if ((self = [super init])) | 30 if ((self = [super init])) |
| 30 adapter_ = adapter; | 31 adapter_ = adapter; |
| 31 return self; | 32 return self; |
| 32 } | 33 } |
| 33 | 34 |
| 34 - (void)windowWillClose:(NSNotification*)notification { | 35 - (void)windowWillClose:(NSNotification*)notification { |
| 35 adapter_->OnWindowWillClose(); | 36 adapter_->OnWindowWillClose(); |
| 36 } | 37 } |
| 37 | 38 |
| 39 - (void)windowDidChangeOcclusionState:(NSNotification*)notification { |
| 40 adapter_->OnWindowDidChangeOcclusionState(); |
| 41 } |
| 42 |
| 38 @end | 43 @end |
| 39 | 44 |
| 40 namespace views { | 45 namespace views { |
| 41 | 46 |
| 42 WidgetOwnerNSWindowAdapter::WidgetOwnerNSWindowAdapter( | 47 WidgetOwnerNSWindowAdapter::WidgetOwnerNSWindowAdapter( |
| 43 BridgedNativeWidget* child, | 48 BridgedNativeWidget* child, |
| 44 NSView* anchor_view) | 49 NSView* anchor_view) |
| 45 : child_(child), | 50 : child_(child), |
| 46 anchor_view_([anchor_view retain]), | 51 anchor_view_([anchor_view retain]), |
| 47 observer_bridge_( | 52 observer_bridge_( |
| 48 [[WidgetOwnerNSWindowAdapterBridge alloc] initWithAdapter:this]) { | 53 [[WidgetOwnerNSWindowAdapterBridge alloc] initWithAdapter:this]) { |
| 49 | 54 |
| 50 // Although the |anchor_view| must be in an NSWindow when the child dialog is | 55 // Although the |anchor_view| must be in an NSWindow when the child dialog is |
| 51 // created, it's permitted for the |anchor_view| to be removed from its view | 56 // created, it's permitted for the |anchor_view| to be removed from its view |
| 52 // hierarchy before the child dialog window is fully removed from screen. When | 57 // hierarchy before the child dialog window is fully removed from screen. When |
| 53 // this happens, [anchor_view_ window] will become nil, so retain both. | 58 // this happens, [anchor_view_ window] will become nil, so retain both. |
| 54 anchor_window_.reset([[anchor_view_ window] retain]); | 59 anchor_window_.reset([[anchor_view_ window] retain]); |
| 55 DCHECK(anchor_window_); | 60 DCHECK(anchor_window_); |
| 56 | 61 |
| 57 [[NSNotificationCenter defaultCenter] | 62 [[NSNotificationCenter defaultCenter] |
| 58 addObserver:observer_bridge_ | 63 addObserver:observer_bridge_ |
| 59 selector:@selector(windowWillClose:) | 64 selector:@selector(windowWillClose:) |
| 60 name:NSWindowWillCloseNotification | 65 name:NSWindowWillCloseNotification |
| 61 object:anchor_window_]; | 66 object:anchor_window_]; |
| 67 |
| 68 // BridgedNativeWidget removes NSWindow parent/child relationships for hidden |
| 69 // windows. Observe when the parent's visibility changes so they can be |
| 70 // reconnected. |
| 71 [[NSNotificationCenter defaultCenter] |
| 72 addObserver:observer_bridge_ |
| 73 selector:@selector(windowDidChangeOcclusionState:) |
| 74 name:NSWindowDidChangeOcclusionStateNotification |
| 75 object:anchor_window_]; |
| 62 } | 76 } |
| 63 | 77 |
| 64 void WidgetOwnerNSWindowAdapter::OnWindowWillClose() { | 78 void WidgetOwnerNSWindowAdapter::OnWindowWillClose() { |
| 65 // Retain the child window before closing it. If the last reference to the | 79 // Retain the child window before closing it. If the last reference to the |
| 66 // NSWindow goes away inside -[NSWindow close], then bad stuff can happen. | 80 // NSWindow goes away inside -[NSWindow close], then bad stuff can happen. |
| 67 // See e.g. http://crbug.com/616701. | 81 // See e.g. http://crbug.com/616701. |
| 68 base::scoped_nsobject<NSWindow> child_window(child_->ns_window(), | 82 base::scoped_nsobject<NSWindow> child_window(child_->ns_window(), |
| 69 base::scoped_policy::RETAIN); | 83 base::scoped_policy::RETAIN); |
| 70 | 84 |
| 71 // AppKit child window relationships break when the windows are not visible, | 85 // AppKit child window relationships break when the windows are not visible, |
| 72 // so if the child is not visible, it won't currently be a child. | 86 // so if the child is not visible, it won't currently be a child. |
| 73 DCHECK(![child_window isVisible] || [child_window parentWindow]); | 87 DCHECK(![child_window isVisible] || [child_window parentWindow]); |
| 74 DCHECK([child_window delegate]); | 88 DCHECK([child_window delegate]); |
| 75 | 89 |
| 76 [child_window close]; | 90 [child_window close]; |
| 77 // Note: |this| will be deleted here. | 91 // Note: |this| will be deleted here. |
| 78 | 92 |
| 79 DCHECK(![child_window parentWindow]); | 93 DCHECK(![child_window parentWindow]); |
| 80 DCHECK(![child_window delegate]); | 94 DCHECK(![child_window delegate]); |
| 81 } | 95 } |
| 82 | 96 |
| 97 void WidgetOwnerNSWindowAdapter::OnWindowDidChangeOcclusionState() { |
| 98 // The adapter only needs to handle a parent "show", since the only way it |
| 99 // should be hidden is via -[NSApp hide], and all BridgedNativeWidgets |
| 100 // subscribe to NSApplicationDidHideNotification already. |
| 101 if (![anchor_window_ isVisible]) |
| 102 return; |
| 103 |
| 104 if (child_->window_visible()) { |
| 105 DCHECK([child_->ns_window() parentWindow]); |
| 106 DCHECK(child_->wants_to_be_visible()); |
| 107 return; |
| 108 } |
| 109 |
| 110 // The parent relationship should have been removed when the child was hidden. |
| 111 DCHECK(![child_->ns_window() parentWindow]); |
| 112 if (!child_->wants_to_be_visible()) |
| 113 return; |
| 114 |
| 115 [child_->ns_window() orderWindow:NSWindowAbove |
| 116 relativeTo:[anchor_window_ windowNumber]]; |
| 117 |
| 118 // Ordering the window should add back the relationship. |
| 119 DCHECK([child_->ns_window() parentWindow]); |
| 120 DCHECK(child_->window_visible()); |
| 121 } |
| 122 |
| 83 NSWindow* WidgetOwnerNSWindowAdapter::GetNSWindow() { | 123 NSWindow* WidgetOwnerNSWindowAdapter::GetNSWindow() { |
| 84 return anchor_window_; | 124 return anchor_window_; |
| 85 } | 125 } |
| 86 | 126 |
| 87 gfx::Vector2d WidgetOwnerNSWindowAdapter::GetChildWindowOffset() const { | 127 gfx::Vector2d WidgetOwnerNSWindowAdapter::GetChildWindowOffset() const { |
| 88 NSRect rect_in_window = | 128 NSRect rect_in_window = |
| 89 [anchor_view_ convertRect:[anchor_view_ bounds] toView:nil]; | 129 [anchor_view_ convertRect:[anchor_view_ bounds] toView:nil]; |
| 90 NSRect rect_in_screen = [anchor_window_ convertRectToScreen:rect_in_window]; | 130 NSRect rect_in_screen = [anchor_window_ convertRectToScreen:rect_in_window]; |
| 91 // Ensure we anchor off the top-left of |anchor_view_| (rect_in_screen.origin | 131 // Ensure we anchor off the top-left of |anchor_view_| (rect_in_screen.origin |
| 92 // is the bottom-left of the view). | 132 // is the bottom-left of the view). |
| (...skipping 10 matching lines...) Expand all Loading... |
| 103 DCHECK_EQ(child, child_); | 143 DCHECK_EQ(child, child_); |
| 104 [GetNSWindow() removeChildWindow:child->ns_window()]; | 144 [GetNSWindow() removeChildWindow:child->ns_window()]; |
| 105 delete this; | 145 delete this; |
| 106 } | 146 } |
| 107 | 147 |
| 108 WidgetOwnerNSWindowAdapter::~WidgetOwnerNSWindowAdapter() { | 148 WidgetOwnerNSWindowAdapter::~WidgetOwnerNSWindowAdapter() { |
| 109 [[NSNotificationCenter defaultCenter] removeObserver:observer_bridge_]; | 149 [[NSNotificationCenter defaultCenter] removeObserver:observer_bridge_]; |
| 110 } | 150 } |
| 111 | 151 |
| 112 } // namespace views | 152 } // namespace views |
| OLD | NEW |