diff options
author | flackr@chromium.org <flackr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-17 20:09:24 +0000 |
---|---|---|
committer | flackr@chromium.org <flackr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-17 20:09:24 +0000 |
commit | 8ce50fba196ca66cb241d1215b853d8b5b345049 (patch) | |
tree | 715f550f4f17112b48f793ac8eeff7ed2e1cd086 /ash | |
parent | d75cad0fa92fd0f165d796f24dfc8ba5395c0bf3 (diff) | |
download | chromium_src-8ce50fba196ca66cb241d1215b853d8b5b345049.zip chromium_src-8ce50fba196ca66cb241d1215b853d8b5b345049.tar.gz chromium_src-8ce50fba196ca66cb241d1215b853d8b5b345049.tar.bz2 |
Add panels as a single group of windows per display for overview window cycling.
BUG=264734
TEST=WindowSelectorTest.*, WindowTest.IsVisibleRespectsSetVisible
Review URL: https://chromiumcodereview.appspot.com/23654037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223665 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash.gyp | 6 | ||||
-rw-r--r-- | ash/wm/mru_window_tracker.cc | 3 | ||||
-rw-r--r-- | ash/wm/overview/scoped_transform_overview_window.cc | 264 | ||||
-rw-r--r-- | ash/wm/overview/scoped_transform_overview_window.h | 97 | ||||
-rw-r--r-- | ash/wm/overview/window_overview.cc | 49 | ||||
-rw-r--r-- | ash/wm/overview/window_overview.h | 13 | ||||
-rw-r--r-- | ash/wm/overview/window_selector.cc | 100 | ||||
-rw-r--r-- | ash/wm/overview/window_selector.h | 10 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_item.cc | 21 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_item.h | 69 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_panels.cc | 177 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_panels.h | 54 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_unittest.cc | 155 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_window.cc | 238 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_window.h | 72 | ||||
-rw-r--r-- | ash/wm/panels/panel_layout_manager.cc | 12 | ||||
-rw-r--r-- | ash/wm/panels/panel_layout_manager.h | 3 |
17 files changed, 1005 insertions, 338 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index dc60602..cab1886 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -437,6 +437,8 @@ 'wm/mru_window_tracker.h', 'wm/overlay_event_filter.cc', 'wm/overlay_event_filter.h', + 'wm/overview/scoped_transform_overview_window.cc', + 'wm/overview/scoped_transform_overview_window.h', 'wm/overview/window_overview.cc', 'wm/overview/window_overview.h', 'wm/overview/window_selector.cc', @@ -444,6 +446,10 @@ 'wm/overview/window_selector_controller.cc', 'wm/overview/window_selector_controller.h', 'wm/overview/window_selector_delegate.h', + 'wm/overview/window_selector_item.cc', + 'wm/overview/window_selector_item.h', + 'wm/overview/window_selector_panels.cc', + 'wm/overview/window_selector_panels.h', 'wm/overview/window_selector_window.cc', 'wm/overview/window_selector_window.h', 'wm/panels/panel_frame_view.cc', diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc index 869f8b5..a476b3e 100644 --- a/ash/wm/mru_window_tracker.cc +++ b/ash/wm/mru_window_tracker.cc @@ -90,7 +90,8 @@ MruWindowTracker::WindowList BuildWindowListInternal( const int kSwitchableWindowContainerIds[] = { internal::kShellWindowId_DefaultContainer, - internal::kShellWindowId_AlwaysOnTopContainer + internal::kShellWindowId_AlwaysOnTopContainer, + internal::kShellWindowId_PanelContainer }; const size_t kSwitchableWindowContainerIdsLength = diff --git a/ash/wm/overview/scoped_transform_overview_window.cc b/ash/wm/overview/scoped_transform_overview_window.cc new file mode 100644 index 0000000..40c57e3 --- /dev/null +++ b/ash/wm/overview/scoped_transform_overview_window.cc @@ -0,0 +1,264 @@ +// Copyright 2013 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/overview/scoped_transform_overview_window.h" + +#include "ash/shell.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/root_window.h" +#include "ui/aura/window.h" +#include "ui/compositor/layer_animation_observer.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/display.h" +#include "ui/gfx/interpolated_transform.h" +#include "ui/gfx/transform_util.h" +#include "ui/views/corewm/shadow_types.h" +#include "ui/views/corewm/window_animations.h" +#include "ui/views/corewm/window_util.h" +#include "ui/views/widget/widget.h" + +namespace ash { + +namespace { + +// Creates a copy of |window| with |recreated_layer| in the |target_root|. +views::Widget* CreateCopyOfWindow(aura::RootWindow* target_root, + aura::Window* src_window, + ui::Layer* recreated_layer) { + views::Widget* widget = new views::Widget; + views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + params.parent = src_window->parent(); + params.can_activate = false; + params.keep_on_top = true; + widget->set_focus_on_creation(false); + widget->Init(params); + widget->SetVisibilityChangedAnimationsEnabled(false); + std::string name = src_window->name() + " (Copy)"; + widget->GetNativeWindow()->SetName(name); + views::corewm::SetShadowType(widget->GetNativeWindow(), + views::corewm::SHADOW_TYPE_RECTANGULAR); + + // Set the bounds in the target root window. + gfx::Display target_display = + Shell::GetScreen()->GetDisplayNearestWindow(target_root); + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(src_window->GetRootWindow()); + if (screen_position_client && target_display.is_valid()) { + screen_position_client->SetBounds(widget->GetNativeWindow(), + src_window->GetBoundsInScreen(), target_display); + } else { + widget->SetBounds(src_window->GetBoundsInScreen()); + } + widget->StackAbove(src_window); + + // Move the |recreated_layer| to the newly created window. + recreated_layer->set_delegate(src_window->layer()->delegate()); + gfx::Rect layer_bounds = recreated_layer->bounds(); + layer_bounds.set_origin(gfx::Point(0, 0)); + recreated_layer->SetBounds(layer_bounds); + recreated_layer->SetVisible(false); + recreated_layer->parent()->Remove(recreated_layer); + + aura::Window* window = widget->GetNativeWindow(); + recreated_layer->SetVisible(true); + window->layer()->Add(recreated_layer); + window->layer()->StackAtTop(recreated_layer); + window->layer()->SetOpacity(1); + window->Show(); + return widget; +} + +// An observer which closes the widget and deletes the layer after an +// animation finishes. +class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver { + public: + CleanupWidgetAfterAnimationObserver(views::Widget* widget, ui::Layer* layer); + + virtual void OnLayerAnimationEnded( + ui::LayerAnimationSequence* sequence) OVERRIDE; + virtual void OnLayerAnimationAborted( + ui::LayerAnimationSequence* sequence) OVERRIDE; + virtual void OnLayerAnimationScheduled( + ui::LayerAnimationSequence* sequence) OVERRIDE; + + protected: + virtual ~CleanupWidgetAfterAnimationObserver(); + + private: + views::Widget* widget_; + ui::Layer* layer_; + + DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver); +}; + +CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver( + views::Widget* widget, + ui::Layer* layer) + : widget_(widget), + layer_(layer) { + widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this); +} + +void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded( + ui::LayerAnimationSequence* sequence) { + delete this; +} + +void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted( + ui::LayerAnimationSequence* sequence) { + delete this; +} + +void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled( + ui::LayerAnimationSequence* sequence) { +} + +CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() { + widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this); + widget_->Close(); + widget_ = NULL; + if (layer_) { + views::corewm::DeepDeleteLayers(layer_); + layer_ = NULL; + } +} + +// The animation settings used for window selector animations. +class WindowSelectorAnimationSettings + : public ui::ScopedLayerAnimationSettings { + public: + WindowSelectorAnimationSettings(aura::Window* window) : + ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()) { + SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + SetTransitionDuration(base::TimeDelta::FromMilliseconds( + ScopedTransformOverviewWindow::kTransitionMilliseconds)); + } + + virtual ~WindowSelectorAnimationSettings() { + } +}; + +} // namespace + +const int ScopedTransformOverviewWindow::kTransitionMilliseconds = 100; + +ScopedTransformOverviewWindow::ScopedTransformOverviewWindow( + aura::Window* window) + : window_(window), + window_copy_(NULL), + layer_(NULL), + minimized_(window->GetProperty(aura::client::kShowStateKey) == + ui::SHOW_STATE_MINIMIZED), + overview_started_(false), + original_transform_(window->layer()->GetTargetTransform()) { +} + +ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() { + if (window_) { + WindowSelectorAnimationSettings animation_settings(window_); + gfx::Transform transform; + window_->SetTransform(original_transform_); + if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) != + ui::SHOW_STATE_MINIMIZED) { + // Setting opacity 0 and visible false ensures that the property change + // to SHOW_STATE_MINIMIZED will not animate the window from its original + // bounds to the minimized position. + window_->layer()->SetOpacity(0); + window_->Hide(); + window_->SetProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_MINIMIZED); + } + } + // If a copy of the window was created, clean it up. + if (window_copy_) { + if (window_) { + // If the initial window wasn't destroyed, the copy needs to be animated + // out. CleanupWidgetAfterAnimationObserver will destroy the widget and + // layer after the animation is complete. + new CleanupWidgetAfterAnimationObserver(window_copy_, layer_); + WindowSelectorAnimationSettings animation_settings( + window_copy_->GetNativeWindow()); + window_copy_->GetNativeWindow()->SetTransform(original_transform_); + } else { + window_copy_->Close(); + if (layer_) + views::corewm::DeepDeleteLayers(layer_); + } + window_copy_ = NULL; + layer_ = NULL; + } +} + +bool ScopedTransformOverviewWindow::Contains(const aura::Window* window) const { + if (window_copy_ && window_copy_->GetNativeWindow()->Contains(window)) + return true; + return window_->Contains(window); +} + +void ScopedTransformOverviewWindow::RestoreWindow() { + if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) == + ui::SHOW_STATE_MINIMIZED) { + window_->Show(); + } +} + +void ScopedTransformOverviewWindow::RestoreWindowOnExit() { + minimized_ = false; + original_transform_ = gfx::Transform(); +} + +void ScopedTransformOverviewWindow::OnWindowDestroyed() { + window_ = NULL; +} + +gfx::Transform ScopedTransformOverviewWindow:: + GetTransformForRectPreservingAspectRatio(const gfx::Rect& rect, + const gfx::Rect& bounds) { + DCHECK(!rect.IsEmpty()); + DCHECK(!bounds.IsEmpty()); + float scale = std::min(1.0f, + std::min(static_cast<float>(bounds.width()) / rect.width(), + static_cast<float>(bounds.height()) / rect.height())); + gfx::Transform transform; + gfx::Vector2d offset( + 0.5 * (bounds.width() - scale * rect.width()), + 0.5 * (bounds.height() - scale * rect.height())); + transform.Translate(bounds.x() - rect.x() + offset.x(), + bounds.y() - rect.y() + offset.y()); + transform.Scale(scale, scale); + return transform; +} + +void ScopedTransformOverviewWindow::SetTransform( + aura::RootWindow* root_window, + const gfx::Transform& transform) { + // If this is the first transform, perform one-time window modifications + // necessary for overview mode. + if (!overview_started_) { + OnOverviewStarted(); + overview_started_ = true; + } + + if (root_window != window_->GetRootWindow()) { + if (!window_copy_) { + DCHECK(!layer_); + layer_ = views::corewm::RecreateWindowLayers(window_, true); + window_copy_ = CreateCopyOfWindow(root_window, window_, layer_); + } + WindowSelectorAnimationSettings animation_settings( + window_copy_->GetNativeWindow()); + window_copy_->GetNativeWindow()->SetTransform(transform); + } + WindowSelectorAnimationSettings animation_settings(window_); + window_->SetTransform(transform); +} + +void ScopedTransformOverviewWindow::OnOverviewStarted() { + RestoreWindow(); +} + +} // namespace ash diff --git a/ash/wm/overview/scoped_transform_overview_window.h b/ash/wm/overview/scoped_transform_overview_window.h new file mode 100644 index 0000000..55e8454 --- /dev/null +++ b/ash/wm/overview/scoped_transform_overview_window.h @@ -0,0 +1,97 @@ +// Copyright 2013 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_OVERVIEW_SCOPED_TRANSFORM_OVERVIEW_WINDOW_H_ +#define ASH_WM_OVERVIEW_SCOPED_TRANSFORM_OVERVIEW_WINDOW_H_ + +#include "base/compiler_specific.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/transform.h" + +namespace aura { +class RootWindow; +class Window; +} + +namespace ui { +class Layer; +} + +namespace views { +class Widget; +} + +namespace ash { + +// Manages a window in the overview mode. This class allows transforming the +// window with a helper to determine the best fit in certain bounds and +// copies the window if being moved to another display. The window's state is +// restored on destruction of this object. +class ScopedTransformOverviewWindow { + public: + // The duration of transitions used for window transforms. + static const int kTransitionMilliseconds; + + // Returns the transform necessary to fit |rect| into |bounds| preserving + // aspect ratio and centering. + static gfx::Transform GetTransformForRectPreservingAspectRatio( + const gfx::Rect& rect, + const gfx::Rect& bounds); + + explicit ScopedTransformOverviewWindow(aura::Window* window); + virtual ~ScopedTransformOverviewWindow(); + + // Returns true if this window selector window contains the |target|. This is + // used to determine if an event targetted this window. + bool Contains(const aura::Window* target) const; + + // Restores the window if it was minimized. + void RestoreWindow(); + + // Restores this window on exit rather than returning it to a minimized state + // if it was minimized on entering overview mode. + void RestoreWindowOnExit(); + + // Informs the ScopedTransformOverviewWindow that the window being watched was + // destroyed. This resets the internal window pointer to avoid calling + // anything on the window at destruction time. + void OnWindowDestroyed(); + + // Sets |transform| on the window and a copy of the window if the target + // |root_window| is not the window's root window. + void SetTransform(aura::RootWindow* root_window, + const gfx::Transform& transform); + + aura::Window* window() const { return window_; } + + protected: + // Dispatched when the overview of this window has started. + virtual void OnOverviewStarted(); + + private: + // A weak pointer to the real window in the overview. + aura::Window* window_; + + // A copy of the window used to transition the window to another root. + views::Widget* window_copy_; + + // A weak pointer to a deep copy of the window's layers. + ui::Layer* layer_; + + // If true, the window was minimized and should be restored if the window + // was not selected. + bool minimized_; + + // True if the window has been transformed for overview mode. + bool overview_started_; + + // The original transform of the window before entering overview mode. + gfx::Transform original_transform_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTransformOverviewWindow); +}; + +} // namespace ash + +#endif // ASH_WM_OVERVIEW_SCOPED_TRANSFORM_OVERVIEW_WINDOW_H_ diff --git a/ash/wm/overview/window_overview.cc b/ash/wm/overview/window_overview.cc index ea139a2..3411c76 100644 --- a/ash/wm/overview/window_overview.cc +++ b/ash/wm/overview/window_overview.cc @@ -10,7 +10,7 @@ #include "ash/shell.h" #include "ash/shell_window_ids.h" #include "ash/wm/overview/window_selector.h" -#include "ash/wm/overview/window_selector_window.h" +#include "ash/wm/overview/window_selector_item.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/aura/root_window.h" #include "ui/aura/window.h" @@ -31,14 +31,14 @@ const float kWindowOverviewSelectionOpacity = 0.5f; const int kWindowOverviewSelectionPadding = 15; // A comparator for locating a given target window. -struct WindowSelectorWindowComparator - : public std::unary_function<WindowSelectorWindow*, bool> { - explicit WindowSelectorWindowComparator(const aura::Window* target_window) +struct WindowSelectorItemComparator + : public std::unary_function<WindowSelectorItem*, bool> { + explicit WindowSelectorItemComparator(const aura::Window* target_window) : target(target_window) { } - bool operator()(const WindowSelectorWindow* window) const { - return target == window->window(); + bool operator()(const WindowSelectorItem* window) const { + return window->TargetedWindow(target) != NULL; } const aura::Window* target; @@ -47,7 +47,7 @@ struct WindowSelectorWindowComparator } // namespace WindowOverview::WindowOverview(WindowSelector* window_selector, - WindowSelectorWindowList* windows, + WindowSelectorItemList* windows, aura::RootWindow* single_root_window) : window_selector_(window_selector), windows_(windows), @@ -89,9 +89,9 @@ void WindowOverview::OnEvent(ui::Event* event) { // If the event is targetted at any of the windows in the overview, then // prevent it from propagating. aura::Window* target = static_cast<aura::Window*>(event->target()); - for (WindowSelectorWindowList::iterator iter = windows_->begin(); + for (WindowSelectorItemList::iterator iter = windows_->begin(); iter != windows_->end(); ++iter) { - if ((*iter)->Contains(target)) { + if ((*iter)->TargetedWindow(target)) { // TODO(flackr): StopPropogation prevents generation of gesture events. // We should find a better way to prevent events from being delivered to // the window, perhaps a transparent window in front of the target window @@ -116,24 +116,24 @@ void WindowOverview::OnKeyEvent(ui::KeyEvent* event) { void WindowOverview::OnMouseEvent(ui::MouseEvent* event) { if (event->type() != ui::ET_MOUSE_RELEASED) return; - WindowSelectorWindow* target = GetEventTarget(event); + aura::Window* target = GetEventTarget(event); if (!target) return; - window_selector_->SelectWindow(target->window()); + window_selector_->SelectWindow(target); } void WindowOverview::OnTouchEvent(ui::TouchEvent* event) { if (event->type() != ui::ET_TOUCH_PRESSED) return; - WindowSelectorWindow* target = GetEventTarget(event); + aura::Window* target = GetEventTarget(event); if (!target) return; - window_selector_->SelectWindow(target->window()); + window_selector_->SelectWindow(target); } -WindowSelectorWindow* WindowOverview::GetEventTarget(ui::LocatedEvent* event) { +aura::Window* WindowOverview::GetEventTarget(ui::LocatedEvent* event) { aura::Window* target = static_cast<aura::Window*>(event->target()); // If the target window doesn't actually contain the event location (i.e. // mouse down over the window and mouse up elsewhere) then do not select the @@ -141,18 +141,19 @@ WindowSelectorWindow* WindowOverview::GetEventTarget(ui::LocatedEvent* event) { if (!target->HitTest(event->location())) return NULL; - for (WindowSelectorWindowList::iterator iter = windows_->begin(); + for (WindowSelectorItemList::iterator iter = windows_->begin(); iter != windows_->end(); ++iter) { - if ((*iter)->Contains(target)) - return *iter; + aura::Window* selected = (*iter)->TargetedWindow(target); + if (selected) + return selected; } return NULL; } void WindowOverview::PositionWindows() { if (single_root_window_) { - std::vector<WindowSelectorWindow*> windows; - for (WindowSelectorWindowList::iterator iter = windows_->begin(); + std::vector<WindowSelectorItem*> windows; + for (WindowSelectorItemList::iterator iter = windows_->begin(); iter != windows_->end(); ++iter) { windows.push_back(*iter); } @@ -165,10 +166,10 @@ void WindowOverview::PositionWindows() { } void WindowOverview::PositionWindowsFromRoot(aura::RootWindow* root_window) { - std::vector<WindowSelectorWindow*> windows; - for (WindowSelectorWindowList::iterator iter = windows_->begin(); + std::vector<WindowSelectorItem*> windows; + for (WindowSelectorItemList::iterator iter = windows_->begin(); iter != windows_->end(); ++iter) { - if ((*iter)->window()->GetRootWindow() == root_window) + if ((*iter)->GetRootWindow() == root_window) windows.push_back(*iter); } PositionWindowsOnRoot(root_window, windows); @@ -176,7 +177,7 @@ void WindowOverview::PositionWindowsFromRoot(aura::RootWindow* root_window) { void WindowOverview::PositionWindowsOnRoot( aura::RootWindow* root_window, - const std::vector<WindowSelectorWindow*>& windows) { + const std::vector<WindowSelectorItem*>& windows) { if (windows.empty()) return; @@ -213,7 +214,7 @@ void WindowOverview::PositionWindowsOnRoot( window_size.width(), window_size.height()); target_bounds.Inset(kWindowMargin, kWindowMargin); - windows[i]->TransformToFitBounds(root_window, target_bounds); + windows[i]->SetBounds(root_window, target_bounds); } } diff --git a/ash/wm/overview/window_overview.h b/ash/wm/overview/window_overview.h index 640de03..96f546b 100644 --- a/ash/wm/overview/window_overview.h +++ b/ash/wm/overview/window_overview.h @@ -11,6 +11,7 @@ #include "ui/base/events/event_handler.h" namespace aura { +class Window; class RootWindow; } @@ -25,21 +26,21 @@ class Widget; namespace ash { class WindowSelector; -class WindowSelectorWindow; +class WindowSelectorItem; // The WindowOverview shows a grid of all of your windows and allows selecting // a window by clicking or tapping on it. It also displays a selection widget // used to indicate the current selection when alt-tabbing between windows. class WindowOverview : public ui::EventHandler { public: - typedef ScopedVector<WindowSelectorWindow> WindowSelectorWindowList; + typedef ScopedVector<WindowSelectorItem> WindowSelectorItemList; // Enters an overview mode displaying |windows| and dispatches methods // on |window_selector| when a window is selected or selection is canceled. // If |single_root_window| is not NULL, all windows will be positioned on the // given root window. WindowOverview(WindowSelector* window_selector, - WindowSelectorWindowList* windows, + WindowSelectorItemList* windows, aura::RootWindow* single_root_window); virtual ~WindowOverview(); @@ -58,7 +59,7 @@ class WindowOverview : public ui::EventHandler { private: // Returns the target of |event| or NULL if the event is not targeted at // any of the windows in the selector. - WindowSelectorWindow* GetEventTarget(ui::LocatedEvent* event); + aura::Window* GetEventTarget(ui::LocatedEvent* event); // Position all of the windows based on the current selection mode. void PositionWindows(); @@ -66,7 +67,7 @@ class WindowOverview : public ui::EventHandler { void PositionWindowsFromRoot(aura::RootWindow* root_window); // Position all of the |windows| to fit on the |root_window|. void PositionWindowsOnRoot(aura::RootWindow* root_window, - const std::vector<WindowSelectorWindow*>& windows); + const std::vector<WindowSelectorItem*>& windows); void InitializeSelectionWidget(); @@ -80,7 +81,7 @@ class WindowOverview : public ui::EventHandler { // A weak pointer to the collection of windows in the overview wrapped by a // helper class which restores their state and helps transform them to other // root windows. - WindowSelectorWindowList* windows_; + WindowSelectorItemList* windows_; // Widget indicating which window is currently selected. scoped_ptr<views::Widget> selection_widget_; diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc index a741afd..913727e 100644 --- a/ash/wm/overview/window_selector.cc +++ b/ash/wm/overview/window_selector.cc @@ -10,7 +10,9 @@ #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/window_overview.h" #include "ash/wm/overview/window_selector_delegate.h" +#include "ash/wm/overview/window_selector_panels.h" #include "ash/wm/overview/window_selector_window.h" +#include "ash/wm/window_settings.h" #include "base/auto_reset.h" #include "base/timer/timer.h" #include "ui/aura/client/activation_client.h" @@ -27,19 +29,33 @@ namespace { const int kOverviewDelayOnCycleMilliseconds = 300; // A comparator for locating a given target window. -struct WindowSelectorWindowComparator - : public std::unary_function<WindowSelectorWindow*, bool> { - explicit WindowSelectorWindowComparator(const aura::Window* target_window) +struct WindowSelectorItemComparator + : public std::unary_function<WindowSelectorItem*, bool> { + explicit WindowSelectorItemComparator(const aura::Window* target_window) : target(target_window) { } - bool operator()(const WindowSelectorWindow* window) const { - return target == window->window(); + bool operator()(const WindowSelectorItem* window) const { + return window->TargetedWindow(target) != NULL; } const aura::Window* target; }; +// A comparator for locating a selector item for a given root. +struct WindowSelectorItemForRoot + : public std::unary_function<WindowSelectorItem*, bool> { + explicit WindowSelectorItemForRoot(const aura::RootWindow* root) + : root_window(root) { + } + + bool operator()(const WindowSelectorItem* item) const { + return item->GetRootWindow() == root_window; + } + + const aura::RootWindow* root_window; +}; + // Filter to watch for the termination of a keyboard gesture to cycle through // multiple windows. class WindowSelectorEventFilter : public ui::EventHandler { @@ -90,14 +106,32 @@ WindowSelector::WindowSelector(const WindowList& windows, restore_focus_window_(NULL), restoring_focus_(false) { DCHECK(delegate_); - RemoveFocusAndSetRestoreWindow(); + std::vector<WindowSelectorPanels*> panels_items; for (size_t i = 0; i < windows.size(); ++i) { - // restore_focus_window_ is already observed from the call to - // RemoveFocusAndSetRestoreWindow. - if (windows[i] != restore_focus_window_) - windows[i]->AddObserver(this); - windows_.push_back(new WindowSelectorWindow(windows[i])); + windows[i]->AddObserver(this); + observed_windows_.insert(windows[i]); + + if (windows[i]->type() == aura::client::WINDOW_TYPE_PANEL && + wm::GetWindowSettings(windows[i])->panel_attached()) { + // Attached panel windows are grouped into a single overview item per + // root window (display). + std::vector<WindowSelectorPanels*>::iterator iter = + std::find_if(panels_items.begin(), panels_items.end(), + WindowSelectorItemForRoot(windows[i]->GetRootWindow())); + WindowSelectorPanels* panels_item = NULL; + if (iter == panels_items.end()) { + panels_item = new WindowSelectorPanels(); + panels_items.push_back(panels_item); + windows_.push_back(panels_item); + } else { + panels_item = *iter; + } + panels_item->AddWindow(windows[i]); + } else { + windows_.push_back(new WindowSelectorWindow(windows[i])); + } } + RemoveFocusAndSetRestoreWindow(); // Observe window activations and switchable containers on all root windows // for newly created windows during overview. @@ -121,8 +155,9 @@ WindowSelector::WindowSelector(const WindowList& windows, WindowSelector::~WindowSelector() { ResetFocusRestoreWindow(true); - for (size_t i = 0; i < windows_.size(); i++) { - windows_[i]->window()->RemoveObserver(this); + for (std::set<aura::Window*>::iterator iter = observed_windows_.begin(); + iter != observed_windows_.end(); ++iter) { + (*iter)->RemoveObserver(this); } Shell::GetInstance()->activation_client()->RemoveObserver(this); Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); @@ -143,7 +178,8 @@ void WindowSelector::Step(WindowSelector::Direction direction) { if (window_overview_) { window_overview_->SetSelection(selected_window_); } else { - aura::Window* current_window = windows_[selected_window_]->window(); + aura::Window* current_window = + windows_[selected_window_]->SelectionWindow(); current_window->Show(); current_window->SetTransform(gfx::Transform()); current_window->parent()->StackChildAtTop(current_window); @@ -153,17 +189,17 @@ void WindowSelector::Step(WindowSelector::Direction direction) { void WindowSelector::SelectWindow() { ResetFocusRestoreWindow(false); - SelectWindow(windows_[selected_window_]->window()); + SelectWindow(windows_[selected_window_]->SelectionWindow()); } void WindowSelector::SelectWindow(aura::Window* window) { - ScopedVector<WindowSelectorWindow>::iterator iter = + ScopedVector<WindowSelectorItem>::iterator iter = std::find_if(windows_.begin(), windows_.end(), - WindowSelectorWindowComparator(window)); + WindowSelectorItemComparator(window)); DCHECK(iter != windows_.end()); // The selected window should not be minimized when window selection is // ended. - (*iter)->RestoreWindowOnExit(); + (*iter)->RestoreWindowOnExit(window); delegate_->OnWindowSelected(window); } @@ -188,9 +224,9 @@ void WindowSelector::OnWindowAdded(aura::Window* new_window) { } void WindowSelector::OnWindowDestroyed(aura::Window* window) { - ScopedVector<WindowSelectorWindow>::iterator iter = + ScopedVector<WindowSelectorItem>::iterator iter = std::find_if(windows_.begin(), windows_.end(), - WindowSelectorWindowComparator(window)); + WindowSelectorItemComparator(window)); DCHECK(window == restore_focus_window_ || iter != windows_.end()); window->RemoveObserver(this); if (window == restore_focus_window_) @@ -198,14 +234,21 @@ void WindowSelector::OnWindowDestroyed(aura::Window* window) { if (iter == windows_.end()) return; + observed_windows_.erase(window); + (*iter)->RemoveWindow(window); + // If there are still windows in this selector entry then the overview is + // still active and the active selection remains the same. + if (!(*iter)->empty()) + return; + size_t deleted_index = iter - windows_.begin(); - (*iter)->OnWindowDestroyed(); windows_.erase(iter); if (windows_.empty()) { CancelSelection(); return; } - window_overview_->OnWindowsChanged(); + if (window_overview_) + window_overview_->OnWindowsChanged(); if (mode_ == CYCLE && selected_window_ >= deleted_index) { if (selected_window_ > deleted_index) selected_window_--; @@ -248,7 +291,10 @@ void WindowSelector::RemoveFocusAndSetRestoreWindow() { restore_focus_window_ = focus_client->GetFocusedWindow(); if (restore_focus_window_) { focus_client->FocusWindow(NULL); - restore_focus_window_->AddObserver(this); + if (observed_windows_.find(restore_focus_window_) == + observed_windows_.end()) { + restore_focus_window_->AddObserver(this); + } } } @@ -259,10 +305,10 @@ void WindowSelector::ResetFocusRestoreWindow(bool focus) { base::AutoReset<bool> restoring_focus(&restoring_focus_, true); restore_focus_window_->Focus(); } - // If the window is in the windows_ list it needs to continue to be observed. - if (std::find_if(windows_.begin(), windows_.end(), - WindowSelectorWindowComparator(restore_focus_window_)) == - windows_.end()) { + // If the window is in the observed_windows_ list it needs to continue to be + // observed. + if (observed_windows_.find(restore_focus_window_) == + observed_windows_.end()) { restore_focus_window_->RemoveObserver(this); } restore_focus_window_ = NULL; diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h index 4059540..b93b5da 100644 --- a/ash/wm/overview/window_selector.h +++ b/ash/wm/overview/window_selector.h @@ -5,6 +5,7 @@ #ifndef ASH_WM_OVERVIEW_WINDOW_SELECTOR_H_ #define ASH_WM_OVERVIEW_WINDOW_SELECTOR_H_ +#include <set> #include <vector> #include "ash/ash_export.h" @@ -31,7 +32,7 @@ class WindowSelectorTest; class WindowOverview; class WindowSelectorDelegate; -class WindowSelectorWindow; +class WindowSelectorItem; // The WindowSelector allows selecting a window by alt-tabbing (CYCLE mode) or // by clicking or tapping on it (OVERVIEW mode). A WindowOverview will be shown @@ -94,9 +95,12 @@ class ASH_EXPORT WindowSelector // |focus|, restores focus to the stored window. void ResetFocusRestoreWindow(bool focus); - // The collection of windows in the overview wrapped by a helper class which + // The collection of items in the overview wrapped by a helper class which // restores their state and helps transform them to other root windows. - ScopedVector<WindowSelectorWindow> windows_; + ScopedVector<WindowSelectorItem> windows_; + + // Tracks observed windows. + std::set<aura::Window*> observed_windows_; // The window selection mode. Mode mode_; diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc new file mode 100644 index 0000000..b5ee7c1 --- /dev/null +++ b/ash/wm/overview/window_selector_item.cc @@ -0,0 +1,21 @@ +// Copyright 2013 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/overview/window_selector_item.h" + +namespace ash { + +WindowSelectorItem::WindowSelectorItem() { +} + +WindowSelectorItem::~WindowSelectorItem() { +} + +void WindowSelectorItem::SetBounds(aura::RootWindow* root_window, + const gfx::Rect& target_bounds) { + bounds_ = target_bounds; + SetItemBounds(root_window, target_bounds); +} + +} // namespace ash diff --git a/ash/wm/overview/window_selector_item.h b/ash/wm/overview/window_selector_item.h new file mode 100644 index 0000000..9e29a59 --- /dev/null +++ b/ash/wm/overview/window_selector_item.h @@ -0,0 +1,69 @@ +// Copyright 2013 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_OVERVIEW_WINDOW_SELECTOR_ITEM_H_ +#define ASH_WM_OVERVIEW_WINDOW_SELECTOR_ITEM_H_ + +#include "base/compiler_specific.h" +#include "ui/gfx/rect.h" + +namespace aura { +class RootWindow; +class Window; +} + +namespace ash { + +// This class represents an item in overview mode. An item can have one or more +// windows, of which only one can be activated by keyboard (i.e. alt+tab) but +// any can be selected with a pointer (touch or mouse). +class WindowSelectorItem { + public: + WindowSelectorItem(); + virtual ~WindowSelectorItem(); + + // Returns the root window on which this item is shown. + virtual const aura::RootWindow* GetRootWindow() const = 0; + + // Returns the targeted window given the event |target| window. + // Returns NULL if no Window in this item was selected. + virtual aura::Window* TargetedWindow(const aura::Window* target) const = 0; + + // Restores |window| on exiting window overview rather than returning it + // to its previous state. + virtual void RestoreWindowOnExit(aura::Window* window) = 0; + + // Returns the |window| to activate on selecting of this item. + virtual aura::Window* SelectionWindow() const = 0; + + // Removes |window| from this item. Check empty() after calling this to see + // if the entire item is now empty. + virtual void RemoveWindow(const aura::Window* window) = 0; + + // Returns true if this item has no more selectable windows (i.e. after + // calling RemoveWindow for the last contained window). + virtual bool empty() const = 0; + + // Sets the bounds of this window selector item to |target_bounds| in the + // |root_window| root window. + void SetBounds(aura::RootWindow* root_window, + const gfx::Rect& target_bounds); + + // Returns the current bounds of this selector item. + const gfx::Rect& bounds() { return bounds_; } + + protected: + virtual void SetItemBounds(aura::RootWindow* root_window, + const gfx::Rect& target_bounds) = 0; + + private: + // The bounds this item is fit to. + gfx::Rect bounds_; + + DISALLOW_COPY_AND_ASSIGN(WindowSelectorItem); +}; + +} // namespace ash + +#endif // ASH_WM_OVERVIEW_WINDOW_SELECTOR_ITEM_H_ diff --git a/ash/wm/overview/window_selector_panels.cc b/ash/wm/overview/window_selector_panels.cc new file mode 100644 index 0000000..69e4602 --- /dev/null +++ b/ash/wm/overview/window_selector_panels.cc @@ -0,0 +1,177 @@ +// Copyright 2013 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/overview/window_selector_panels.h" + +#include "ash/screen_ash.h" +#include "ash/shell.h" +#include "ash/shell_window_ids.h" +#include "ash/wm/overview/scoped_transform_overview_window.h" +#include "ash/wm/panels/panel_layout_manager.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/window.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_animation_observer.h" +#include "ui/compositor/layer_animation_sequence.h" +#include "ui/views/widget/widget.h" + +namespace ash { + +namespace { + +const int kPanelCalloutFadeInDurationMilliseconds = 50; + +// This class extends ScopedTransformOverviewMode to hide and show the callout +// widget for a panel window when entering / leaving overview mode. +class ScopedTransformPanelWindow : public ScopedTransformOverviewWindow { + public: + ScopedTransformPanelWindow(aura::Window* window); + virtual ~ScopedTransformPanelWindow(); + + protected: + virtual void OnOverviewStarted() OVERRIDE; + + private: + // Returns the callout widget for the transformed panel. + views::Widget* GetCalloutWidget(); + + // Restores the callout visibility. + void RestoreCallout(); + + // Trigger relayout + void Relayout(); + + bool callout_visible_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTransformPanelWindow); +}; + +ScopedTransformPanelWindow::ScopedTransformPanelWindow(aura::Window* window) + : ScopedTransformOverviewWindow(window) { +} + +ScopedTransformPanelWindow::~ScopedTransformPanelWindow() { + // window() will be NULL if the window was destroyed. + if (window()) + RestoreCallout(); +} + +void ScopedTransformPanelWindow::OnOverviewStarted() { + ScopedTransformOverviewWindow::OnOverviewStarted(); + GetCalloutWidget()->GetLayer()->SetOpacity(0.0f); +} + +views::Widget* ScopedTransformPanelWindow::GetCalloutWidget() { + DCHECK(window()->parent()->id() == internal::kShellWindowId_PanelContainer); + internal::PanelLayoutManager* panel_layout_manager = + static_cast<internal::PanelLayoutManager*>( + window()->parent()->layout_manager()); + return panel_layout_manager->GetCalloutWidgetForPanel(window()); +} + +void ScopedTransformPanelWindow::RestoreCallout() { + scoped_ptr<ui::LayerAnimationSequence> sequence( + new ui::LayerAnimationSequence); + ui::LayerAnimationElement::AnimatableProperties paused_properties; + paused_properties.insert(ui::LayerAnimationElement::OPACITY); + sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement( + paused_properties, base::TimeDelta::FromMilliseconds( + ScopedTransformOverviewWindow::kTransitionMilliseconds))); + sequence->AddElement(ui::LayerAnimationElement::CreateOpacityElement(1, + base::TimeDelta::FromMilliseconds( + kPanelCalloutFadeInDurationMilliseconds))); + GetCalloutWidget()->GetLayer()->GetAnimator()->StartAnimation( + sequence.release()); +} + +} // namespace + +WindowSelectorPanels::WindowSelectorPanels() { +} + +WindowSelectorPanels::~WindowSelectorPanels() { +} + +void WindowSelectorPanels::AddWindow(aura::Window* window) { + transform_windows_.push_back(new ScopedTransformPanelWindow(window)); +} + +const aura::RootWindow* WindowSelectorPanels::GetRootWindow() const { + return transform_windows_.front()->window()->GetRootWindow(); +} + +aura::Window* WindowSelectorPanels::TargetedWindow( + const aura::Window* target) const { + for (WindowList::const_iterator iter = transform_windows_.begin(); + iter != transform_windows_.end(); ++iter) { + if ((*iter)->Contains(target)) + return (*iter)->window(); + } + return NULL; +} + +void WindowSelectorPanels::RestoreWindowOnExit(aura::Window* window) { + for (WindowList::iterator iter = transform_windows_.begin(); + iter != transform_windows_.end(); ++iter) { + if ((*iter)->Contains(window)) { + (*iter)->RestoreWindowOnExit(); + break; + } + } +} + +aura::Window* WindowSelectorPanels::SelectionWindow() const { + return transform_windows_.front()->window(); +} + +void WindowSelectorPanels::RemoveWindow(const aura::Window* window) { + for (WindowList::iterator iter = transform_windows_.begin(); + iter != transform_windows_.end(); ++iter) { + if ((*iter)->Contains(window)) { + (*iter)->OnWindowDestroyed(); + transform_windows_.erase(iter); + break; + } + } +} + +bool WindowSelectorPanels::empty() const { + return transform_windows_.empty(); +} + +void WindowSelectorPanels::SetItemBounds(aura::RootWindow* root_window, + const gfx::Rect& target_bounds) { + // Panel windows affect the position of each other. Restore all panel windows + // first in order to have the correct layout. + for (WindowList::iterator iter = transform_windows_.begin(); + iter != transform_windows_.end(); ++iter) { + (*iter)->RestoreWindow(); + } + 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); + } + gfx::Transform bounding_transform = + ScopedTransformOverviewWindow::GetTransformForRectPreservingAspectRatio( + bounding_rect, target_bounds); + 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()); + transform.Translate(bounding_rect.x() - bounds.x(), + bounding_rect.y() - bounds.y()); + transform.PreconcatTransform(bounding_transform); + transform.Translate(bounds.x() - bounding_rect.x(), + bounds.y() - bounding_rect.y()); + (*iter)->SetTransform(root_window, transform); + } +} + +} // namespace ash diff --git a/ash/wm/overview/window_selector_panels.h b/ash/wm/overview/window_selector_panels.h new file mode 100644 index 0000000..ebb0430 --- /dev/null +++ b/ash/wm/overview/window_selector_panels.h @@ -0,0 +1,54 @@ +// Copyright 2013 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_OVERVIEW_WINDOW_SELECTOR_PANELS_H_ +#define ASH_WM_OVERVIEW_WINDOW_SELECTOR_PANELS_H_ + +#include "ash/wm/overview/window_selector_item.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_vector.h" +#include "ui/gfx/rect.h" + +namespace aura { +class RootWindow; +class Window; +} + +namespace ash { + +class ScopedTransformOverviewWindow; + +// This class implements a window selector item containing one or more attached +// panel windows. These panels are grouped into a single overview item in +// overview mode and the callout arrows are hidden at this point. +class WindowSelectorPanels : public WindowSelectorItem { + public: + WindowSelectorPanels(); + virtual ~WindowSelectorPanels(); + + // Adds |window| to the selector item. This window should be an attached + // panel window. + void AddWindow(aura::Window* window); + + // WindowSelectorItem: + virtual const aura::RootWindow* GetRootWindow() const OVERRIDE; + virtual aura::Window* TargetedWindow( + const aura::Window* target) const OVERRIDE; + virtual void RestoreWindowOnExit(aura::Window* window) OVERRIDE; + virtual aura::Window* SelectionWindow() const OVERRIDE; + virtual void RemoveWindow(const aura::Window* window) OVERRIDE; + virtual bool empty() const OVERRIDE; + virtual void SetItemBounds(aura::RootWindow* root_window, + const gfx::Rect& target_bounds) OVERRIDE; + + private: + typedef ScopedVector<ScopedTransformOverviewWindow> WindowList; + WindowList transform_windows_; + + DISALLOW_COPY_AND_ASSIGN(WindowSelectorPanels); +}; + +} // namespace ash + +#endif // ASH_WM_OVERVIEW_WINDOW_SELECTOR_PANELS_H_ diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index 2142f9a..b58a543 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ash/launcher/launcher.h" #include "ash/root_window_controller.h" #include "ash/screen_ash.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" +#include "ash/test/launcher_test_api.h" +#include "ash/test/launcher_view_test_api.h" #include "ash/test/shell_test_api.h" +#include "ash/test/test_launcher_delegate.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/window_selector.h" #include "ash/wm/overview/window_selector_controller.h" @@ -34,10 +38,27 @@ class WindowSelectorTest : public test::AshTestBase { WindowSelectorTest() {} virtual ~WindowSelectorTest() {} + virtual void SetUp() OVERRIDE { + test::AshTestBase::SetUp(); + ASSERT_TRUE(test::TestLauncherDelegate::instance()); + + launcher_view_test_.reset(new test::LauncherViewTestAPI( + test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).launcher_view())); + launcher_view_test_->SetAnimationDuration(1); + } + aura::Window* CreateWindow(const gfx::Rect& bounds) { return CreateTestWindowInShellWithDelegate(&wd, -1, bounds); } + aura::Window* CreatePanelWindow(const gfx::Rect& bounds) { + aura::Window* window = CreateTestWindowInShellWithDelegateAndType( + NULL, aura::client::WINDOW_TYPE_PANEL, 0, bounds); + test::TestLauncherDelegate::instance()->AddLauncherItem(window); + launcher_view_test()->RunMessageLoopUntilAnimationsDone(); + return window; + } + bool WindowsOverlapping(aura::Window* window1, aura::Window* window2) { gfx::RectF window1_bounds = GetTransformedTargetBounds(window1); gfx::RectF window2_bounds = GetTransformedTargetBounds(window2); @@ -68,13 +89,15 @@ class WindowSelectorTest : public test::AshTestBase { } gfx::RectF GetTransformedBounds(aura::Window* window) { - gfx::RectF bounds(window->layer()->bounds()); + gfx::RectF bounds(ash::ScreenAsh::ConvertRectToScreen( + window->parent(), window->layer()->bounds())); window->layer()->transform().TransformRect(&bounds); return bounds; } gfx::RectF GetTransformedTargetBounds(aura::Window* window) { - gfx::RectF bounds(window->layer()->GetTargetBounds()); + gfx::RectF bounds(ash::ScreenAsh::ConvertRectToScreen( + window->parent(), window->layer()->GetTargetBounds())); window->layer()->GetTargetTransform().TransformRect(&bounds); return bounds; } @@ -95,8 +118,13 @@ class WindowSelectorTest : public test::AshTestBase { Shell::GetPrimaryRootWindow())->GetFocusedWindow(); } + test::LauncherViewTestAPI* launcher_view_test() { + return launcher_view_test_.get(); + } + private: aura::test::TestWindowDelegate wd; + scoped_ptr<test::LauncherViewTestAPI> launcher_view_test_; DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest); }; @@ -106,7 +134,11 @@ TEST_F(WindowSelectorTest, Basic) { gfx::Rect bounds(0, 0, 400, 400); scoped_ptr<aura::Window> window1(CreateWindow(bounds)); scoped_ptr<aura::Window> window2(CreateWindow(bounds)); + scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); + scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get())); + EXPECT_TRUE(WindowsOverlapping(window1.get(), panel1.get())); + EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); wm::ActivateWindow(window2.get()); EXPECT_FALSE(wm::IsActiveWindow(window1.get())); EXPECT_TRUE(wm::IsActiveWindow(window2.get())); @@ -117,6 +149,10 @@ TEST_F(WindowSelectorTest, Basic) { ToggleOverview(); EXPECT_EQ(NULL, GetFocusedWindow()); EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get())); + EXPECT_FALSE(WindowsOverlapping(window1.get(), panel1.get())); + // Panels 1 and 2 should still be overlapping being in a single selector + // item. + EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); // Clicking window 1 should activate it. ClickWindow(window1.get()); @@ -148,6 +184,81 @@ TEST_F(WindowSelectorTest, BasicCycle) { EXPECT_TRUE(wm::IsActiveWindow(window3.get())); } +// Tests cycles between panel and normal windows. +TEST_F(WindowSelectorTest, CyclePanels) { + gfx::Rect bounds(0, 0, 400, 400); + scoped_ptr<aura::Window> window1(CreateWindow(bounds)); + scoped_ptr<aura::Window> window2(CreateWindow(bounds)); + scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); + scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); + wm::ActivateWindow(window2.get()); + wm::ActivateWindow(window1.get()); + wm::ActivateWindow(panel2.get()); + wm::ActivateWindow(panel1.get()); + EXPECT_TRUE(wm::IsActiveWindow(panel1.get())); + + // Cycling once should select window1 since the panels are grouped into a + // single selectable item. + Cycle(WindowSelector::FORWARD); + StopCycling(); + EXPECT_TRUE(wm::IsActiveWindow(window1.get())); + + // Cycling again should select the most recently used panel. + Cycle(WindowSelector::FORWARD); + StopCycling(); + EXPECT_TRUE(wm::IsActiveWindow(panel1.get())); +} + +// Tests cycles between panel and normal windows. +TEST_F(WindowSelectorTest, CyclePanelsDestroyed) { + gfx::Rect bounds(0, 0, 400, 400); + scoped_ptr<aura::Window> window1(CreateWindow(bounds)); + scoped_ptr<aura::Window> window2(CreateWindow(bounds)); + scoped_ptr<aura::Window> window3(CreateWindow(bounds)); + scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); + scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); + wm::ActivateWindow(window3.get()); + wm::ActivateWindow(panel2.get()); + wm::ActivateWindow(panel1.get()); + wm::ActivateWindow(window2.get()); + wm::ActivateWindow(window1.get()); + EXPECT_TRUE(wm::IsActiveWindow(window1.get())); + + // Cycling once highlights window2. + Cycle(WindowSelector::FORWARD); + // All panels are destroyed. + panel1.reset(); + panel2.reset(); + // Cycling again should now select window3. + Cycle(WindowSelector::FORWARD); + StopCycling(); + EXPECT_TRUE(wm::IsActiveWindow(window3.get())); +} + +// Tests cycles between panel and normal windows. +TEST_F(WindowSelectorTest, CycleMruPanelDestroyed) { + gfx::Rect bounds(0, 0, 400, 400); + scoped_ptr<aura::Window> window1(CreateWindow(bounds)); + scoped_ptr<aura::Window> window2(CreateWindow(bounds)); + scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds)); + scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds)); + wm::ActivateWindow(panel2.get()); + wm::ActivateWindow(panel1.get()); + wm::ActivateWindow(window2.get()); + wm::ActivateWindow(window1.get()); + EXPECT_TRUE(wm::IsActiveWindow(window1.get())); + + // Cycling once highlights window2. + Cycle(WindowSelector::FORWARD); + // Panel 1 is the next item as the MRU panel, removing it should make panel 2 + // the next window to be selected. + panel1.reset(); + // Cycling again should now select window3. + Cycle(WindowSelector::FORWARD); + StopCycling(); + EXPECT_TRUE(wm::IsActiveWindow(panel2.get())); +} + // Tests that a newly created window aborts overview. TEST_F(WindowSelectorTest, NewWindowCancelsOveriew) { gfx::Rect bounds(0, 0, 400, 400); @@ -257,24 +368,40 @@ TEST_F(WindowSelectorTest, MultipleDisplays) { if (!SupportsMultipleDisplays()) return; - UpdateDisplay("400x400,400x400"); + UpdateDisplay("600x400,600x400"); Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); - - scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 100, 100))); - scoped_ptr<aura::Window> window2(CreateWindow(gfx::Rect(0, 0, 100, 100))); - scoped_ptr<aura::Window> window3(CreateWindow(gfx::Rect(450, 0, 100, 100))); - scoped_ptr<aura::Window> window4(CreateWindow(gfx::Rect(450, 0, 100, 100))); + gfx::Rect bounds1(0, 0, 200, 200); + gfx::Rect bounds2(650, 0, 200, 200); + + scoped_ptr<aura::Window> window1(CreateWindow(bounds1)); + scoped_ptr<aura::Window> window2(CreateWindow(bounds1)); + scoped_ptr<aura::Window> window3(CreateWindow(bounds2)); + scoped_ptr<aura::Window> window4(CreateWindow(bounds2)); + scoped_ptr<aura::Window> panel1(CreatePanelWindow(bounds1)); + scoped_ptr<aura::Window> panel2(CreatePanelWindow(bounds1)); + scoped_ptr<aura::Window> panel3(CreatePanelWindow(bounds2)); + scoped_ptr<aura::Window> panel4(CreatePanelWindow(bounds2)); EXPECT_EQ(root_windows[0], window1->GetRootWindow()); EXPECT_EQ(root_windows[0], window2->GetRootWindow()); EXPECT_EQ(root_windows[1], window3->GetRootWindow()); EXPECT_EQ(root_windows[1], window4->GetRootWindow()); + EXPECT_EQ(root_windows[0], panel1->GetRootWindow()); + EXPECT_EQ(root_windows[0], panel2->GetRootWindow()); + EXPECT_EQ(root_windows[1], panel3->GetRootWindow()); + EXPECT_EQ(root_windows[1], panel4->GetRootWindow()); + // In overview mode, each window remains in the same root window. ToggleOverview(); EXPECT_EQ(root_windows[0], window1->GetRootWindow()); EXPECT_EQ(root_windows[0], window2->GetRootWindow()); EXPECT_EQ(root_windows[1], window3->GetRootWindow()); EXPECT_EQ(root_windows[1], window4->GetRootWindow()); + EXPECT_EQ(root_windows[0], panel1->GetRootWindow()); + EXPECT_EQ(root_windows[0], panel2->GetRootWindow()); + EXPECT_EQ(root_windows[1], panel3->GetRootWindow()); + EXPECT_EQ(root_windows[1], panel4->GetRootWindow()); + root_windows[0]->bounds().Contains( ToEnclosingRect(GetTransformedBounds(window1.get()))); root_windows[0]->bounds().Contains( @@ -283,6 +410,18 @@ TEST_F(WindowSelectorTest, MultipleDisplays) { ToEnclosingRect(GetTransformedBounds(window3.get()))); root_windows[1]->bounds().Contains( ToEnclosingRect(GetTransformedBounds(window4.get()))); + + root_windows[0]->bounds().Contains( + ToEnclosingRect(GetTransformedBounds(panel1.get()))); + root_windows[0]->bounds().Contains( + ToEnclosingRect(GetTransformedBounds(panel2.get()))); + root_windows[1]->bounds().Contains( + ToEnclosingRect(GetTransformedBounds(panel3.get()))); + root_windows[1]->bounds().Contains( + ToEnclosingRect(GetTransformedBounds(panel4.get()))); + EXPECT_TRUE(WindowsOverlapping(panel1.get(), panel2.get())); + EXPECT_TRUE(WindowsOverlapping(panel3.get(), panel4.get())); + EXPECT_FALSE(WindowsOverlapping(panel1.get(), panel3.get())); } } // namespace internal diff --git a/ash/wm/overview/window_selector_window.cc b/ash/wm/overview/window_selector_window.cc index 226c782..a421600 100644 --- a/ash/wm/overview/window_selector_window.cc +++ b/ash/wm/overview/window_selector_window.cc @@ -4,239 +4,51 @@ #include "ash/wm/overview/window_selector_window.h" -#include "ash/shell.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/client/screen_position_client.h" -#include "ui/aura/root_window.h" #include "ui/aura/window.h" -#include "ui/compositor/layer_animation_observer.h" -#include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/gfx/display.h" -#include "ui/gfx/interpolated_transform.h" -#include "ui/gfx/transform_util.h" -#include "ui/views/corewm/shadow_types.h" -#include "ui/views/corewm/window_animations.h" -#include "ui/views/corewm/window_util.h" -#include "ui/views/widget/widget.h" namespace ash { -namespace { - -const int kOverviewWindowTransitionMilliseconds = 100; - -// Creates a copy of |window| with |recreated_layer| in the |target_root|. -views::Widget* CreateCopyOfWindow(aura::RootWindow* target_root, - aura::Window* src_window, - ui::Layer* recreated_layer) { - views::Widget* widget = new views::Widget; - views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); - params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; - params.parent = src_window->parent(); - params.can_activate = false; - params.keep_on_top = true; - widget->set_focus_on_creation(false); - widget->Init(params); - widget->SetVisibilityChangedAnimationsEnabled(false); - std::string name = src_window->name() + " (Copy)"; - widget->GetNativeWindow()->SetName(name); - views::corewm::SetShadowType(widget->GetNativeWindow(), - views::corewm::SHADOW_TYPE_RECTANGULAR); - - // Set the bounds in the target root window. - gfx::Display target_display = - Shell::GetScreen()->GetDisplayNearestWindow(target_root); - aura::client::ScreenPositionClient* screen_position_client = - aura::client::GetScreenPositionClient(src_window->GetRootWindow()); - if (screen_position_client && target_display.is_valid()) { - screen_position_client->SetBounds(widget->GetNativeWindow(), - src_window->GetBoundsInScreen(), target_display); - } else { - widget->SetBounds(src_window->GetBoundsInScreen()); - } - widget->StackAbove(src_window); - - // Move the |recreated_layer| to the newly created window. - recreated_layer->set_delegate(src_window->layer()->delegate()); - gfx::Rect layer_bounds = recreated_layer->bounds(); - layer_bounds.set_origin(gfx::Point(0, 0)); - recreated_layer->SetBounds(layer_bounds); - recreated_layer->SetVisible(false); - recreated_layer->parent()->Remove(recreated_layer); - - aura::Window* window = widget->GetNativeWindow(); - recreated_layer->SetVisible(true); - window->layer()->Add(recreated_layer); - window->layer()->StackAtTop(recreated_layer); - window->layer()->SetOpacity(1); - window->Show(); - return widget; -} - -// An observer which closes the widget and deletes the layer after an -// animation finishes. -class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver { - public: - CleanupWidgetAfterAnimationObserver(views::Widget* widget, ui::Layer* layer); - - virtual void OnLayerAnimationEnded( - ui::LayerAnimationSequence* sequence) OVERRIDE; - virtual void OnLayerAnimationAborted( - ui::LayerAnimationSequence* sequence) OVERRIDE; - virtual void OnLayerAnimationScheduled( - ui::LayerAnimationSequence* sequence) OVERRIDE; - - protected: - virtual ~CleanupWidgetAfterAnimationObserver(); - - private: - views::Widget* widget_; - ui::Layer* layer_; - - DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver); -}; - -CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver( - views::Widget* widget, - ui::Layer* layer) - : widget_(widget), - layer_(layer) { - widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this); -} - -void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded( - ui::LayerAnimationSequence* sequence) { - delete this; +WindowSelectorWindow::WindowSelectorWindow(aura::Window* window) + : transform_window_(window) { } -void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted( - ui::LayerAnimationSequence* sequence) { - delete this; +WindowSelectorWindow::~WindowSelectorWindow() { } -void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled( - ui::LayerAnimationSequence* sequence) { +const aura::RootWindow* WindowSelectorWindow::GetRootWindow() const { + return transform_window_.window()->GetRootWindow(); } -CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() { - widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this); - widget_->Close(); - widget_ = NULL; - if (layer_) { - views::corewm::DeepDeleteLayers(layer_); - layer_ = NULL; - } +aura::Window* WindowSelectorWindow::TargetedWindow( + const aura::Window* target) const { + if (transform_window_.Contains(target)) + return transform_window_.window(); + return NULL; } -// The animation settings used for window selector animations. -class WindowSelectorAnimationSettings - : public ui::ScopedLayerAnimationSettings { - public: - WindowSelectorAnimationSettings(aura::Window* window) : - ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()) { - SetPreemptionStrategy( - ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - SetTransitionDuration(base::TimeDelta::FromMilliseconds( - kOverviewWindowTransitionMilliseconds)); - } - - virtual ~WindowSelectorAnimationSettings() { - } -}; - -} // namespace - -WindowSelectorWindow::WindowSelectorWindow(aura::Window* window) - : window_(window), - window_copy_(NULL), - layer_(NULL), - minimized_(window->GetProperty(aura::client::kShowStateKey) == - ui::SHOW_STATE_MINIMIZED), - original_transform_(window->layer()->GetTargetTransform()) { -} - -WindowSelectorWindow::~WindowSelectorWindow() { - if (window_) { - WindowSelectorAnimationSettings animation_settings(window_); - gfx::Transform transform; - window_->SetTransform(original_transform_); - if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) != - ui::SHOW_STATE_MINIMIZED) { - // Setting opacity 0 and visible false ensures that the property change - // to SHOW_STATE_MINIMIZED will not animate the window from its original - // bounds to the minimized position. - window_->layer()->SetOpacity(0); - window_->layer()->SetVisible(false); - window_->SetProperty(aura::client::kShowStateKey, - ui::SHOW_STATE_MINIMIZED); - } - } - // If a copy of the window was created, clean it up. - if (window_copy_) { - if (window_) { - // If the initial window wasn't destroyed, the copy needs to be animated - // out. CleanupWidgetAfterAnimationObserver will destroy the widget and - // layer after the animation is complete. - new CleanupWidgetAfterAnimationObserver(window_copy_, layer_); - WindowSelectorAnimationSettings animation_settings( - window_copy_->GetNativeWindow()); - window_copy_->GetNativeWindow()->SetTransform(original_transform_); - } else { - window_copy_->Close(); - if (layer_) - views::corewm::DeepDeleteLayers(layer_); - } - window_copy_ = NULL; - layer_ = NULL; - } +void WindowSelectorWindow::RestoreWindowOnExit(aura::Window* window) { + transform_window_.RestoreWindowOnExit(); } -bool WindowSelectorWindow::Contains(const aura::Window* window) const { - if (window_copy_ && window_copy_->GetNativeWindow()->Contains(window)) - return true; - return window_->Contains(window); +aura::Window* WindowSelectorWindow::SelectionWindow() const { + return transform_window_.window(); } -void WindowSelectorWindow::RestoreWindowOnExit() { - minimized_ = false; - original_transform_ = gfx::Transform(); +void WindowSelectorWindow::RemoveWindow(const aura::Window* window) { + DCHECK_EQ(transform_window_.window(), window); + transform_window_.OnWindowDestroyed(); } -void WindowSelectorWindow::OnWindowDestroyed() { - window_ = NULL; +bool WindowSelectorWindow::empty() const { + return transform_window_.window() == NULL; } -void WindowSelectorWindow::TransformToFitBounds( - aura::RootWindow* root_window, - const gfx::Rect& target_bounds) { - if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) == - ui::SHOW_STATE_MINIMIZED) { - window_->Show(); - } - fit_bounds_ = target_bounds; - const gfx::Rect bounds = window_->GetBoundsInScreen(); - float scale = std::min(1.0f, - std::min(static_cast<float>(target_bounds.width()) / bounds.width(), - static_cast<float>(target_bounds.height()) / bounds.height())); - gfx::Transform transform; - gfx::Vector2d offset( - 0.5 * (target_bounds.width() - scale * bounds.width()), - 0.5 * (target_bounds.height() - scale * bounds.height())); - transform.Translate(target_bounds.x() - bounds.x() + offset.x(), - target_bounds.y() - bounds.y() + offset.y()); - transform.Scale(scale, scale); - if (root_window != window_->GetRootWindow()) { - if (!window_copy_) { - DCHECK(!layer_); - layer_ = views::corewm::RecreateWindowLayers(window_, true); - window_copy_ = CreateCopyOfWindow(root_window, window_, layer_); - } - WindowSelectorAnimationSettings animation_settings( - window_copy_->GetNativeWindow()); - window_copy_->GetNativeWindow()->SetTransform(transform); - } - WindowSelectorAnimationSettings animation_settings(window_); - window_->SetTransform(transform); +void WindowSelectorWindow::SetItemBounds(aura::RootWindow* root_window, + const gfx::Rect& target_bounds) { + gfx::Rect bounding_rect = transform_window_.window()->GetBoundsInScreen(); + transform_window_.SetTransform(root_window, + ScopedTransformOverviewWindow::GetTransformForRectPreservingAspectRatio( + transform_window_.window()->GetBoundsInScreen(), target_bounds)); } } // namespace ash diff --git a/ash/wm/overview/window_selector_window.h b/ash/wm/overview/window_selector_window.h index 050ac93..7c28209 100644 --- a/ash/wm/overview/window_selector_window.h +++ b/ash/wm/overview/window_selector_window.h @@ -5,76 +5,38 @@ #ifndef ASH_WM_OVERVIEW_WINDOW_SELECTOR_WINDOW_H_ #define ASH_WM_OVERVIEW_WINDOW_SELECTOR_WINDOW_H_ +#include "ash/wm/overview/scoped_transform_overview_window.h" +#include "ash/wm/overview/window_selector_item.h" #include "base/compiler_specific.h" #include "ui/gfx/rect.h" -#include "ui/gfx/transform.h" namespace aura { class RootWindow; class Window; } -namespace ui { -class Layer; -} - -namespace views { -class Widget; -} - namespace ash { -// Manages a window in the overview mode. This class transitions the window -// to the best fit within the available overview rectangle, copying it if the -// window is sent to another display and restores the window state on -// deletion. -class WindowSelectorWindow { +// This implements a window overview item with a single window which can be +// selected. +class WindowSelectorWindow : public WindowSelectorItem { public: - explicit WindowSelectorWindow(aura::Window* window); + WindowSelectorWindow(aura::Window* window); virtual ~WindowSelectorWindow(); - aura::Window* window() { return window_; } - const aura::Window* window() const { return window_; } - - // Returns true if this window selector window contains the |target|. This is - // used to determine if an event targetted this window. - bool Contains(const aura::Window* target) const; - - // Restores this window on exit rather than returning it to a minimized state - // if it was minimized on entering overview mode. - void RestoreWindowOnExit(); - - // Informs the WindowSelectorWindow that the window being watched was - // destroyed. This resets the internal window pointer to avoid calling - // anything on the window at destruction time. - void OnWindowDestroyed(); - - // Applies a transform to the window to fit within |target_bounds| while - // maintaining its aspect ratio. - void TransformToFitBounds(aura::RootWindow* root_window, - const gfx::Rect& target_bounds); - - const gfx::Rect& bounds() { return fit_bounds_; } + // WindowSelectorItem: + virtual const aura::RootWindow* GetRootWindow() const OVERRIDE; + virtual aura::Window* TargetedWindow( + const aura::Window* target) const OVERRIDE; + virtual void RestoreWindowOnExit(aura::Window* window) OVERRIDE; + virtual aura::Window* SelectionWindow() const OVERRIDE; + virtual void RemoveWindow(const aura::Window* window) OVERRIDE; + virtual bool empty() const OVERRIDE; + virtual void SetItemBounds(aura::RootWindow* root_window, + const gfx::Rect& target_bounds) OVERRIDE; private: - // A weak pointer to the real window in the overview. - aura::Window* window_; - - // A copy of the window used to transition the window to another root. - views::Widget* window_copy_; - - // A weak pointer to a deep copy of the window's layers. - ui::Layer* layer_; - - // If true, the window was minimized and should be restored if the window - // was not selected. - bool minimized_; - - // The original transform of the window before entering overview mode. - gfx::Transform original_transform_; - - // The bounds this window is fit to. - gfx::Rect fit_bounds_; + ScopedTransformOverviewWindow transform_window_; DISALLOW_COPY_AND_ASSIGN(WindowSelectorWindow); }; diff --git a/ash/wm/panels/panel_layout_manager.cc b/ash/wm/panels/panel_layout_manager.cc index 65f15e6..ea6f9e9 100644 --- a/ash/wm/panels/panel_layout_manager.cc +++ b/ash/wm/panels/panel_layout_manager.cc @@ -330,6 +330,15 @@ void PanelLayoutManager::ToggleMinimize(aura::Window* panel) { } } +views::Widget* PanelLayoutManager::GetCalloutWidgetForPanel( + aura::Window* panel) { + DCHECK(panel->parent() == panel_container_); + PanelList::iterator found = + std::find(panel_windows_.begin(), panel_windows_.end(), panel); + DCHECK(found != panel_windows_.end()); + return found->callout_widget; +} + //////////////////////////////////////////////////////////////////////////////// // PanelLayoutManager, aura::LayoutManager implementation: void PanelLayoutManager::OnWindowResized() { @@ -832,7 +841,8 @@ void PanelLayoutManager::UpdateCallouts() { ui::Layer* layer = callout_widget->GetNativeWindow()->layer(); // If the panel is not over the callout position or has just become visible // then fade in the callout. - if (distance_until_over_panel > 0 || layer->GetTargetOpacity() < 1) { + if ((distance_until_over_panel > 0 || layer->GetTargetOpacity() < 1) && + panel->layer()->GetTargetTransform().IsIdentity()) { if (distance_until_over_panel > 0 && slide_distance >= distance_until_over_panel) { layer->SetOpacity(0); diff --git a/ash/wm/panels/panel_layout_manager.h b/ash/wm/panels/panel_layout_manager.h index f3b128c..9c35b00 100644 --- a/ash/wm/panels/panel_layout_manager.h +++ b/ash/wm/panels/panel_layout_manager.h @@ -71,6 +71,9 @@ class ASH_EXPORT PanelLayoutManager void ToggleMinimize(aura::Window* panel); + // Returns the callout widget (arrow) for |panel|. + views::Widget* GetCalloutWidgetForPanel(aura::Window* panel); + ash::Launcher* launcher() { return launcher_; } void SetLauncher(ash::Launcher* launcher); |