| 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" |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 if (focus_manager_) | 118 if (focus_manager_) |
| 119 focus_manager_->RemoveFocusChangeListener(this); | 119 focus_manager_->RemoveFocusChangeListener(this); |
| 120 | 120 |
| 121 if (focus_manager) | 121 if (focus_manager) |
| 122 focus_manager->AddFocusChangeListener(this); | 122 focus_manager->AddFocusChangeListener(this); |
| 123 | 123 |
| 124 focus_manager_ = focus_manager; | 124 focus_manager_ = focus_manager; |
| 125 } | 125 } |
| 126 | 126 |
| 127 void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) { | 127 void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) { |
| 128 [window_ setFrame:gfx::ScreenRectToNSRect(new_bounds) | 128 gfx::Rect actual_new_bounds(new_bounds); |
| 129 if (parent_) |
| 130 actual_new_bounds.Offset(parent_->GetRestoredBounds().OffsetFromOrigin()); |
| 131 |
| 132 [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds) |
| 129 display:YES | 133 display:YES |
| 130 animate:NO]; | 134 animate:NO]; |
| 131 } | 135 } |
| 132 | 136 |
| 133 void BridgedNativeWidget::SetRootView(views::View* view) { | 137 void BridgedNativeWidget::SetRootView(views::View* view) { |
| 134 if (view == [bridged_view_ hostedView]) | 138 if (view == [bridged_view_ hostedView]) |
| 135 return; | 139 return; |
| 136 | 140 |
| 137 // If this is ever false, the compositor will need to be properly torn down | 141 // If this is ever false, the compositor will need to be properly torn down |
| 138 // and replaced, pointing at the new view. | 142 // and replaced, pointing at the new view. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 157 // Ensure that: | 161 // Ensure that: |
| 158 // - A window with an invisible parent is not made visible. | 162 // - A window with an invisible parent is not made visible. |
| 159 // - A parent changing visibility updates child window visibility. | 163 // - A parent changing visibility updates child window visibility. |
| 160 // * But only when changed via this function - ignore changes via the | 164 // * But only when changed via this function - ignore changes via the |
| 161 // NSWindow API, or changes propagating out from here. | 165 // NSWindow API, or changes propagating out from here. |
| 162 wants_to_be_visible_ = new_state != HIDE_WINDOW; | 166 wants_to_be_visible_ = new_state != HIDE_WINDOW; |
| 163 | 167 |
| 164 if (new_state == HIDE_WINDOW) { | 168 if (new_state == HIDE_WINDOW) { |
| 165 [window_ orderOut:nil]; | 169 [window_ orderOut:nil]; |
| 166 DCHECK(!window_visible_); | 170 DCHECK(!window_visible_); |
| 167 NotifyVisibilityChangeDown(); | |
| 168 return; | 171 return; |
| 169 } | 172 } |
| 170 | 173 |
| 171 DCHECK(wants_to_be_visible_); | 174 DCHECK(wants_to_be_visible_); |
| 172 | 175 |
| 173 // If there's a hidden ancestor, return and wait for it to become visible. | 176 // If there's a hidden ancestor, return and wait for it to become visible. |
| 174 for (BridgedNativeWidget* ancestor = parent(); | 177 for (BridgedNativeWidget* ancestor = parent(); |
| 175 ancestor; | 178 ancestor; |
| 176 ancestor = ancestor->parent()) { | 179 ancestor = ancestor->parent()) { |
| 177 if (!ancestor->window_visible_) | 180 if (!ancestor->window_visible_) |
| 178 return; | 181 return; |
| 179 } | 182 } |
| 180 | 183 |
| 181 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { | 184 if (new_state == SHOW_AND_ACTIVATE_WINDOW) { |
| 182 [window_ makeKeyAndOrderFront:nil]; | 185 [window_ makeKeyAndOrderFront:nil]; |
| 183 [NSApp activateIgnoringOtherApps:YES]; | 186 [NSApp activateIgnoringOtherApps:YES]; |
| 184 } else { | 187 } else { |
| 185 // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a | 188 // ui::SHOW_STATE_INACTIVE is typically used to avoid stealing focus from a |
| 186 // parent window. So, if there's a parent, order above that. Otherwise, this | 189 // parent window. So, if there's a parent, order above that. Otherwise, this |
| 187 // will order above all windows at the same level. | 190 // will order above all windows at the same level. |
| 188 NSInteger parent_window_number = 0; | 191 NSInteger parent_window_number = 0; |
| 189 if (parent()) | 192 if (parent()) |
| 190 parent_window_number = [parent()->ns_window() windowNumber]; | 193 parent_window_number = [parent()->ns_window() windowNumber]; |
| 191 | 194 |
| 192 [window_ orderWindow:NSWindowAbove | 195 [window_ orderWindow:NSWindowAbove |
| 193 relativeTo:parent_window_number]; | 196 relativeTo:parent_window_number]; |
| 194 } | 197 } |
| 195 DCHECK(window_visible_); | 198 DCHECK(window_visible_); |
| 196 NotifyVisibilityChangeDown(); | |
| 197 } | 199 } |
| 198 | 200 |
| 199 void BridgedNativeWidget::AcquireCapture() { | 201 void BridgedNativeWidget::AcquireCapture() { |
| 200 DCHECK(!HasCapture()); | 202 DCHECK(!HasCapture()); |
| 201 if (!window_visible_) | 203 if (!window_visible_) |
| 202 return; // Capture on hidden windows is disallowed. | 204 return; // Capture on hidden windows is disallowed. |
| 203 | 205 |
| 204 mouse_capture_.reset(new CocoaMouseCapture(this)); | 206 mouse_capture_.reset(new CocoaMouseCapture(this)); |
| 205 } | 207 } |
| 206 | 208 |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { | 323 void BridgedNativeWidget::OnVisibilityChangedTo(bool new_visibility) { |
| 322 if (window_visible_ == new_visibility) | 324 if (window_visible_ == new_visibility) |
| 323 return; | 325 return; |
| 324 | 326 |
| 325 window_visible_ = new_visibility; | 327 window_visible_ = new_visibility; |
| 326 | 328 |
| 327 // If arriving via SetVisible(), |wants_to_be_visible_| should already be set. | 329 // If arriving via SetVisible(), |wants_to_be_visible_| should already be set. |
| 328 // If made visible externally (e.g. Cmd+H), just roll with it. Don't try (yet) | 330 // If made visible externally (e.g. Cmd+H), just roll with it. Don't try (yet) |
| 329 // to distinguish being *hidden* externally from being hidden by a parent | 331 // to distinguish being *hidden* externally from being hidden by a parent |
| 330 // window - we might not need that. | 332 // window - we might not need that. |
| 331 if (window_visible_) | 333 if (window_visible_) { |
| 332 wants_to_be_visible_ = true; | 334 wants_to_be_visible_ = true; |
| 333 | 335 |
| 334 // Capture on hidden windows is not permitted. | 336 if (parent_) |
| 335 if (!window_visible_) | 337 [parent_->ns_window() addChildWindow:window_ ordered:NSWindowAbove]; |
| 336 mouse_capture_.reset(); | 338 } else { |
| 339 mouse_capture_.reset(); // Capture on hidden windows is not permitted. |
| 340 |
| 341 // When becoming invisible, remove the entry in any parent's childWindow |
| 342 // list. Cocoa's childWindow management breaks down when child windows are |
| 343 // hidden. |
| 344 if (parent_) |
| 345 [parent_->ns_window() removeChildWindow:window_]; |
| 346 } |
| 337 | 347 |
| 338 // TODO(tapted): Investigate whether we want this for Mac. This is what Aura | 348 // TODO(tapted): Investigate whether we want this for Mac. This is what Aura |
| 339 // does, and it is what tests expect. However, because layer drawing is | 349 // does, and it is what tests expect. However, because layer drawing is |
| 340 // asynchronous (and things like deminiaturize in AppKit are not), it can | 350 // asynchronous (and things like deminiaturize in AppKit are not), it can |
| 341 // result in a CALayer appearing on screen before it has been redrawn in the | 351 // result in a CALayer appearing on screen before it has been redrawn in the |
| 342 // GPU process. This is a general problem. In content, a helper class, | 352 // GPU process. This is a general problem. In content, a helper class, |
| 343 // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:] | 353 // RenderWidgetResizeHelper, blocks the UI thread in -[NSView setFrameSize:] |
| 344 // and RenderWidgetHostView::Show() until a frame is ready. | 354 // and RenderWidgetHostView::Show() until a frame is ready. |
| 345 if (layer()) { | 355 if (layer()) { |
| 346 layer()->SetVisible(window_visible_); | 356 layer()->SetVisible(window_visible_); |
| 347 layer()->SchedulePaint(gfx::Rect(GetClientAreaSize())); | 357 layer()->SchedulePaint(gfx::Rect(GetClientAreaSize())); |
| 348 } | 358 } |
| 349 | 359 |
| 360 NotifyVisibilityChangeDown(); |
| 361 |
| 350 native_widget_mac_->GetWidget()->OnNativeWidgetVisibilityChanged( | 362 native_widget_mac_->GetWidget()->OnNativeWidgetVisibilityChanged( |
| 351 window_visible_); | 363 window_visible_); |
| 352 | 364 |
| 353 // Toolkit-views suppresses redraws while not visible. To prevent Cocoa asking | 365 // Toolkit-views suppresses redraws while not visible. To prevent Cocoa asking |
| 354 // for an "empty" draw, disable auto-display while hidden. For example, this | 366 // for an "empty" draw, disable auto-display while hidden. For example, this |
| 355 // prevents Cocoa drawing just *after* a minimize, resulting in a blank window | 367 // prevents Cocoa drawing just *after* a minimize, resulting in a blank window |
| 356 // represented in the deminiaturize animation. | 368 // represented in the deminiaturize animation. |
| 357 [window_ setAutodisplay:window_visible_]; | 369 [window_ setAutodisplay:window_visible_]; |
| 358 } | 370 } |
| 359 | 371 |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 513 child->parent_ = nullptr; | 525 child->parent_ = nullptr; |
| 514 } | 526 } |
| 515 | 527 |
| 516 void BridgedNativeWidget::NotifyVisibilityChangeDown() { | 528 void BridgedNativeWidget::NotifyVisibilityChangeDown() { |
| 517 // Child windows sometimes like to close themselves in response to visibility | 529 // Child windows sometimes like to close themselves in response to visibility |
| 518 // changes. That's supported, but only with the asynchronous Widget::Close(). | 530 // changes. That's supported, but only with the asynchronous Widget::Close(). |
| 519 // Perform a heuristic to detect child removal that would break these loops. | 531 // Perform a heuristic to detect child removal that would break these loops. |
| 520 const size_t child_count = child_windows_.size(); | 532 const size_t child_count = child_windows_.size(); |
| 521 if (!window_visible_) { | 533 if (!window_visible_) { |
| 522 for (BridgedNativeWidget* child : child_windows_) { | 534 for (BridgedNativeWidget* child : child_windows_) { |
| 523 if (child->window_visible_) { | 535 if (child->window_visible_) |
| 524 [child->ns_window() orderOut:nil]; | 536 [child->ns_window() orderOut:nil]; |
| 525 child->NotifyVisibilityChangeDown(); | 537 |
| 526 CHECK_EQ(child_count, child_windows_.size()); | 538 DCHECK(!child->window_visible_); |
| 527 } | 539 CHECK_EQ(child_count, child_windows_.size()); |
| 528 } | 540 } |
| 541 // The orderOut calls above should result in a call to OnVisibilityChanged() |
| 542 // in each child. There, children will remove themselves from the NSWindow |
| 543 // childWindow list as well as propagate NotifyVisibilityChangeDown() calls |
| 544 // to any children of their own. |
| 545 DCHECK_EQ(0u, [[window_ childWindows] count]); |
| 529 return; | 546 return; |
| 530 } | 547 } |
| 531 | 548 |
| 549 NSUInteger visible_children = 0; // For a DCHECK below. |
| 532 NSInteger parent_window_number = [window_ windowNumber]; | 550 NSInteger parent_window_number = [window_ windowNumber]; |
| 533 for (BridgedNativeWidget* child: child_windows_) { | 551 for (BridgedNativeWidget* child: child_windows_) { |
| 534 // Note: order the child windows on top, regardless of whether or not they | 552 // Note: order the child windows on top, regardless of whether or not they |
| 535 // are currently visible. They probably aren't, since the parent was hidden | 553 // are currently visible. They probably aren't, since the parent was hidden |
| 536 // prior to this, but they could have been made visible in other ways. | 554 // prior to this, but they could have been made visible in other ways. |
| 537 if (child->wants_to_be_visible_) { | 555 if (child->wants_to_be_visible_) { |
| 556 ++visible_children; |
| 557 // Here -[NSWindow orderWindow:relativeTo:] is used to put the window on |
| 558 // screen. However, that by itself is insufficient to guarantee a correct |
| 559 // z-order relationship. If this function is being called from a z-order |
| 560 // change in the parent, orderWindow turns out to be unreliable (i.e. the |
| 561 // ordering doesn't always take effect). What this actually relies on is |
| 562 // the resulting call to OnVisibilityChanged() in the child, which will |
| 563 // then insert itself into -[NSWindow childWindows] to let Cocoa do its |
| 564 // internal layering magic. |
| 538 [child->ns_window() orderWindow:NSWindowAbove | 565 [child->ns_window() orderWindow:NSWindowAbove |
| 539 relativeTo:parent_window_number]; | 566 relativeTo:parent_window_number]; |
| 540 child->NotifyVisibilityChangeDown(); | 567 DCHECK(child->window_visible_); |
| 541 CHECK_EQ(child_count, child_windows_.size()); | |
| 542 } | 568 } |
| 569 CHECK_EQ(child_count, child_windows_.size()); |
| 543 } | 570 } |
| 571 DCHECK_EQ(visible_children, [[window_ childWindows] count]); |
| 544 } | 572 } |
| 545 | 573 |
| 546 gfx::Size BridgedNativeWidget::GetClientAreaSize() const { | 574 gfx::Size BridgedNativeWidget::GetClientAreaSize() const { |
| 547 NSRect content_rect = [window_ contentRectForFrameRect:[window_ frame]]; | 575 NSRect content_rect = [window_ contentRectForFrameRect:[window_ frame]]; |
| 548 return gfx::Size(NSWidth(content_rect), NSHeight(content_rect)); | 576 return gfx::Size(NSWidth(content_rect), NSHeight(content_rect)); |
| 549 } | 577 } |
| 550 | 578 |
| 551 void BridgedNativeWidget::CreateCompositor() { | 579 void BridgedNativeWidget::CreateCompositor() { |
| 552 DCHECK(!compositor_); | 580 DCHECK(!compositor_); |
| 553 DCHECK(!compositor_widget_); | 581 DCHECK(!compositor_widget_); |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 634 window_, &kWindowPropertiesKey); | 662 window_, &kWindowPropertiesKey); |
| 635 if (!properties) { | 663 if (!properties) { |
| 636 properties = [NSMutableDictionary dictionary]; | 664 properties = [NSMutableDictionary dictionary]; |
| 637 objc_setAssociatedObject(window_, &kWindowPropertiesKey, | 665 objc_setAssociatedObject(window_, &kWindowPropertiesKey, |
| 638 properties, OBJC_ASSOCIATION_RETAIN); | 666 properties, OBJC_ASSOCIATION_RETAIN); |
| 639 } | 667 } |
| 640 return properties; | 668 return properties; |
| 641 } | 669 } |
| 642 | 670 |
| 643 } // namespace views | 671 } // namespace views |
| OLD | NEW |