From 6b87f5d78bda81eb6b8804bd148b0d448dd79a6e Mon Sep 17 00:00:00 2001 From: "flackr@chromium.org" Date: Tue, 8 Oct 2013 18:45:25 +0000 Subject: [PATCH] Transform parents of modal windows with modal window. Hide non overview windows. BUG=304289 TEST=WindowSelectorTest.NonActivatableWindowsHidden, WindowSelectorTest.ModalChild, WindowSelectorTest.ClickModalWindowParent TEST=Open a browser, press Ctrl+O to open a modal window. Enter overview. Browser should be transformed with modal file open window. Clicking on browser should activate file open window. Review URL: https://codereview.chromium.org/26487003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227555 0039d316-1c4b-4281-b951-d872f2087c98 --- .../overview/scoped_transform_overview_window.cc | 97 +++++++++++++++++----- ash/wm/overview/scoped_transform_overview_window.h | 3 + ash/wm/overview/window_overview.cc | 29 ++++--- ash/wm/overview/window_overview.h | 9 +- ash/wm/overview/window_selector_panels.cc | 9 +- ash/wm/overview/window_selector_unittest.cc | 54 +++++++++++- ash/wm/overview/window_selector_window.cc | 2 +- 7 files changed, 155 insertions(+), 48 deletions(-) diff --git a/ash/wm/overview/scoped_transform_overview_window.cc b/ash/wm/overview/scoped_transform_overview_window.cc index 0318e471fee2..d51a56e7cbf4 100644 --- a/ash/wm/overview/scoped_transform_overview_window.cc +++ b/ash/wm/overview/scoped_transform_overview_window.cc @@ -4,6 +4,7 @@ #include "ash/wm/overview/scoped_transform_overview_window.h" +#include "ash/screen_ash.h" #include "ash/shell.h" #include "ash/wm/window_state.h" #include "ui/aura/client/aura_constants.h" @@ -161,6 +162,40 @@ void SetTransformOnWindow(aura::Window* window, } } +gfx::Transform TranslateTransformOrigin(const gfx::Vector2d& new_origin, + const gfx::Transform& transform) { + gfx::Transform result; + result.Translate(-new_origin.x(), -new_origin.y()); + result.PreconcatTransform(transform); + result.Translate(new_origin.x(), new_origin.y()); + return result; +} + +void SetTransformOnWindowAndAllTransientChildren( + aura::Window* window, + const gfx::Transform& transform, + bool animate) { + SetTransformOnWindow(window, transform, animate); + + aura::Window::Windows transient_children = window->transient_children(); + for (aura::Window::Windows::iterator iter = transient_children.begin(); + iter != transient_children.end(); ++iter) { + aura::Window* transient_child = *iter; + gfx::Rect window_bounds = window->bounds(); + gfx::Rect child_bounds = transient_child->bounds(); + gfx::Transform transient_window_transform( + TranslateTransformOrigin(child_bounds.origin() - window_bounds.origin(), + transform)); + SetTransformOnWindow(transient_child, transient_window_transform, animate); + } +} + +aura::Window* GetModalTransientParent(aura::Window* window) { + if (window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW) + return window->transient_parent(); + return NULL; +} + } // namespace const int ScopedTransformOverviewWindow::kTransitionMilliseconds = 100; @@ -215,10 +250,27 @@ ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() { } } -bool ScopedTransformOverviewWindow::Contains(const aura::Window* window) const { - if (window_copy_ && window_copy_->GetNativeWindow()->Contains(window)) +bool ScopedTransformOverviewWindow::Contains(const aura::Window* target) const { + if (window_copy_ && window_copy_->GetNativeWindow()->Contains(target)) return true; - return window_->Contains(window); + aura::Window* window = window_; + while (window) { + if (window->Contains(target)) + return true; + window = GetModalTransientParent(window); + } + return false; +} + +gfx::Rect ScopedTransformOverviewWindow::GetBoundsInScreen() const { + gfx::Rect bounds; + aura::Window* window = window_; + while (window) { + bounds.Union(ScreenAsh::ConvertRectToScreen(window->parent(), + window->GetTargetBounds())); + window = GetModalTransientParent(window); + } + return bounds; } void ScopedTransformOverviewWindow::RestoreWindow() { @@ -277,6 +329,9 @@ void ScopedTransformOverviewWindow::SetTransform( if (root_window != window_->GetRootWindow() && !window_copy_) { DCHECK(!layer_); + // TODO(flackr): Create copies of the transient children and transient + // parent windows as well. Currently they will only be visible on the + // window's initial display. layer_ = views::corewm::RecreateWindowLayers(window_, true); window_copy_ = CreateCopyOfWindow(root_window, window_, layer_); } @@ -286,28 +341,24 @@ void ScopedTransformOverviewWindow::SetTransform( void ScopedTransformOverviewWindow::SetTransformOnWindowAndTransientChildren( const gfx::Transform& transform, bool animate) { - SetTransformOnWindow(window_, transform, animate); - + gfx::Point origin(GetBoundsInScreen().origin()); + aura::Window* window = window_; + while (window->transient_parent()) + window = window->transient_parent(); if (window_copy_) { - SetTransformOnWindow(window_copy_->GetNativeWindow(), transform, animate); - } - - // TODO(flackr): Create copies of the transient children windows as well. - // Currently they will only be visible on the window's initial display. - aura::Window::Windows transient_children = window_->transient_children(); - for (aura::Window::Windows::iterator iter = transient_children.begin(); - iter != transient_children.end(); ++iter) { - aura::Window* transient_child = *iter; - gfx::Transform transient_window_transform; - gfx::Rect window_bounds = window_->bounds(); - gfx::Rect child_bounds = transient_child->bounds(); - transient_window_transform.Translate(window_bounds.x() - child_bounds.x(), - window_bounds.y() - child_bounds.y()); - transient_window_transform.PreconcatTransform(transform); - transient_window_transform.Translate(child_bounds.x() - window_bounds.x(), - child_bounds.y() - window_bounds.y()); - SetTransformOnWindow(transient_child, transient_window_transform, animate); + SetTransformOnWindow( + window_copy_->GetNativeWindow(), + TranslateTransformOrigin(ScreenAsh::ConvertRectToScreen( + window_->parent(), window_->GetTargetBounds()).origin() - origin, + transform), + animate); } + SetTransformOnWindowAndAllTransientChildren( + window, + TranslateTransformOrigin(ScreenAsh::ConvertRectToScreen( + window->parent(), window->GetTargetBounds()).origin() - origin, + transform), + animate); } void ScopedTransformOverviewWindow::PrepareForOverview() { diff --git a/ash/wm/overview/scoped_transform_overview_window.h b/ash/wm/overview/scoped_transform_overview_window.h index c9b75690cea2..af656c1f02c2 100644 --- a/ash/wm/overview/scoped_transform_overview_window.h +++ b/ash/wm/overview/scoped_transform_overview_window.h @@ -46,6 +46,9 @@ class ScopedTransformOverviewWindow { // used to determine if an event targetted this window. bool Contains(const aura::Window* target) const; + // Returns the original bounds of all transformed windows. + gfx::Rect GetBoundsInScreen() const; + // Restores the window if it was minimized. void RestoreWindow(); diff --git a/ash/wm/overview/window_overview.cc b/ash/wm/overview/window_overview.cc index f3a928d49e9e..5574416f84bb 100644 --- a/ash/wm/overview/window_overview.cc +++ b/ash/wm/overview/window_overview.cc @@ -123,14 +123,25 @@ WindowOverview::WindowOverview(WindowSelector* window_selector, // as suggested there. cursor_client_->LockCursor(); } - ash::Shell::GetInstance()->AddPreTargetHandler(this); + ash::Shell::GetInstance()->PrependPreTargetHandler(this); Shell* shell = Shell::GetInstance(); shell->delegate()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW); - SetOpacityOfNonOverviewWindows(0); + HideAndTrackNonOverviewWindows(); } WindowOverview::~WindowOverview() { - SetOpacityOfNonOverviewWindows(1); + const aura::WindowTracker::Windows hidden_windows = hidden_windows_.windows(); + for (aura::WindowTracker::Windows::const_iterator iter = + hidden_windows.begin(); iter != hidden_windows.end(); ++iter) { + ui::ScopedLayerAnimationSettings settings( + (*iter)->layer()->GetAnimator()); + settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds( + ScopedTransformOverviewWindow::kTransitionMilliseconds)); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + (*iter)->Show(); + (*iter)->layer()->SetOpacity(1); + } if (cursor_client_) cursor_client_->UnlockCursor(); ash::Shell::GetInstance()->RemovePreTargetHandler(this); @@ -284,7 +295,7 @@ aura::Window* WindowOverview::GetTargetedWindow(aura::Window* window) { return NULL; } -void WindowOverview::SetOpacityOfNonOverviewWindows(float opacity) { +void WindowOverview::HideAndTrackNonOverviewWindows() { Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); for (Shell::RootWindowList::const_iterator root_iter = root_windows.begin(); root_iter != root_windows.end(); ++root_iter) { @@ -294,8 +305,6 @@ void WindowOverview::SetOpacityOfNonOverviewWindows(float opacity) { for (aura::Window::Windows::const_iterator iter = container->children().begin(); iter != container->children().end(); ++iter) { - // Skip windows in overview and those which are not visible. A layer - // opacity of 0 is still considered visible. if (GetTargetedWindow(*iter) || !(*iter)->IsVisible()) continue; ui::ScopedLayerAnimationSettings settings( @@ -304,11 +313,9 @@ void WindowOverview::SetOpacityOfNonOverviewWindows(float opacity) { ScopedTransformOverviewWindow::kTransitionMilliseconds)); settings.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - // Changing the visibility (i.e. calling Window::Hide) would also hide - // modal child windows, however the modal child window being activatable - // is in the overview and should be visible. Instead, use opacity to - // fade out non-activatable windows during overview. - (*iter)->layer()->SetOpacity(opacity); + (*iter)->Hide(); + (*iter)->layer()->SetOpacity(0); + hidden_windows_.Add(*iter); } } } diff --git a/ash/wm/overview/window_overview.h b/ash/wm/overview/window_overview.h index 8fbaed7b90e7..595cee1f83c7 100644 --- a/ash/wm/overview/window_overview.h +++ b/ash/wm/overview/window_overview.h @@ -9,6 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/time/time.h" +#include "ui/aura/window_tracker.h" #include "ui/events/event_handler.h" #include "ui/gfx/rect.h" @@ -76,8 +77,8 @@ class WindowOverview : public ui::EventHandler { // no overview window was found for |window|. aura::Window* GetTargetedWindow(aura::Window* window); - // Sets the opacity of all windows not in the overview to |opacity|. - void SetOpacityOfNonOverviewWindows(float opacity); + // Hide and track all hidden windows not in overview. + void HideAndTrackNonOverviewWindows(); // Position all of the windows based on the current selection mode. void PositionWindows(); @@ -119,6 +120,10 @@ class WindowOverview : public ui::EventHandler { // The cursor client used to lock the current cursor during overview. aura::client::CursorClient* cursor_client_; + // Tracks windows which were hidden because they were not part of the + // overview. + aura::WindowTracker hidden_windows_; + DISALLOW_COPY_AND_ASSIGN(WindowOverview); }; diff --git a/ash/wm/overview/window_selector_panels.cc b/ash/wm/overview/window_selector_panels.cc index a028de0a81fc..52642bf713b7 100644 --- a/ash/wm/overview/window_selector_panels.cc +++ b/ash/wm/overview/window_selector_panels.cc @@ -152,10 +152,7 @@ void WindowSelectorPanels::SetItemBounds(aura::RootWindow* root_window, gfx::Rect bounding_rect; for (WindowList::iterator iter = transform_windows_.begin(); iter != transform_windows_.end(); ++iter) { - aura::Window* panel = (*iter)->window(); - gfx::Rect bounds = ScreenAsh::ConvertRectToScreen( - panel->parent(), panel->GetTargetBounds()); - bounding_rect.Union(bounds); + bounding_rect.Union((*iter)->GetBoundsInScreen()); } gfx::Transform bounding_transform = ScopedTransformOverviewWindow::GetTransformForRectPreservingAspectRatio( @@ -163,9 +160,7 @@ void WindowSelectorPanels::SetItemBounds(aura::RootWindow* root_window, for (WindowList::iterator iter = transform_windows_.begin(); iter != transform_windows_.end(); ++iter) { gfx::Transform transform; - aura::Window* panel = (*iter)->window(); - gfx::Rect bounds = ScreenAsh::ConvertRectToScreen( - panel->parent(), panel->GetTargetBounds()); + gfx::Rect bounds = (*iter)->GetBoundsInScreen(); transform.Translate(bounding_rect.x() - bounds.x(), bounding_rect.y() - bounds.y()); transform.PreconcatTransform(bounding_transform); diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index 1b9f971e93ab..eda6a8a01b78 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc @@ -510,12 +510,58 @@ TEST_F(WindowSelectorTest, NonActivatableWindowsHidden) { scoped_ptr window1(CreateWindow(bounds)); scoped_ptr window2(CreateWindow(bounds)); scoped_ptr non_activatable_window( - CreateNonActivatableWindow(bounds)); - EXPECT_EQ(1.0f, non_activatable_window->layer()->GetTargetOpacity()); + CreateNonActivatableWindow(Shell::GetPrimaryRootWindow()->bounds())); + EXPECT_TRUE(non_activatable_window->IsVisible()); ToggleOverview(); - EXPECT_EQ(0.0f, non_activatable_window->layer()->GetTargetOpacity()); + EXPECT_FALSE(non_activatable_window->IsVisible()); ToggleOverview(); - EXPECT_EQ(1.0f, non_activatable_window->layer()->GetTargetOpacity()); + EXPECT_TRUE(non_activatable_window->IsVisible()); + + // Test that a window behind the fullscreen non-activatable window can be + // clicked. + non_activatable_window->parent()->StackChildAtTop( + non_activatable_window.get()); + ToggleOverview(); + ClickWindow(window1.get()); + EXPECT_FALSE(IsSelecting()); + EXPECT_TRUE(wm::IsActiveWindow(window1.get())); +} + +// Tests that windows with modal child windows are transformed with the modal +// child even though not activatable themselves. +TEST_F(WindowSelectorTest, ModalChild) { + gfx::Rect bounds(0, 0, 400, 400); + scoped_ptr window1(CreateWindow(bounds)); + scoped_ptr child1(CreateWindow(bounds)); + child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); + window1->AddTransientChild(child1.get()); + EXPECT_EQ(window1->parent(), child1->parent()); + ToggleOverview(); + EXPECT_TRUE(window1->IsVisible()); + EXPECT_TRUE(child1->IsVisible()); + EXPECT_EQ(ToEnclosingRect(GetTransformedTargetBounds(child1.get())), + ToEnclosingRect(GetTransformedTargetBounds(window1.get()))); + ToggleOverview(); +} + +// Tests that clicking a modal window's parent activates the modal window in +// overview. +TEST_F(WindowSelectorTest, ClickModalWindowParent) { + scoped_ptr window1(CreateWindow(gfx::Rect(0, 0, 180, 180))); + scoped_ptr child1(CreateWindow(gfx::Rect(200, 0, 180, 180))); + child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); + window1->AddTransientChild(child1.get()); + EXPECT_FALSE(WindowsOverlapping(window1.get(), child1.get())); + EXPECT_EQ(window1->parent(), child1->parent()); + ToggleOverview(); + // Given that their relative positions are preserved, the windows should still + // not overlap. + EXPECT_FALSE(WindowsOverlapping(window1.get(), child1.get())); + ClickWindow(window1.get()); + EXPECT_FALSE(IsSelecting()); + + // Clicking on window1 should activate child1. + EXPECT_TRUE(wm::IsActiveWindow(child1.get())); } // Tests that windows remain on the display they are currently on in overview diff --git a/ash/wm/overview/window_selector_window.cc b/ash/wm/overview/window_selector_window.cc index 1c1bfb0b39e9..d257d29d76f6 100644 --- a/ash/wm/overview/window_selector_window.cc +++ b/ash/wm/overview/window_selector_window.cc @@ -52,7 +52,7 @@ void WindowSelectorWindow::SetItemBounds(aura::RootWindow* root_window, gfx::Rect bounding_rect = transform_window_.window()->GetBoundsInScreen(); transform_window_.SetTransform(root_window, ScopedTransformOverviewWindow::GetTransformForRectPreservingAspectRatio( - transform_window_.window()->GetBoundsInScreen(), target_bounds), + transform_window_.GetBoundsInScreen(), target_bounds), animate); } -- 2.11.4.GIT