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 #include "base/mac/mac_util.h" | 10 #include "base/mac/mac_util.h" |
11 #import "base/mac/sdk_forward_declarations.h" | 11 #import "base/mac/sdk_forward_declarations.h" |
12 #include "base/thread_task_runner_handle.h" | 12 #include "base/thread_task_runner_handle.h" |
13 #include "ui/base/ime/input_method.h" | 13 #include "ui/base/ime/input_method.h" |
14 #include "ui/base/ime/input_method_factory.h" | 14 #include "ui/base/ime/input_method_factory.h" |
15 #include "ui/base/ui_base_switches_util.h" | 15 #include "ui/base/ui_base_switches_util.h" |
16 #include "ui/gfx/display.h" | 16 #include "ui/gfx/display.h" |
17 #include "ui/gfx/geometry/dip_util.h" | 17 #include "ui/gfx/geometry/dip_util.h" |
18 #import "ui/gfx/mac/coordinate_conversion.h" | 18 #import "ui/gfx/mac/coordinate_conversion.h" |
19 #import "ui/gfx/mac/nswindow_frame_controls.h" | 19 #import "ui/gfx/mac/nswindow_frame_controls.h" |
20 #include "ui/gfx/screen.h" | 20 #include "ui/gfx/screen.h" |
21 #import "ui/views/cocoa/cocoa_mouse_capture.h" | 21 #import "ui/views/cocoa/cocoa_mouse_capture.h" |
22 #import "ui/views/cocoa/bridged_content_view.h" | 22 #import "ui/views/cocoa/bridged_content_view.h" |
23 #import "ui/views/cocoa/views_nswindow_delegate.h" | 23 #import "ui/views/cocoa/views_nswindow_delegate.h" |
24 #import "ui/views/cocoa/widget_owner_nswindow_adapter.h" | |
24 #include "ui/views/widget/native_widget_mac.h" | 25 #include "ui/views/widget/native_widget_mac.h" |
25 #include "ui/views/ime/input_method_bridge.h" | 26 #include "ui/views/ime/input_method_bridge.h" |
26 #include "ui/views/ime/null_input_method.h" | 27 #include "ui/views/ime/null_input_method.h" |
27 #include "ui/views/view.h" | 28 #include "ui/views/view.h" |
28 #include "ui/views/views_delegate.h" | 29 #include "ui/views/views_delegate.h" |
29 #include "ui/views/widget/widget.h" | 30 #include "ui/views/widget/widget.h" |
30 #include "ui/views/widget/widget_aura_utils.h" | 31 #include "ui/views/widget/widget_aura_utils.h" |
31 #include "ui/views/widget/widget_delegate.h" | 32 #include "ui/views/widget/widget_delegate.h" |
32 | 33 |
33 // The NSView that hosts the composited CALayer drawing the UI. It fills the | 34 // The NSView that hosts the composited CALayer drawing the UI. It fills the |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 object:nil]; | 149 object:nil]; |
149 | 150 |
150 // Validate the window's initial state, otherwise the bridge's initial | 151 // Validate the window's initial state, otherwise the bridge's initial |
151 // tracking state will be incorrect. | 152 // tracking state will be incorrect. |
152 DCHECK(![window_ isVisible]); | 153 DCHECK(![window_ isVisible]); |
153 DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask); | 154 DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask); |
154 | 155 |
155 if (params.parent) { | 156 if (params.parent) { |
156 // Disallow creating child windows of views not currently in an NSWindow. | 157 // Disallow creating child windows of views not currently in an NSWindow. |
157 CHECK([params.parent window]); | 158 CHECK([params.parent window]); |
158 BridgedNativeWidget* parent = | 159 BridgedNativeWidget* bridged_native_widget_parent = |
159 NativeWidgetMac::GetBridgeForNativeWindow([params.parent window]); | 160 NativeWidgetMac::GetBridgeForNativeWindow([params.parent window]); |
160 // The parent could be an NSWindow without an associated Widget. That could | 161 // If the parent is another BridgedNativeWidget, just add to the collection |
161 // work by observing NSWindowWillCloseNotification, but for now it's not | 162 // of child windows it owns and manages. Otherwise, create an adapter to |
162 // supported, and there might not be a use-case for that. | 163 // anchor the child widget and observe when the parent NSWindow is closed. |
163 CHECK(parent); | 164 if (bridged_native_widget_parent) { |
164 parent_ = parent; | 165 parent_ = bridged_native_widget_parent; |
165 parent->child_windows_.push_back(this); | 166 bridged_native_widget_parent->child_windows_.push_back(this); |
167 } else { | |
168 parent_ = new WidgetOwnerNSWindowAdapter(this, params.parent); | |
169 } | |
166 } | 170 } |
167 | 171 |
168 // Set a meaningful initial bounds. Note that except for frameless widgets | 172 // Set a meaningful initial bounds. Note that except for frameless widgets |
169 // with no WidgetDelegate, the bounds will be set again by Widget after | 173 // with no WidgetDelegate, the bounds will be set again by Widget after |
170 // initializing the non-client view. In the former case, if bounds were not | 174 // initializing the non-client view. In the former case, if bounds were not |
171 // set at all, the creator of the Widget is expected to call SetBounds() | 175 // set at all, the creator of the Widget is expected to call SetBounds() |
172 // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized | 176 // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized |
173 // (i.e. 1x1) window appearing. | 177 // (i.e. 1x1) window appearing. |
174 if (!params.bounds.IsEmpty()) { | 178 if (!params.bounds.IsEmpty()) { |
175 SetBounds(params.bounds); | 179 SetBounds(params.bounds); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
226 // the first time. They also have no frame, so just update the content size. | 230 // the first time. They also have no frame, so just update the content size. |
227 [window_ setContentSize:NSMakeSize(clamped_content_size.width(), | 231 [window_ setContentSize:NSMakeSize(clamped_content_size.width(), |
228 clamped_content_size.height())]; | 232 clamped_content_size.height())]; |
229 return; | 233 return; |
230 } | 234 } |
231 gfx::Rect actual_new_bounds( | 235 gfx::Rect actual_new_bounds( |
232 new_bounds.origin(), | 236 new_bounds.origin(), |
233 GetWindowSizeForClientSize(window_, clamped_content_size)); | 237 GetWindowSizeForClientSize(window_, clamped_content_size)); |
234 | 238 |
235 if (parent_ && !PositionWindowInScreenCoordinates(widget, widget_type_)) | 239 if (parent_ && !PositionWindowInScreenCoordinates(widget, widget_type_)) |
236 actual_new_bounds.Offset(parent_->GetRestoredBounds().OffsetFromOrigin()); | 240 actual_new_bounds.Offset(parent_->ChildWindowOffset()); |
237 | 241 |
238 [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds) | 242 [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds) |
239 display:YES | 243 display:YES |
240 animate:NO]; | 244 animate:NO]; |
241 } | 245 } |
242 | 246 |
243 void BridgedNativeWidget::SetRootView(views::View* view) { | 247 void BridgedNativeWidget::SetRootView(views::View* view) { |
244 if (view == [bridged_view_ hostedView]) | 248 if (view == [bridged_view_ hostedView]) |
245 return; | 249 return; |
246 | 250 |
(...skipping 24 matching lines...) Expand all Loading... | |
271 // NSWindow API, or changes propagating out from here. | 275 // NSWindow API, or changes propagating out from here. |
272 wants_to_be_visible_ = new_state != HIDE_WINDOW; | 276 wants_to_be_visible_ = new_state != HIDE_WINDOW; |
273 | 277 |
274 if (new_state == HIDE_WINDOW) { | 278 if (new_state == HIDE_WINDOW) { |
275 [window_ orderOut:nil]; | 279 [window_ orderOut:nil]; |
276 DCHECK(!window_visible_); | 280 DCHECK(!window_visible_); |
277 return; | 281 return; |
278 } | 282 } |
279 | 283 |
280 DCHECK(wants_to_be_visible_); | 284 DCHECK(wants_to_be_visible_); |
281 | 285 // If the parent (or an ancestor) is hidden, return and wait for it to become |
282 // If there's a hidden ancestor, return and wait for it to become visible. | 286 // visible. |
283 for (BridgedNativeWidget* ancestor = parent(); | 287 if (parent() && !parent()->IsVisibleParent()) |
284 ancestor; | 288 return; |
285 ancestor = ancestor->parent()) { | |
286 if (!ancestor->window_visible_) | |
287 return; | |
288 } | |
289 | 289 |
290 if (native_widget_mac_->GetWidget()->IsModal()) { | 290 if (native_widget_mac_->GetWidget()->IsModal()) { |
291 NSWindow* parent_window = parent_->ns_window(); | 291 NSWindow* parent_window = parent_->GetNSWindow(); |
292 DCHECK(parent_window); | 292 DCHECK(parent_window); |
293 | 293 |
294 [NSApp beginSheet:window_ | 294 [NSApp beginSheet:window_ |
295 modalForWindow:parent_window | 295 modalForWindow:parent_window |
296 modalDelegate:[window_ delegate] | 296 modalDelegate:[window_ delegate] |
297 didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) | 297 didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) |
298 contextInfo:nullptr]; | 298 contextInfo:nullptr]; |
299 return; | 299 return; |
300 } | 300 } |
301 | 301 |
302 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { | 302 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { |
303 [window_ makeKeyAndOrderFront:nil]; | 303 [window_ makeKeyAndOrderFront:nil]; |
304 [NSApp activateIgnoringOtherApps:YES]; | 304 [NSApp activateIgnoringOtherApps:YES]; |
305 } else { | 305 } else { |
306 // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a | 306 // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a |
307 // parent window. So, if there's a parent, order above that. Otherwise, this | 307 // parent window. So, if there's a parent, order above that. Otherwise, this |
308 // will order above all windows at the same level. | 308 // will order above all windows at the same level. |
309 NSInteger parent_window_number = 0; | 309 NSInteger parent_window_number = 0; |
310 if (parent()) | 310 if (parent()) |
311 parent_window_number = [parent()->ns_window() windowNumber]; | 311 parent_window_number = [parent()->GetNSWindow() windowNumber]; |
312 | 312 |
313 [window_ orderWindow:NSWindowAbove | 313 [window_ orderWindow:NSWindowAbove |
314 relativeTo:parent_window_number]; | 314 relativeTo:parent_window_number]; |
315 } | 315 } |
316 DCHECK(window_visible_); | 316 DCHECK(window_visible_); |
317 } | 317 } |
318 | 318 |
319 void BridgedNativeWidget::AcquireCapture() { | 319 void BridgedNativeWidget::AcquireCapture() { |
320 DCHECK(!HasCapture()); | 320 DCHECK(!HasCapture()); |
321 if (!window_visible_) | 321 if (!window_visible_) |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
354 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { | 354 void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { |
355 NSString* key = [NSString stringWithUTF8String:name]; | 355 NSString* key = [NSString stringWithUTF8String:name]; |
356 return [[GetWindowProperties() objectForKey:key] pointerValue]; | 356 return [[GetWindowProperties() objectForKey:key] pointerValue]; |
357 } | 357 } |
358 | 358 |
359 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { | 359 void BridgedNativeWidget::SetCursor(NSCursor* cursor) { |
360 [window_delegate_ setCursor:cursor]; | 360 [window_delegate_ setCursor:cursor]; |
361 } | 361 } |
362 | 362 |
363 void BridgedNativeWidget::OnWindowWillClose() { | 363 void BridgedNativeWidget::OnWindowWillClose() { |
364 if (parent_) | 364 if (parent_) { |
365 parent_->RemoveChildWindow(this); | 365 parent_->RemoveChildWindow(this); |
366 parent_ = nullptr; | |
367 } | |
366 [window_ setDelegate:nil]; | 368 [window_ setDelegate:nil]; |
367 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; | 369 [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; |
368 native_widget_mac_->OnWindowWillClose(); | 370 native_widget_mac_->OnWindowWillClose(); |
369 } | 371 } |
370 | 372 |
371 void BridgedNativeWidget::OnFullscreenTransitionStart( | 373 void BridgedNativeWidget::OnFullscreenTransitionStart( |
372 bool target_fullscreen_state) { | 374 bool target_fullscreen_state) { |
373 // Note: This can fail for fullscreen changes started externally, but a user | 375 // Note: This can fail for fullscreen changes started externally, but a user |
374 // shouldn't be able to do that if the window is invisible to begin with. | 376 // shouldn't be able to do that if the window is invisible to begin with. |
375 DCHECK(window_visible_); | 377 DCHECK(window_visible_); |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
457 window_visible_ = new_visibility; | 459 window_visible_ = new_visibility; |
458 | 460 |
459 // If arriving via SetVisible(), |wants_to_be_visible_| should already be set. | 461 // If arriving via SetVisible(), |wants_to_be_visible_| should already be set. |
460 // If made visible externally (e.g. Cmd+H), just roll with it. Don't try (yet) | 462 // If made visible externally (e.g. Cmd+H), just roll with it. Don't try (yet) |
461 // to distinguish being *hidden* externally from being hidden by a parent | 463 // to distinguish being *hidden* externally from being hidden by a parent |
462 // window - we might not need that. | 464 // window - we might not need that. |
463 if (window_visible_) { | 465 if (window_visible_) { |
464 wants_to_be_visible_ = true; | 466 wants_to_be_visible_ = true; |
465 | 467 |
466 if (parent_) | 468 if (parent_) |
467 [parent_->ns_window() addChildWindow:window_ ordered:NSWindowAbove]; | 469 [parent_->GetNSWindow() addChildWindow:window_ ordered:NSWindowAbove]; |
468 } else { | 470 } else { |
469 mouse_capture_.reset(); // Capture on hidden windows is not permitted. | 471 mouse_capture_.reset(); // Capture on hidden windows is not permitted. |
470 | 472 |
471 // When becoming invisible, remove the entry in any parent's childWindow | 473 // When becoming invisible, remove the entry in any parent's childWindow |
472 // list. Cocoa's childWindow management breaks down when child windows are | 474 // list. Cocoa's childWindow management breaks down when child windows are |
473 // hidden. | 475 // hidden. |
474 if (parent_) | 476 if (parent_) |
475 [parent_->ns_window() removeChildWindow:window_]; | 477 [parent_->GetNSWindow() removeChildWindow:window_]; |
476 } | 478 } |
477 | 479 |
478 // TODO(tapted): Investigate whether we want this for Mac. This is what Aura | 480 // TODO(tapted): Investigate whether we want this for Mac. This is what Aura |
479 // does, and it is what tests expect. However, because layer drawing is | 481 // does, and it is what tests expect. However, because layer drawing is |
480 // asynchronous (and things like deminiaturize in AppKit are not), it can | 482 // asynchronous (and things like deminiaturize in AppKit are not), it can |
481 // result in a CALayer appearing on screen before it has been redrawn in the | 483 // result in a CALayer appearing on screen before it has been redrawn in the |
482 // GPU process. This is a general problem. In content, a helper class, | 484 // GPU process. This is a general problem. In content, a helper class, |
483 // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:] | 485 // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:] |
484 // and RenderWidgetHostView::Show() until a frame is ready. | 486 // and RenderWidgetHostView::Show() until a frame is ready. |
485 if (layer()) { | 487 if (layer()) { |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
657 | 659 |
658 void BridgedNativeWidget::AcceleratedWidgetSwapCompleted( | 660 void BridgedNativeWidget::AcceleratedWidgetSwapCompleted( |
659 const std::vector<ui::LatencyInfo>& latency_info) { | 661 const std::vector<ui::LatencyInfo>& latency_info) { |
660 } | 662 } |
661 | 663 |
662 void BridgedNativeWidget::AcceleratedWidgetHitError() { | 664 void BridgedNativeWidget::AcceleratedWidgetHitError() { |
663 compositor_->ScheduleFullRedraw(); | 665 compositor_->ScheduleFullRedraw(); |
664 } | 666 } |
665 | 667 |
666 //////////////////////////////////////////////////////////////////////////////// | 668 //////////////////////////////////////////////////////////////////////////////// |
667 // BridgedNativeWidget, private: | 669 // BridgedNativeWidget, BridgedNativeWidgetOwner: |
668 | 670 |
669 void BridgedNativeWidget::RemoveOrDestroyChildren() { | 671 NSWindow* BridgedNativeWidget::GetNSWindow() { |
670 // TODO(tapted): Implement unowned child windows if required. | 672 return window_; |
671 while (!child_windows_.empty()) { | 673 } |
672 // The NSWindow can only be destroyed after -[NSWindow close] is complete. | 674 |
673 // Retain the window, otherwise the reference count can reach zero when the | 675 gfx::Vector2d BridgedNativeWidget::ChildWindowOffset() { |
674 // child calls back into RemoveChildWindow() via its OnWindowWillClose(). | 676 return gfx::ScreenRectFromNSRect([window_ frame]).OffsetFromOrigin(); |
675 base::scoped_nsobject<NSWindow> child( | 677 } |
676 [child_windows_.back()->ns_window() retain]); | 678 |
677 [child close]; | 679 bool BridgedNativeWidget::IsVisibleParent() { |
678 } | 680 // If there's a hidden ancestor, IsVisible() returns false regardless. |
681 if (!window_visible_ || !parent()) | |
682 return window_visible_; | |
683 return parent()->IsVisibleParent(); | |
Andre
2015/05/07 17:32:37
How about,
return parent() ? parent()->IsVisible
tapted
2015/05/07 23:43:50
Done (also put the recursive call after the sequen
| |
679 } | 684 } |
680 | 685 |
681 void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) { | 686 void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) { |
682 auto location = std::find( | 687 auto location = std::find( |
683 child_windows_.begin(), child_windows_.end(), child); | 688 child_windows_.begin(), child_windows_.end(), child); |
684 DCHECK(location != child_windows_.end()); | 689 DCHECK(location != child_windows_.end()); |
685 child_windows_.erase(location); | 690 child_windows_.erase(location); |
686 child->parent_ = nullptr; | |
687 | 691 |
688 // Note the child is sometimes removed already by AppKit. This depends on OS | 692 // Note the child is sometimes removed already by AppKit. This depends on OS |
689 // version, and possibly some unpredictable reference counting. Removing it | 693 // version, and possibly some unpredictable reference counting. Removing it |
690 // here should be safe regardless. | 694 // here should be safe regardless. |
691 [window_ removeChildWindow:child->window_]; | 695 [window_ removeChildWindow:child->window_]; |
692 } | 696 } |
693 | 697 |
698 //////////////////////////////////////////////////////////////////////////////// | |
699 // BridgedNativeWidget, private: | |
700 | |
701 void BridgedNativeWidget::RemoveOrDestroyChildren() { | |
702 // TODO(tapted): Implement unowned child windows if required. | |
703 while (!child_windows_.empty()) { | |
704 // The NSWindow can only be destroyed after -[NSWindow close] is complete. | |
705 // Retain the window, otherwise the reference count can reach zero when the | |
706 // child calls back into RemoveChildWindow() via its OnWindowWillClose(). | |
707 base::scoped_nsobject<NSWindow> child( | |
708 [child_windows_.back()->ns_window() retain]); | |
709 [child close]; | |
710 } | |
711 } | |
712 | |
694 void BridgedNativeWidget::NotifyVisibilityChangeDown() { | 713 void BridgedNativeWidget::NotifyVisibilityChangeDown() { |
695 // Child windows sometimes like to close themselves in response to visibility | 714 // Child windows sometimes like to close themselves in response to visibility |
696 // changes. That's supported, but only with the asynchronous Widget::Close(). | 715 // changes. That's supported, but only with the asynchronous Widget::Close(). |
697 // Perform a heuristic to detect child removal that would break these loops. | 716 // Perform a heuristic to detect child removal that would break these loops. |
698 const size_t child_count = child_windows_.size(); | 717 const size_t child_count = child_windows_.size(); |
699 if (!window_visible_) { | 718 if (!window_visible_) { |
700 for (BridgedNativeWidget* child : child_windows_) { | 719 for (BridgedNativeWidget* child : child_windows_) { |
701 if (child->window_visible_) | 720 if (child->window_visible_) |
702 [child->ns_window() orderOut:nil]; | 721 [child->ns_window() orderOut:nil]; |
703 | 722 |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
832 window_, &kWindowPropertiesKey); | 851 window_, &kWindowPropertiesKey); |
833 if (!properties) { | 852 if (!properties) { |
834 properties = [NSMutableDictionary dictionary]; | 853 properties = [NSMutableDictionary dictionary]; |
835 objc_setAssociatedObject(window_, &kWindowPropertiesKey, | 854 objc_setAssociatedObject(window_, &kWindowPropertiesKey, |
836 properties, OBJC_ASSOCIATION_RETAIN); | 855 properties, OBJC_ASSOCIATION_RETAIN); |
837 } | 856 } |
838 return properties; | 857 return properties; |
839 } | 858 } |
840 | 859 |
841 } // namespace views | 860 } // namespace views |
OLD | NEW |