diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-27 16:03:48 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-27 16:03:48 +0000 |
commit | ddd91e99989b2aa7539516f673f746f088cc607c (patch) | |
tree | 3f32d988f46f677e504b8fe42829cfce80962da7 | |
parent | 92c0ca8e8f3873d7cbd8b72d751ba0106486c7dc (diff) | |
download | chromium_src-ddd91e99989b2aa7539516f673f746f088cc607c.zip chromium_src-ddd91e99989b2aa7539516f673f746f088cc607c.tar.gz chromium_src-ddd91e99989b2aa7539516f673f746f088cc607c.tar.bz2 |
Allow hide animations to work again:
- Introduces VisibilityClient, which aura::Window defers to to update layer visibility, if present. This replaces code which previously undid layer visibility changes after they were set. Layer visibility changes are potentially destructiver (they can drop textures associated with the layer and require a repaint).
- Implements this in ash in VisibilityController, which animates the visibility changes of children in annotated parents.
- Annotates various windows as requiring child window visibility change animations. This replaces animating visibility changes in LayoutManagers.
- Adds a second layer of visibility determination to aura::Window. Some code may check Window->IsVisible which was simply testing layer visibility. We want Window->IsVisible to return false as soon as Window::Hide() is called, so we add a member for this.
- We prevent stacking changes to the window from being propagated to the layer if the layer's delegate has been detached (i.e. the layer is hiding)
BUG=none
TEST=none
Review URL: https://chromiumcodereview.appspot.com/9235016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119453 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ash/ash.gyp | 5 | ||||
-rw-r--r-- | ash/shell.cc | 11 | ||||
-rw-r--r-- | ash/shell.h | 2 | ||||
-rw-r--r-- | ash/shell/window_type_launcher.cc | 35 | ||||
-rw-r--r-- | ash/shell/window_type_launcher.h | 1 | ||||
-rw-r--r-- | ash/wm/menu_container_layout_manager.cc | 46 | ||||
-rw-r--r-- | ash/wm/menu_container_layout_manager.h | 36 | ||||
-rw-r--r-- | ash/wm/system_modal_container_layout_manager.cc | 1 | ||||
-rw-r--r-- | ash/wm/toplevel_layout_manager.cc | 1 | ||||
-rw-r--r-- | ash/wm/visibility_controller.cc | 54 | ||||
-rw-r--r-- | ash/wm/visibility_controller.h | 37 | ||||
-rw-r--r-- | ash/wm/visibility_controller_unittest.cc | 46 | ||||
-rw-r--r-- | ash/wm/window_animations.cc | 19 | ||||
-rw-r--r-- | ui/aura/aura.gyp | 2 | ||||
-rw-r--r-- | ui/aura/client/visibility_client.cc | 29 | ||||
-rw-r--r-- | ui/aura/client/visibility_client.h | 35 | ||||
-rw-r--r-- | ui/aura/root_window.cc | 1 | ||||
-rw-r--r-- | ui/aura/window.cc | 55 | ||||
-rw-r--r-- | ui/aura/window.h | 39 | ||||
-rw-r--r-- | ui/aura/window_unittest.cc | 103 | ||||
-rw-r--r-- | ui/views/widget/native_widget_aura.cc | 17 |
21 files changed, 419 insertions, 156 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index e8fbe4d..04759880 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -115,8 +115,6 @@ 'wm/dialog_frame_view.h', 'wm/image_grid.cc', 'wm/image_grid.h', - 'wm/menu_container_layout_manager.cc', - 'wm/menu_container_layout_manager.h', 'wm/system_modal_container_layout_manager.cc', 'wm/system_modal_container_layout_manager.h', 'wm/system_modal_container_event_filter.cc', @@ -150,6 +148,8 @@ 'wm/toplevel_window_event_filter.h', 'wm/video_detector.cc', 'wm/video_detector.h', + 'wm/visibility_controller.cc', + 'wm/visibility_controller.h', 'wm/window_animations.cc', 'wm/window_animations.h', 'wm/window_cycle_controller.cc', @@ -233,6 +233,7 @@ 'wm/toplevel_layout_manager_unittest.cc', 'wm/toplevel_window_event_filter_unittest.cc', 'wm/video_detector_unittest.cc', + 'wm/visibility_controller_unittest.cc', 'wm/window_cycle_controller_unittest.cc', 'wm/window_modality_controller_unittest.cc', 'wm/workspace_controller_unittest.cc', diff --git a/ash/shell.cc b/ash/shell.cc index 128aae2..9328dfa 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -21,7 +21,6 @@ #include "ash/wm/compact_layout_manager.h" #include "ash/wm/compact_status_area_layout_manager.h" #include "ash/wm/dialog_frame_view.h" -#include "ash/wm/menu_container_layout_manager.h" #include "ash/wm/power_button_controller.h" #include "ash/wm/root_window_event_filter.h" #include "ash/wm/root_window_layout_manager.h" @@ -33,6 +32,7 @@ #include "ash/wm/toplevel_layout_manager.h" #include "ash/wm/toplevel_window_event_filter.h" #include "ash/wm/video_detector.h" +#include "ash/wm/visibility_controller.h" #include "ash/wm/window_cycle_controller.h" #include "ash/wm/window_modality_controller.h" #include "ash/wm/window_util.h" @@ -81,6 +81,7 @@ void CreateSpecialContainers(aura::Window::Windows* containers) { default_container->SetEventFilter( new ToplevelWindowEventFilter(default_container)); default_container->set_id(internal::kShellWindowId_DefaultContainer); + SetChildWindowVisibilityChangesAnimated(default_container); containers->push_back(default_container); aura::Window* always_on_top_container = new aura::Window(NULL); @@ -88,6 +89,7 @@ void CreateSpecialContainers(aura::Window::Windows* containers) { new ToplevelWindowEventFilter(always_on_top_container)); always_on_top_container->set_id( internal::kShellWindowId_AlwaysOnTopContainer); + SetChildWindowVisibilityChangesAnimated(always_on_top_container); containers->push_back(always_on_top_container); aura::Window* panel_container = new aura::Window(NULL); @@ -104,6 +106,7 @@ void CreateSpecialContainers(aura::Window::Windows* containers) { modal_container->SetLayoutManager( new internal::SystemModalContainerLayoutManager(modal_container)); modal_container->set_id(internal::kShellWindowId_SystemModalContainer); + SetChildWindowVisibilityChangesAnimated(modal_container); containers->push_back(modal_container); // TODO(beng): Figure out if we can make this use @@ -120,6 +123,7 @@ void CreateSpecialContainers(aura::Window::Windows* containers) { new internal::SystemModalContainerLayoutManager(lock_modal_container)); lock_modal_container->set_id( internal::kShellWindowId_LockSystemModalContainer); + SetChildWindowVisibilityChangesAnimated(lock_modal_container); containers->push_back(lock_modal_container); aura::Window* status_container = new aura::Window(NULL); @@ -128,7 +132,7 @@ void CreateSpecialContainers(aura::Window::Windows* containers) { aura::Window* menu_container = new aura::Window(NULL); menu_container->set_id(internal::kShellWindowId_MenuAndTooltipContainer); - menu_container->SetLayoutManager(new internal::MenuContainerLayoutManager); + SetChildWindowVisibilityChangesAnimated(menu_container); containers->push_back(menu_container); aura::Window* setting_bubble_container = new aura::Window(NULL); @@ -299,6 +303,9 @@ void Shell::Init() { window_modality_controller_.reset(new internal::WindowModalityController); AddRootWindowEventFilter(window_modality_controller_.get()); + visibility_controller_.reset(new internal::VisibilityController); + aura::client::SetVisibilityClient(visibility_controller_.get()); + accelerator_filter_.reset(new internal::AcceleratorFilter); AddRootWindowEventFilter(accelerator_filter_.get()); diff --git a/ash/shell.h b/ash/shell.h index 975a3b7..a1dcb76 100644 --- a/ash/shell.h +++ b/ash/shell.h @@ -51,6 +51,7 @@ class RootWindowLayoutManager; class ShadowController; class StackingController; class TooltipController; +class VisibilityController; class WindowModalityController; class WorkspaceController; } @@ -183,6 +184,7 @@ class ASH_EXPORT Shell { scoped_ptr<internal::WorkspaceController> workspace_controller_; scoped_ptr<internal::ShadowController> shadow_controller_; scoped_ptr<internal::TooltipController> tooltip_controller_; + scoped_ptr<internal::VisibilityController> visibility_controller_; scoped_ptr<PowerButtonController> power_button_controller_; scoped_ptr<VideoDetector> video_detector_; scoped_ptr<WindowCycleController> window_cycle_controller_; diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc index b7424da..3cefa2c 100644 --- a/ash/shell/window_type_launcher.cc +++ b/ash/shell/window_type_launcher.cc @@ -116,6 +116,18 @@ class NonModalTransient : public views::WidgetDelegateView { widget->Show(); } + static void ToggleNonModalTransient(aura::Window* parent) { + if (!non_modal_transient_) { + non_modal_transient_ = + views::Widget::CreateWindowWithParent(new NonModalTransient, parent); + non_modal_transient_->GetNativeView()->SetName("NonModalTransient"); + } + if (non_modal_transient_->IsVisible()) + non_modal_transient_->Hide(); + else + non_modal_transient_->Show(); + } + // Overridden from views::View: virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { canvas->FillRect(color_, GetLocalBounds()); @@ -134,13 +146,22 @@ class NonModalTransient : public views::WidgetDelegateView { virtual string16 GetWindowTitle() const OVERRIDE { return ASCIIToUTF16("Non-Modal Transient"); } + virtual void DeleteDelegate() OVERRIDE { + if (GetWidget() == non_modal_transient_) + non_modal_transient_ = NULL; + } private: SkColor color_; + static views::Widget* non_modal_transient_; + DISALLOW_COPY_AND_ASSIGN(NonModalTransient); }; +// static +views::Widget* NonModalTransient::non_modal_transient_ = NULL; + } // namespace void InitWindowTypeLauncher() { @@ -178,7 +199,10 @@ WindowTypeLauncher::WindowTypeLauncher() this, ASCIIToUTF16("Open Non-Modal Transient Window")))), ALLOW_THIS_IN_INITIALIZER_LIST(examples_button_( new views::NativeTextButton( - this, ASCIIToUTF16("Open Views Examples Window")))) { + this, ASCIIToUTF16("Open Views Examples Window")))), + ALLOW_THIS_IN_INITIALIZER_LIST(show_hide_window_button_( + new views::NativeTextButton( + this, ASCIIToUTF16("Show/Hide a Window")))) { AddChildView(create_button_); AddChildView(create_nonresizable_button_); AddChildView(bubble_button_); @@ -188,6 +212,7 @@ WindowTypeLauncher::WindowTypeLauncher() AddChildView(window_modal_button_); AddChildView(transient_button_); AddChildView(examples_button_); + AddChildView(show_hide_window_button_); #if !defined(OS_MACOSX) set_context_menu_controller(this); #endif @@ -247,6 +272,12 @@ void WindowTypeLauncher::Layout() { examples_button_->SetBounds( 5, transient_button_->y() - examples_ps.height() - 5, examples_ps.width(), examples_ps.height()); + + gfx::Size show_hide_window_ps = + show_hide_window_button_->GetPreferredSize(); + show_hide_window_button_->SetBounds( + 5, examples_button_->y() - show_hide_window_ps.height() - 5, + show_hide_window_ps.width(), show_hide_window_ps.height()); } bool WindowTypeLauncher::OnMousePressed(const views::MouseEvent& event) { @@ -292,6 +323,8 @@ void WindowTypeLauncher::ButtonPressed(views::Button* sender, ui::MODAL_TYPE_WINDOW); } else if (sender == transient_button_) { NonModalTransient::OpenNonModalTransient(GetWidget()->GetNativeView()); + } else if (sender == show_hide_window_button_) { + NonModalTransient::ToggleNonModalTransient(GetWidget()->GetNativeView()); } #if !defined(OS_MACOSX) else if (sender == examples_button_) { diff --git a/ash/shell/window_type_launcher.h b/ash/shell/window_type_launcher.h index 190e37b..3dbe197 100644 --- a/ash/shell/window_type_launcher.h +++ b/ash/shell/window_type_launcher.h @@ -76,6 +76,7 @@ class WindowTypeLauncher : public views::WidgetDelegateView, views::NativeTextButton* window_modal_button_; views::NativeTextButton* transient_button_; views::NativeTextButton* examples_button_; + views::NativeTextButton* show_hide_window_button_; #if !defined(OS_MACOSX) scoped_ptr<views::MenuRunner> menu_runner_; #endif diff --git a/ash/wm/menu_container_layout_manager.cc b/ash/wm/menu_container_layout_manager.cc deleted file mode 100644 index f440f5b..0000000 --- a/ash/wm/menu_container_layout_manager.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ash/wm/menu_container_layout_manager.h" - -#include "ash/wm/window_animations.h" -#include "ui/aura/client/window_types.h" -#include "ui/aura/window.h" - -namespace ash { -namespace internal { - -MenuContainerLayoutManager::MenuContainerLayoutManager() { -} - -MenuContainerLayoutManager::~MenuContainerLayoutManager() { -} - -void MenuContainerLayoutManager::OnWindowResized() { -} - -void MenuContainerLayoutManager::OnWindowAddedToLayout(aura::Window* child) { - DCHECK(child->type() == aura::client::WINDOW_TYPE_MENU || - child->type() == aura::client::WINDOW_TYPE_TOOLTIP); -} - -void MenuContainerLayoutManager::OnWillRemoveWindowFromLayout( - aura::Window* child) { -} - -void MenuContainerLayoutManager::OnChildWindowVisibilityChanged( - aura::Window* child, - bool visible) { - if (child->type() == aura::client::WINDOW_TYPE_TOOLTIP) - AnimateOnChildWindowVisibilityChanged(child, visible); -} - -void MenuContainerLayoutManager::SetChildBounds( - aura::Window* child, - const gfx::Rect& requested_bounds) { - SetChildBoundsDirect(child, requested_bounds); -} - -} // namespace internal -} // namespace ash diff --git a/ash/wm/menu_container_layout_manager.h b/ash/wm/menu_container_layout_manager.h deleted file mode 100644 index 9395c1f..0000000 --- a/ash/wm/menu_container_layout_manager.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef ASH_WM_MENU_CONTAINER_LAYOUT_MANAGER_H_ -#define ASH_WM_MENU_CONTAINER_LAYOUT_MANAGER_H_ -#pragma once - -#include "base/compiler_specific.h" -#include "ui/aura/layout_manager.h" - -namespace ash { -namespace internal { - -class MenuContainerLayoutManager : public aura::LayoutManager { - public: - MenuContainerLayoutManager(); - virtual ~MenuContainerLayoutManager(); - - // Overridden from aura::LayoutManager: - virtual void OnWindowResized() OVERRIDE; - virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE; - virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE; - virtual void OnChildWindowVisibilityChanged(aura::Window* child, - bool visible) OVERRIDE; - virtual void SetChildBounds(aura::Window* child, - const gfx::Rect& requested_bounds) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(MenuContainerLayoutManager); -}; - -} // namespace internal -} // namespace ash - -#endif // ASH_WM_MENU_CONTAINER_LAYOUT_MANAGER_H_ diff --git a/ash/wm/system_modal_container_layout_manager.cc b/ash/wm/system_modal_container_layout_manager.cc index 8a585ac..01a7bfb 100644 --- a/ash/wm/system_modal_container_layout_manager.cc +++ b/ash/wm/system_modal_container_layout_manager.cc @@ -95,7 +95,6 @@ void SystemModalContainerLayoutManager::OnWillRemoveWindowFromLayout( void SystemModalContainerLayoutManager::OnChildWindowVisibilityChanged( aura::Window* child, bool visible) { - AnimateOnChildWindowVisibilityChanged(child, visible); } void SystemModalContainerLayoutManager::SetChildBounds( diff --git a/ash/wm/toplevel_layout_manager.cc b/ash/wm/toplevel_layout_manager.cc index e880eec..64302d2 100644 --- a/ash/wm/toplevel_layout_manager.cc +++ b/ash/wm/toplevel_layout_manager.cc @@ -62,7 +62,6 @@ void ToplevelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) { void ToplevelLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child, bool visible) { BaseLayoutManager::OnChildWindowVisibilityChanged(child, visible); - AnimateOnChildWindowVisibilityChanged(child, visible); UpdateShelfVisibility(); } diff --git a/ash/wm/visibility_controller.cc b/ash/wm/visibility_controller.cc new file mode 100644 index 0000000..8006ff3 --- /dev/null +++ b/ash/wm/visibility_controller.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/visibility_controller.h" + +#include "ash/wm/window_animations.h" +#include "ui/aura/window.h" + +namespace ash { +namespace internal { +namespace { + +// Property set on all windows whose child windows' visibility changes are +// animated. The type of the value is bool. +const char kChildWindowVisibilityChangesAnimated[] = + "ash/wm/ChildWindowVisibilityChangesAnimated"; + +bool GetChildWindowVisibilityChangesAnimated(aura::Window* window) { + if (!window) + return false; + return window->GetIntProperty(kChildWindowVisibilityChangesAnimated) != 0; +} + +} // namespace + +VisibilityController::VisibilityController() { +} + +VisibilityController::~VisibilityController() { +} + +void VisibilityController::UpdateLayerVisibility(aura::Window* window, + bool visible) { + bool animated = GetChildWindowVisibilityChangesAnimated(window->parent()) && + window->type() != aura::client::WINDOW_TYPE_CONTROL && + window->type() != aura::client::WINDOW_TYPE_UNKNOWN; + if (animated) + AnimateOnChildWindowVisibilityChanged(window, visible); + + // When a window is made visible, we always make its layer visible + // immediately. When a window is hidden, the layer must be left visible and + // only made not visible once the animation is complete. + if (!animated || visible) + window->layer()->SetVisible(visible); +} + +} // namespace internal + +void SetChildWindowVisibilityChangesAnimated(aura::Window* window) { + window->SetIntProperty(internal::kChildWindowVisibilityChangesAnimated, 1); +} + +} // namespace ash diff --git a/ash/wm/visibility_controller.h b/ash/wm/visibility_controller.h new file mode 100644 index 0000000..df8f82f --- /dev/null +++ b/ash/wm/visibility_controller.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_VISIBILITY_CONTROLLER_H_ +#define ASH_WM_VISIBILITY_CONTROLLER_H_ +#pragma once + +#include "ash/ash_export.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "ui/aura/client/visibility_client.h" + +namespace ash { +namespace internal { + +class VisibilityController : public aura::client::VisibilityClient { + public: + VisibilityController(); + virtual ~VisibilityController(); + + // Overridden from aura::client::VisibilityClient: + virtual void UpdateLayerVisibility(aura::Window* window, + bool visible) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(VisibilityController); +}; + +} // namespace internal + +// Tells |window| to animate visibility changes to its children. +void ASH_EXPORT SetChildWindowVisibilityChangesAnimated(aura::Window* window); + +} // namespace ash + +#endif // ASH_WM_VISIBILITY_CONTROLLER_H_ diff --git a/ash/wm/visibility_controller_unittest.cc b/ash/wm/visibility_controller_unittest.cc new file mode 100644 index 0000000..aaa15a5 --- /dev/null +++ b/ash/wm/visibility_controller_unittest.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/visibility_controller.h" + +#include "ash/test/aura_shell_test_base.h" +#include "ui/aura/test/test_windows.h" +#include "ui/aura/test/test_window_delegate.h" +#include "ui/aura/window.h" + +namespace ash { +namespace internal { + +typedef test::AuraShellTestBase VisibilityControllerTest; + +// Hiding a window in an animatable container should not hide the window's layer +// immediately. +TEST_F(VisibilityControllerTest, AnimateHideDoesntHideWindowLayer) { + scoped_ptr<aura::Window> container( + aura::test::CreateTestWindowWithId(-1, NULL)); + SetChildWindowVisibilityChangesAnimated(container.get()); + + aura::test::TestWindowDelegate d; + scoped_ptr<aura::Window> animatable( + aura::test::CreateTestWindowWithDelegate( + &d, -2, gfx::Rect(0, 0, 50, 50), container.get())); + scoped_ptr<aura::Window> non_animatable( + aura::test::CreateTestWindowWithDelegateAndType( + &d, aura::client::WINDOW_TYPE_CONTROL, -3, gfx::Rect(51, 51, 50, 50), + container.get())); + EXPECT_TRUE(animatable->IsVisible()); + EXPECT_TRUE(animatable->layer()->visible()); + animatable->Hide(); + EXPECT_FALSE(animatable->IsVisible()); + EXPECT_TRUE(animatable->layer()->visible()); + + EXPECT_TRUE(non_animatable->IsVisible()); + EXPECT_TRUE(non_animatable->layer()->visible()); + non_animatable->Hide(); + EXPECT_FALSE(non_animatable->IsVisible()); + EXPECT_FALSE(non_animatable->layer()->visible()); +} + +} // namespace internal +} // namespace ash diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc index 91779da..d644f2e 100644 --- a/ash/wm/window_animations.cc +++ b/ash/wm/window_animations.cc @@ -102,6 +102,7 @@ class HidingWindowAnimationObserver : public ui::ImplicitAnimationObserver, void AnimateShowWindowCommon(aura::Window* window, const ui::Transform& start_transform, const ui::Transform& end_transform) { + window->layer()->set_delegate(window); window->layer()->SetOpacity(kWindowAnimation_HideOpacity); window->layer()->SetTransform(start_transform); @@ -117,18 +118,14 @@ void AnimateShowWindowCommon(aura::Window* window, // its transform to |end_transform|. void AnimateHideWindowCommon(aura::Window* window, const ui::Transform& end_transform) { - // The window's layer was just hidden, but we need it to draw until it's fully - // transparent, so we show it again. This is undone once the animation is - // complete. - window->layer()->SetVisible(true); - { - // Property sets within this scope will be implicitly animated. - ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); - settings.AddImplicitObserver(new HidingWindowAnimationObserver(window)); + window->layer()->set_delegate(NULL); - window->layer()->SetOpacity(kWindowAnimation_HideOpacity); - window->layer()->SetTransform(end_transform); - } + // Property sets within this scope will be implicitly animated. + ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); + settings.AddImplicitObserver(new HidingWindowAnimationObserver(window)); + + window->layer()->SetOpacity(kWindowAnimation_HideOpacity); + window->layer()->SetTransform(end_transform); } // Show/Hide windows using a shrink animation. diff --git a/ui/aura/aura.gyp b/ui/aura/aura.gyp index 8e805ee..19f2828 100644 --- a/ui/aura/aura.gyp +++ b/ui/aura/aura.gyp @@ -40,6 +40,8 @@ 'client/stacking_client.h', 'client/tooltip_client.cc', 'client/tooltip_client.h', + 'client/visibility_client.cc', + 'client/visibility_client.h', 'client/window_move_client.cc', 'client/window_move_client.h', 'client/window_types.h', diff --git a/ui/aura/client/visibility_client.cc b/ui/aura/client/visibility_client.cc new file mode 100644 index 0000000..3c42538 --- /dev/null +++ b/ui/aura/client/visibility_client.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/aura/client/visibility_client.h" + +#include "ui/aura/root_window.h" + +namespace aura { +namespace client { +namespace { + +// A property key to store a client that handles window visibility changes. The +// type of the value is |aura::client::VisibilityClient*|. +const char kRootWindowVisibilityClient[] = "RootWindowVisibilityClient"; + +} // namespace + +void SetVisibilityClient(VisibilityClient* client) { + RootWindow::GetInstance()->SetProperty(kRootWindowVisibilityClient, client); +} + +VisibilityClient* GetVisibilityClient() { + return reinterpret_cast<VisibilityClient*>( + RootWindow::GetInstance()->GetProperty(kRootWindowVisibilityClient)); +} + +} // namespace client +} // namespace aura diff --git a/ui/aura/client/visibility_client.h b/ui/aura/client/visibility_client.h new file mode 100644 index 0000000..bde4ba5 --- /dev/null +++ b/ui/aura/client/visibility_client.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_AURA_CLIENT_VISIBILITY_CLIENT_H_ +#define UI_AURA_CLIENT_VISIBILITY_CLIENT_H_ +#pragma once + +#include "ui/aura/aura_export.h" + +namespace aura { +class Window; +namespace client { + +// An interface implemented by an object that manages the visibility of Windows' +// layers as Window visibility changes. +class AURA_EXPORT VisibilityClient { + public: + // Called when |window|'s visibility is changing to |visible|. The implementor + // can decide whether or not to pass on the visibility to the underlying + // layer. + virtual void UpdateLayerVisibility(Window* window, bool visible) = 0; + + protected: + virtual ~VisibilityClient() {} +}; + +// Sets/Gets the VisibilityClient on the RootWindow. +AURA_EXPORT void SetVisibilityClient(VisibilityClient* client); +AURA_EXPORT VisibilityClient* GetVisibilityClient(); + +} // namespace clients +} // namespace aura + +#endif // UI_AURA_CLIENT_VISIBILITY_CLIENT_H_ diff --git a/ui/aura/root_window.cc b/ui/aura/root_window.cc index 984cd308..b7e5f5e 100644 --- a/ui/aura/root_window.cc +++ b/ui/aura/root_window.cc @@ -272,7 +272,6 @@ void RootWindow::WindowInitialized(Window* window) { } void RootWindow::WindowDestroying(Window* window) { - // Update the focused window state if the window was focused. if (focused_window_ == window) SetFocusedWindow(focused_window_->parent()); diff --git a/ui/aura/window.cc b/ui/aura/window.cc index a47d897..b628a92 100644 --- a/ui/aura/window.cc +++ b/ui/aura/window.cc @@ -10,6 +10,7 @@ #include "base/stl_util.h" #include "base/string_util.h" #include "ui/aura/client/stacking_client.h" +#include "ui/aura/client/visibility_client.h" #include "ui/aura/event.h" #include "ui/aura/event_filter.h" #include "ui/aura/layout_manager.h" @@ -35,11 +36,19 @@ Window* GetParentForWindow(Window* window, Window* suggested_parent) { } // namespace +Window::TestApi::TestApi(Window* window) : window_(window) {} + +bool Window::TestApi::OwnsLayer() const { + return !!window_->layer_owner_.get(); +} + Window::Window(WindowDelegate* delegate) : type_(client::WINDOW_TYPE_UNKNOWN), delegate_(delegate), + layer_(NULL), parent_(NULL), transient_parent_(NULL), + visible_(false), id_(-1), transparent_(false), user_data_(NULL), @@ -48,9 +57,6 @@ Window::Window(WindowDelegate* delegate) } Window::~Window() { - if (layer_.get()) - layer_->set_delegate(NULL); - // Let the delegate know we're in the processing of destroying. if (delegate_) delegate_->OnWindowDestroying(); @@ -94,10 +100,16 @@ Window::~Window() { DCHECK(transient_children_.empty()); FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroyed(this)); + + // The layer will either be destroyed by layer_owner_'s dtor, or by whoever + // acquired it. + layer_->set_delegate(NULL); + layer_ = NULL; } void Window::Init(ui::Layer::LayerType layer_type) { - layer_.reset(new ui::Layer(layer_type)); + layer_ = new ui::Layer(layer_type); + layer_owner_.reset(layer_); layer_->SetVisible(false); layer_->set_delegate(this); UpdateLayerName(name_); @@ -126,7 +138,7 @@ void Window::SetTransparent(bool transparent) { } ui::Layer* Window::AcquireLayer() { - return layer_.release(); + return layer_owner_.release(); } void Window::Show() { @@ -139,7 +151,11 @@ void Window::Hide() { } bool Window::IsVisible() const { - return layer_->IsDrawn(); + // Layer visibility can be inconsistent with window visibility, for example + // when a Window is hidden, we want this function to return false immediately + // after, even though the client may decide to animate the hide effect (and + // so the layer will be visible for some time after Hide() is called). + return visible_ && layer_->IsDrawn(); } gfx::Rect Window::GetScreenBounds() const { @@ -235,7 +251,8 @@ void Window::StackChildAbove(Window* child, Window* other) { children_.erase(children_.begin() + child_i); children_.insert(children_.begin() + dest_i, child); - layer()->StackAbove(child->layer(), other->layer()); + if (other->layer()->delegate()) + layer()->StackAbove(child->layer(), other->layer()); // Stack any transient children that share the same parent to be in front of // 'child'. @@ -259,7 +276,7 @@ void Window::AddChild(Window* child) { child->parent()->RemoveChild(child); child->parent_ = this; - layer_->Add(child->layer_.get()); + layer_->Add(child->layer_); children_.push_back(child); if (layout_manager_.get()) @@ -296,9 +313,11 @@ void Window::RemoveChild(Window* child) { child->parent_ = NULL; if (root_window) root_window->WindowDetachedFromRootWindow(child); - // The either this window or |child| may have released its layer. - if (layer_.get() && child->layer_.get()) - layer_->Remove(child->layer_.get()); + // We should only remove the child's layer if the child still owns that layer. + // Someone else may have acquired ownership of it via AcquireLayer() and may + // expect the hierarchy to go unchanged as the Window is destroyed. + if (child->layer_owner_.get()) + layer_->Remove(child->layer_); children_.erase(i); child->OnParentChanged(); } @@ -518,17 +537,21 @@ void Window::SetBoundsInternal(const gfx::Rect& new_bounds) { } void Window::SetVisible(bool visible) { - // TODO(beng): Replace this with layer_->GetTargetVisibility(). - // See http://crbug.com/110487 if (visible == layer_->visible()) return; // No change. bool was_visible = IsVisible(); - if (visible != (layer_->visible() && layer_->GetTargetOpacity() != 0.0f)) - layer_->SetVisible(visible); + if (visible != layer_->visible()) { + if (client::GetVisibilityClient()) + client::GetVisibilityClient()->UpdateLayerVisibility(this, visible); + else + layer_->SetVisible(visible); + } + visible_ = visible; bool is_visible = IsVisible(); if (was_visible != is_visible) { - SchedulePaint(); + if (is_visible) + SchedulePaint(); if (delegate_) delegate_->OnWindowVisibilityChanged(is_visible); } diff --git a/ui/aura/window.h b/ui/aura/window.h index 9d3a2fd..ea27d4b 100644 --- a/ui/aura/window.h +++ b/ui/aura/window.h @@ -50,6 +50,20 @@ class AURA_EXPORT Window : public ui::LayerDelegate { public: typedef std::vector<Window*> Windows; + class AURA_EXPORT TestApi { + public: + explicit TestApi(Window* window); + + bool OwnsLayer() const; + + private: + TestApi(); + + Window* window_; + + DISALLOW_COPY_AND_ASSIGN(TestApi); + }; + explicit Window(WindowDelegate* delegate); virtual ~Window(); @@ -73,17 +87,15 @@ class AURA_EXPORT Window : public ui::LayerDelegate { bool transparent() const { return transparent_; } void SetTransparent(bool transparent); - ui::Layer* layer() { return layer_.get(); } - const ui::Layer* layer() const { return layer_.get(); } + ui::Layer* layer() { return layer_; } + const ui::Layer* layer() const { return layer_; } - // Releases the Window's reference to its layer, and returns it. + // Releases the Window's owning reference to its layer, and returns it. // This is used when you need to animate the presentation of the Window just // prior to destroying it. The window can be destroyed soon after calling this // function, and the caller is then responsible for disposing of the layer - // once any animation completes. - // Note that Window methods generally don't expect a NULL layer, and so this - // function should only be called right before destroying the Window, - // otherwise you will crash. + // once any animation completes. Note that layer() will remain valid until the + // end of ~Window(). ui::Layer* AcquireLayer(); WindowDelegate* delegate() { return delegate_; } @@ -326,7 +338,13 @@ class AURA_EXPORT Window : public ui::LayerDelegate { WindowDelegate* delegate_; - scoped_ptr<ui::Layer> layer_; + // The Window will own its layer unless ownership is relinquished via a call + // to AcquireLayer(). After that moment |layer_| will still be valid but + // |layer_owner_| will be NULL. The reason for releasing ownership is that + // the client may wish to animate the window's layer beyond the lifetime of + // the window, e.g. fading it out when it is destroyed. + scoped_ptr<ui::Layer> layer_owner_; + ui::Layer* layer_; // The Window's parent. Window* parent_; @@ -339,6 +357,11 @@ class AURA_EXPORT Window : public ui::LayerDelegate { Window* transient_parent_; + // The visibility state of the window as set by Show()/Hide(). This may differ + // from the visibility of the underlying layer, which may remain visible after + // the window is hidden (e.g. to animate its disappearance). + bool visible_; + int id_; std::string name_; diff --git a/ui/aura/window_unittest.cc b/ui/aura/window_unittest.cc index a5bd458..079ae02 100644 --- a/ui/aura/window_unittest.cc +++ b/ui/aura/window_unittest.cc @@ -9,6 +9,7 @@ #include "base/stringprintf.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/stacking_client.h" +#include "ui/aura/client/visibility_client.h" #include "ui/aura/event.h" #include "ui/aura/focus_manager.h" #include "ui/aura/root_window.h" @@ -1123,35 +1124,99 @@ TEST_F(WindowObserverTest, PropertyChanged) { EXPECT_EQ("name= old=0 new=0", PropertyChangeInfoAndClear()); } -class LayerGrabber : public WindowObserver { - public: - explicit LayerGrabber(Window* window) { - window->AddObserver(this); +TEST_F(WindowTest, AcquireLayer) { + scoped_ptr<Window> window1(CreateTestWindowWithId(1, NULL)); + scoped_ptr<Window> window2(CreateTestWindowWithId(2, NULL)); + ui::Layer* parent = window1->parent()->layer(); + EXPECT_EQ(2U, parent->children().size()); + + Window::TestApi window1_test_api(window1.get()); + Window::TestApi window2_test_api(window2.get()); + + EXPECT_TRUE(window1_test_api.OwnsLayer()); + EXPECT_TRUE(window2_test_api.OwnsLayer()); + + // After acquisition, window1 should not own its layer, but it should still + // be available to the window. + scoped_ptr<ui::Layer> window1_layer(window1->AcquireLayer()); + EXPECT_FALSE(window1_test_api.OwnsLayer()); + EXPECT_TRUE(window1_layer.get() == window1->layer()); + + // Upon destruction, window1's layer should still be valid, and in the layer + // hierarchy, but window2's should be gone, and no longer in the hierarchy. + window1.reset(); + window2.reset(); + + // This should be set by the window's destructor. + EXPECT_TRUE(window1_layer->delegate() == NULL); + EXPECT_EQ(1U, parent->children().size()); +} + +TEST_F(WindowTest, DontRestackWindowsWhoseLayersHaveNoDelegate) { + scoped_ptr<Window> window1(CreateTestWindowWithId(1, NULL)); + scoped_ptr<Window> window2(CreateTestWindowWithId(2, NULL)); + + // This brings window1 (and its layer) to the front. + RootWindow::GetInstance()->StackChildAbove(window1.get(), window2.get()); + EXPECT_EQ(RootWindow::GetInstance()->children().front(), window2.get()); + EXPECT_EQ(RootWindow::GetInstance()->children().back(), window1.get()); + EXPECT_EQ(RootWindow::GetInstance()->layer()->children().front(), + window2->layer()); + EXPECT_EQ(RootWindow::GetInstance()->layer()->children().back(), + window1->layer()); + + // This brings window2 (but NOT its layer) to the front. + window1->layer()->set_delegate(NULL); + RootWindow::GetInstance()->StackChildAbove(window2.get(), window1.get()); + EXPECT_EQ(RootWindow::GetInstance()->children().front(), window1.get()); + EXPECT_EQ(RootWindow::GetInstance()->children().back(), window2.get()); + EXPECT_EQ(RootWindow::GetInstance()->layer()->children().front(), + window2->layer()); + EXPECT_EQ(RootWindow::GetInstance()->layer()->children().back(), + window1->layer()); +} + +class TestVisibilityClient : public client::VisibilityClient { +public: + TestVisibilityClient() : ignore_visibility_changes_(false) { + client::SetVisibilityClient(this); + } + virtual ~TestVisibilityClient() { + client::SetVisibilityClient(NULL); } - virtual ~LayerGrabber() {} - ui::Layer* layer() { return layer_.get(); } + void set_ignore_visibility_changes(bool ignore_visibility_changes) { + ignore_visibility_changes_ = ignore_visibility_changes; + } - // Overridden from WindowObserver: - virtual void OnWindowDestroying(Window* window) OVERRIDE { - window->RemoveObserver(this); - layer_.reset(window->AcquireLayer()); + // Overridden from client::VisibilityClient: + virtual void UpdateLayerVisibility(aura::Window* window, + bool visible) OVERRIDE { + if (!ignore_visibility_changes_) + window->layer()->SetVisible(visible); } private: - scoped_ptr<ui::Layer> layer_; - DISALLOW_COPY_AND_ASSIGN(LayerGrabber); + bool ignore_visibility_changes_; + DISALLOW_COPY_AND_ASSIGN(TestVisibilityClient); }; -TEST_F(WindowTest, AcquireLayer) { +TEST_F(WindowTest, VisibilityClientIsVisible) { + TestVisibilityClient client; + scoped_ptr<Window> window(CreateTestWindowWithId(1, NULL)); + EXPECT_TRUE(window->IsVisible()); + EXPECT_TRUE(window->layer()->visible()); + window->Hide(); - LayerGrabber grabber(window.get()); - window.reset(); - EXPECT_FALSE(grabber.layer() == NULL); - EXPECT_FALSE(grabber.layer()->visible()); - // This should be set by the window's destructor. - EXPECT_TRUE(grabber.layer()->delegate() == NULL); + EXPECT_FALSE(window->IsVisible()); + EXPECT_FALSE(window->layer()->visible()); + window->Show(); + + client.set_ignore_visibility_changes(true); + window->Hide(); + EXPECT_FALSE(window->IsVisible()); + EXPECT_TRUE(window->layer()->visible()); } } // namespace test diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc index 487d706..57026ea 100644 --- a/ui/views/widget/native_widget_aura.cc +++ b/ui/views/widget/native_widget_aura.cc @@ -161,21 +161,19 @@ gfx::Font NativeWidgetAura::GetWindowTitleFont() { void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { ownership_ = params.ownership; window_->set_user_data(this); - Widget::InitParams::Type window_type = - params.child ? Widget::InitParams::TYPE_CONTROL : params.type; - window_->SetType(GetAuraWindowTypeForWidgetType(window_type)); + window_->SetType(GetAuraWindowTypeForWidgetType(params.type)); // TODO(jamescook): Should this use params.show_state instead? window_->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); window_->SetTransparent(params.transparent); window_->Init(params.create_texture_for_layer ? ui::Layer::LAYER_HAS_TEXTURE : ui::Layer::LAYER_HAS_NO_TEXTURE); - if (window_type == Widget::InitParams::TYPE_CONTROL) + if (params.type == Widget::InitParams::TYPE_CONTROL) window_->Show(); delegate_->OnNativeWidgetCreated(); window_->SetBounds(params.bounds); - if (window_type == Widget::InitParams::TYPE_CONTROL) { + if (params.child) { window_->SetParent(params.GetParent()); } else { // Set up the transient child before the window is added. This way the @@ -195,9 +193,8 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { can_activate_ = params.can_activate; DCHECK(GetWidget()->GetRootView()); #if !defined(OS_MACOSX) - if (params.type != Widget::InitParams::TYPE_TOOLTIP) { + if (params.type != Widget::InitParams::TYPE_TOOLTIP) tooltip_manager_.reset(new views::TooltipManagerAura(this)); - } #endif // !defined(OS_MACOSX) drop_helper_.reset(new DropHelper(GetWidget()->GetRootView())); @@ -699,11 +696,7 @@ void NativeWidgetAura::OnCaptureLost() { } void NativeWidgetAura::OnPaint(gfx::Canvas* canvas) { - // Because we may animate closed it's entirely possible to be asked to paint - // while closing. We ignore paints during this time as most likely the data - // associated with views is in a weird state. - if (!close_widget_factory_.HasWeakPtrs()) - delegate_->OnNativeWidgetPaint(canvas); + delegate_->OnNativeWidgetPaint(canvas); } void NativeWidgetAura::OnWindowDestroying() { |