1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ash/wm/overview/window_selector_panels.h"
6
7#include "ash/screen_util.h"
8#include "ash/shell.h"
9#include "ash/shell_window_ids.h"
10#include "ash/wm/overview/scoped_transform_overview_window.h"
11#include "ash/wm/overview/transparent_activate_window_button.h"
12#include "ash/wm/panels/panel_layout_manager.h"
13#include "ash/wm/window_util.h"
14#include "ui/aura/window.h"
15#include "ui/compositor/layer.h"
16#include "ui/compositor/layer_animation_observer.h"
17#include "ui/compositor/layer_animation_sequence.h"
18#include "ui/views/controls/button/button.h"
19
20namespace ash {
21
22namespace {
23
24// This class extends ScopedTransformOverviewMode to hide and show the callout
25// widget for a panel window when entering / leaving overview mode, as well as
26// to add a transparent button for each panel window.
27class ScopedTransformPanelWindow : public ScopedTransformOverviewWindow {
28 public:
29  explicit ScopedTransformPanelWindow(aura::Window* window);
30  virtual ~ScopedTransformPanelWindow();
31
32  // ScopedTransformOverviewWindow overrides:
33  virtual void PrepareForOverview() OVERRIDE;
34
35  virtual void SetTransform(
36      aura::Window* root_window,
37      const gfx::Transform& transform,
38      bool animate) OVERRIDE;
39
40 private:
41  // Returns the panel window bounds after the transformation.
42  gfx::Rect GetTransformedBounds();
43
44  scoped_ptr<TransparentActivateWindowButton> window_button_;
45
46  DISALLOW_COPY_AND_ASSIGN(ScopedTransformPanelWindow);
47};
48
49ScopedTransformPanelWindow::ScopedTransformPanelWindow(aura::Window* window)
50    : ScopedTransformOverviewWindow(window) {
51}
52
53ScopedTransformPanelWindow::~ScopedTransformPanelWindow() {
54}
55
56void ScopedTransformPanelWindow::PrepareForOverview() {
57  ScopedTransformOverviewWindow::PrepareForOverview();
58  window_button_.reset(new TransparentActivateWindowButton(window()));
59}
60
61void ScopedTransformPanelWindow::SetTransform(
62    aura::Window* root_window,
63    const gfx::Transform& transform,
64    bool animate) {
65  ScopedTransformOverviewWindow::SetTransform(root_window, transform, animate);
66  window_button_->SetBounds(GetTransformedBounds());
67}
68
69gfx::Rect ScopedTransformPanelWindow::GetTransformedBounds() {
70  gfx::RectF bounds(ScreenUtil::ConvertRectToScreen(
71          window()->GetRootWindow(), window()->layer()->bounds()));
72  gfx::Transform new_transform;
73  new_transform.Translate(bounds.x(),
74                          bounds.y());
75  new_transform.PreconcatTransform(window()->layer()->GetTargetTransform());
76  new_transform.Translate(-bounds.x(),
77                          -bounds.y());
78  new_transform.TransformRect(&bounds);
79  return ToEnclosingRect(bounds);
80}
81
82}  // namespace
83
84WindowSelectorPanels::WindowSelectorPanels(aura::Window* panels_root_window)
85    : panels_root_window_(panels_root_window) {
86  static_cast<PanelLayoutManager*>(
87      Shell::GetContainer(panels_root_window_, kShellWindowId_PanelContainer)->
88          layout_manager())->SetShowCalloutWidgets(false);
89}
90
91WindowSelectorPanels::~WindowSelectorPanels() {
92  static_cast<PanelLayoutManager*>(
93      Shell::GetContainer(panels_root_window_, kShellWindowId_PanelContainer)->
94          layout_manager())->SetShowCalloutWidgets(true);
95  for (WindowList::iterator iter = transform_windows_.begin();
96      iter != transform_windows_.end(); iter++) {
97    (*iter)->window()->RemoveObserver(this);
98  }
99}
100
101void WindowSelectorPanels::AddWindow(aura::Window* window) {
102  DCHECK(window->GetRootWindow() == panels_root_window_);
103  window->AddObserver(this);
104  transform_windows_.push_back(new ScopedTransformPanelWindow(window));
105}
106
107aura::Window* WindowSelectorPanels::GetRootWindow() {
108  return transform_windows_.front()->window()->GetRootWindow();
109}
110
111bool WindowSelectorPanels::HasSelectableWindow(const aura::Window* window) {
112  for (WindowList::const_iterator iter = transform_windows_.begin();
113       iter != transform_windows_.end(); ++iter) {
114    if ((*iter)->window() == window)
115      return true;
116  }
117  return false;
118}
119
120bool WindowSelectorPanels::Contains(const aura::Window* target) {
121  for (WindowList::const_iterator iter = transform_windows_.begin();
122       iter != transform_windows_.end(); ++iter) {
123    if ((*iter)->Contains(target))
124      return true;
125  }
126  return false;
127}
128
129void WindowSelectorPanels::RestoreWindowOnExit(aura::Window* window) {
130  for (WindowList::iterator iter = transform_windows_.begin();
131       iter != transform_windows_.end(); ++iter) {
132    if ((*iter)->Contains(window)) {
133      (*iter)->RestoreWindowOnExit();
134      break;
135    }
136  }
137}
138
139aura::Window* WindowSelectorPanels::SelectionWindow() {
140  return transform_windows_.front()->window();
141}
142
143void WindowSelectorPanels::RemoveWindow(const aura::Window* window) {
144  for (WindowList::iterator iter = transform_windows_.begin();
145       iter != transform_windows_.end(); ++iter) {
146    if ((*iter)->window() == window) {
147      (*iter)->window()->RemoveObserver(this);
148      (*iter)->OnWindowDestroyed();
149      transform_windows_.erase(iter);
150      break;
151    }
152  }
153  WindowSelectorItem::RemoveWindow(window);
154}
155
156bool WindowSelectorPanels::empty() const {
157  return transform_windows_.empty();
158}
159
160void WindowSelectorPanels::PrepareForOverview() {
161  // |panel_windows| will hold all the windows in the panel container, sorted
162  // according to their stacking order.
163  const aura::Window::Windows panels =
164      transform_windows_.front()->window()->parent()->children();
165
166  // Call PrepareForOverview() in the reverse stacking order so that the
167  // transparent windows that handle the events are in the correct stacking
168  // order.
169  size_t transformed_windows = 0;
170  for (aura::Window::Windows::const_reverse_iterator iter = panels.rbegin();
171      iter != panels.rend(); iter++) {
172    for (size_t j = 0; j < transform_windows_.size(); ++j) {
173      if (transform_windows_[j]->window() == (*iter)) {
174        transform_windows_[j]->PrepareForOverview();
175        transformed_windows++;
176      }
177    }
178  }
179  DCHECK(transformed_windows == transform_windows_.size());
180}
181
182void WindowSelectorPanels::SetItemBounds(aura::Window* root_window,
183                                         const gfx::Rect& target_bounds,
184                                         bool animate) {
185  gfx::Rect bounding_rect;
186  for (WindowList::iterator iter = transform_windows_.begin();
187       iter != transform_windows_.end(); ++iter) {
188    bounding_rect.Union((*iter)->GetBoundsInScreen());
189  }
190  set_bounds(ScopedTransformOverviewWindow::
191      ShrinkRectToFitPreservingAspectRatio(bounding_rect, target_bounds));
192  gfx::Transform bounding_transform =
193      ScopedTransformOverviewWindow::GetTransformForRect(bounding_rect,
194                                                         bounds());
195  for (WindowList::iterator iter = transform_windows_.begin();
196       iter != transform_windows_.end(); ++iter) {
197    gfx::Transform transform;
198    gfx::Rect bounds = (*iter)->GetBoundsInScreen();
199    transform.Translate(bounding_rect.x() - bounds.x(),
200                        bounding_rect.y() - bounds.y());
201    transform.PreconcatTransform(bounding_transform);
202    transform.Translate(bounds.x() - bounding_rect.x(),
203                        bounds.y() - bounding_rect.y());
204    (*iter)->SetTransform(root_window, transform, animate);
205  }
206}
207
208void WindowSelectorPanels::SetOpacity(float opacity) {
209  // TODO(flackr): find a way to make panels that are hidden behind other panels
210  // look nice.
211  for (WindowList::iterator iter = transform_windows_.begin();
212       iter != transform_windows_.end(); iter++) {
213    (*iter)->window()->layer()->SetOpacity(opacity);
214  }
215  WindowSelectorItem::SetOpacity(opacity);
216}
217
218}  // namespace ash
219