1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "athena/wm/window_manager_impl.h"
6
7#include <algorithm>
8
9#include "athena/screen/public/screen_manager.h"
10#include "athena/util/container_priorities.h"
11#include "athena/wm/bezel_controller.h"
12#include "athena/wm/public/window_manager_observer.h"
13#include "athena/wm/split_view_controller.h"
14#include "athena/wm/title_drag_controller.h"
15#include "athena/wm/window_list_provider_impl.h"
16#include "athena/wm/window_overview_mode.h"
17#include "base/bind.h"
18#include "base/logging.h"
19#include "ui/aura/layout_manager.h"
20#include "ui/aura/window.h"
21#include "ui/compositor/closure_animation_observer.h"
22#include "ui/compositor/scoped_layer_animation_settings.h"
23#include "ui/gfx/display.h"
24#include "ui/gfx/screen.h"
25#include "ui/wm/core/shadow_controller.h"
26#include "ui/wm/core/window_util.h"
27#include "ui/wm/core/wm_state.h"
28#include "ui/wm/public/activation_client.h"
29#include "ui/wm/public/window_types.h"
30
31namespace athena {
32namespace {
33class WindowManagerImpl* instance = NULL;
34
35void SetWindowState(aura::Window* window,
36                    const gfx::Rect& bounds,
37                    const gfx::Transform& transform) {
38  window->SetBounds(bounds);
39  window->SetTransform(transform);
40}
41
42}  // namespace
43
44class AthenaContainerLayoutManager : public aura::LayoutManager {
45 public:
46  AthenaContainerLayoutManager();
47  virtual ~AthenaContainerLayoutManager();
48
49 private:
50  // aura::LayoutManager:
51  virtual void OnWindowResized() OVERRIDE;
52  virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
53  virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE;
54  virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE;
55  virtual void OnChildWindowVisibilityChanged(aura::Window* child,
56                                              bool visible) OVERRIDE;
57  virtual void SetChildBounds(aura::Window* child,
58                              const gfx::Rect& requested_bounds) OVERRIDE;
59
60  DISALLOW_COPY_AND_ASSIGN(AthenaContainerLayoutManager);
61};
62
63AthenaContainerLayoutManager::AthenaContainerLayoutManager() {
64}
65
66AthenaContainerLayoutManager::~AthenaContainerLayoutManager() {
67}
68
69void AthenaContainerLayoutManager::OnWindowResized() {
70  // Resize all the existing windows.
71  const aura::Window::Windows& list =
72      instance->window_list_provider_->GetWindowList();
73  const gfx::Size work_area =
74      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size();
75  bool is_splitview = instance->split_view_controller_->IsSplitViewModeActive();
76  gfx::Size split_size;
77  if (is_splitview) {
78    CHECK(instance->split_view_controller_->left_window());
79    split_size =
80        instance->split_view_controller_->left_window()->bounds().size();
81  }
82
83  for (aura::Window::Windows::const_iterator iter = list.begin();
84       iter != list.end();
85       ++iter) {
86    aura::Window* window = *iter;
87    if (is_splitview) {
88      if (window == instance->split_view_controller_->left_window())
89        window->SetBounds(gfx::Rect(split_size));
90      else if (window == instance->split_view_controller_->right_window())
91        window->SetBounds(
92            gfx::Rect(gfx::Point(split_size.width(), 0), split_size));
93      else
94        window->SetBounds(gfx::Rect(work_area));
95    } else {
96      window->SetBounds(gfx::Rect(work_area));
97    }
98  }
99}
100
101void AthenaContainerLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
102  if (!instance->window_list_provider_->IsWindowInList(child))
103    return;
104
105  if (instance->split_view_controller_->IsSplitViewModeActive() &&
106      !instance->IsOverviewModeActive()) {
107    instance->split_view_controller_->ReplaceWindow(
108        instance->split_view_controller_->left_window(), child);
109  } else {
110    gfx::Size size =
111        gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size();
112    child->SetBounds(gfx::Rect(size));
113  }
114
115  if (instance->IsOverviewModeActive()) {
116    // TODO(pkotwicz|oshima). Creating a new window should only exit overview
117    // mode if the new window is activated. crbug.com/415266
118    instance->OnSelectWindow(child);
119  }
120}
121
122void AthenaContainerLayoutManager::OnWillRemoveWindowFromLayout(
123    aura::Window* child) {
124}
125
126void AthenaContainerLayoutManager::OnWindowRemovedFromLayout(
127    aura::Window* child) {
128}
129
130void AthenaContainerLayoutManager::OnChildWindowVisibilityChanged(
131    aura::Window* child,
132    bool visible) {
133}
134
135void AthenaContainerLayoutManager::SetChildBounds(
136    aura::Window* child,
137    const gfx::Rect& requested_bounds) {
138  if (!requested_bounds.IsEmpty())
139    SetChildBoundsDirect(child, requested_bounds);
140}
141
142WindowManagerImpl::WindowManagerImpl() {
143  ScreenManager::ContainerParams params("DefaultContainer", CP_DEFAULT);
144  params.can_activate_children = true;
145  container_.reset(ScreenManager::Get()->CreateDefaultContainer(params));
146  container_->SetLayoutManager(new AthenaContainerLayoutManager);
147  container_->AddObserver(this);
148  window_list_provider_.reset(new WindowListProviderImpl(container_.get()));
149  bezel_controller_.reset(new BezelController(container_.get()));
150  split_view_controller_.reset(
151      new SplitViewController(container_.get(), window_list_provider_.get()));
152  AddObserver(split_view_controller_.get());
153  bezel_controller_->set_left_right_delegate(split_view_controller_.get());
154  container_->AddPreTargetHandler(bezel_controller_.get());
155  title_drag_controller_.reset(new TitleDragController(container_.get(), this));
156  wm_state_.reset(new wm::WMState());
157  aura::client::ActivationClient* activation_client =
158      aura::client::GetActivationClient(container_->GetRootWindow());
159  shadow_controller_.reset(new wm::ShadowController(activation_client));
160  instance = this;
161  InstallAccelerators();
162}
163
164WindowManagerImpl::~WindowManagerImpl() {
165  overview_.reset();
166  RemoveObserver(split_view_controller_.get());
167  split_view_controller_.reset();
168  window_list_provider_.reset();
169  if (container_) {
170    container_->RemoveObserver(this);
171    container_->RemovePreTargetHandler(bezel_controller_.get());
172  }
173  // |title_drag_controller_| needs to be reset before |container_|.
174  title_drag_controller_.reset();
175  container_.reset();
176  instance = NULL;
177}
178
179void WindowManagerImpl::ToggleSplitView() {
180  if (IsOverviewModeActive())
181    return;
182
183  if (split_view_controller_->IsSplitViewModeActive()) {
184    split_view_controller_->DeactivateSplitMode();
185    FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit());
186    // Relayout so that windows are maximzied.
187    container_->layout_manager()->OnWindowResized();
188  } else if (split_view_controller_->CanActivateSplitViewMode()) {
189    FOR_EACH_OBSERVER(WindowManagerObserver,
190                      observers_,
191                      OnSplitViewModeEnter());
192    split_view_controller_->ActivateSplitMode(NULL, NULL, NULL);
193  }
194}
195
196void WindowManagerImpl::ToggleOverview() {
197  if (IsOverviewModeActive()) {
198    SetInOverview(false);
199
200    // Activate the window which was active prior to entering overview.
201    const aura::Window::Windows windows =
202        window_list_provider_->GetWindowList();
203    if (!windows.empty()) {
204      aura::Window* window = windows.back();
205      // Show the window in case the exit overview animation has finished and
206      // |window| was hidden.
207      window->Show();
208
209      wm::ActivateWindow(window);
210    }
211  } else {
212    SetInOverview(true);
213  }
214}
215
216bool WindowManagerImpl::IsOverviewModeActive() {
217  return overview_;
218}
219
220void WindowManagerImpl::SetInOverview(bool active) {
221  bool in_overview = !!overview_;
222  if (active == in_overview)
223    return;
224
225  bezel_controller_->set_left_right_delegate(
226      active ? NULL : split_view_controller_.get());
227  if (active) {
228    FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeEnter());
229
230    // Note: The window_list_provider_ resembles the exact window list of the
231    // container, so no re-stacking is required before showing the OverviewMode.
232    overview_ = WindowOverviewMode::Create(
233        container_.get(), window_list_provider_.get(),
234        split_view_controller_.get(), this);
235  } else {
236    overview_.reset();
237    FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeExit());
238  }
239}
240
241void WindowManagerImpl::InstallAccelerators() {
242  const AcceleratorData accelerator_data[] = {
243      {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_NONE, CMD_TOGGLE_OVERVIEW,
244       AF_NONE},
245      {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_CONTROL_DOWN,
246       CMD_TOGGLE_SPLIT_VIEW, AF_NONE},
247  };
248  AcceleratorManager::Get()->RegisterAccelerators(
249      accelerator_data, arraysize(accelerator_data), this);
250}
251
252void WindowManagerImpl::AddObserver(WindowManagerObserver* observer) {
253  observers_.AddObserver(observer);
254}
255
256void WindowManagerImpl::RemoveObserver(WindowManagerObserver* observer) {
257  observers_.RemoveObserver(observer);
258}
259
260void WindowManagerImpl::ToggleSplitViewForTest() {
261  ToggleSplitView();
262}
263
264WindowListProvider* WindowManagerImpl::GetWindowListProvider() {
265  return window_list_provider_.get();
266}
267
268void WindowManagerImpl::OnSelectWindow(aura::Window* window) {
269  SetInOverview(false);
270
271  // Show the window in case the exit overview animation has finished and
272  // |window| was hidden.
273  window->Show();
274
275  wm::ActivateWindow(window);
276
277  if (split_view_controller_->IsSplitViewModeActive()) {
278    split_view_controller_->DeactivateSplitMode();
279    FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit());
280  }
281  // If |window| does not have the size of the work-area, then make sure it is
282  // resized.
283  const gfx::Size work_area =
284      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size();
285  if (window->GetTargetBounds().size() != work_area) {
286    const gfx::Rect& window_bounds = window->bounds();
287    const gfx::Rect desired_bounds(work_area);
288    gfx::Transform transform;
289    transform.Translate(desired_bounds.x() - window_bounds.x(),
290                        desired_bounds.y() - window_bounds.y());
291    transform.Scale(desired_bounds.width() / window_bounds.width(),
292                    desired_bounds.height() / window_bounds.height());
293    ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
294    settings.SetPreemptionStrategy(
295        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
296    settings.AddObserver(
297        new ui::ClosureAnimationObserver(base::Bind(&SetWindowState,
298                                                    base::Unretained(window),
299                                                    desired_bounds,
300                                                    gfx::Transform())));
301    window->SetTransform(transform);
302  }
303}
304
305void WindowManagerImpl::OnSelectSplitViewWindow(aura::Window* left,
306                                                aura::Window* right,
307                                                aura::Window* to_activate) {
308  SetInOverview(false);
309  FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeEnter());
310  split_view_controller_->ActivateSplitMode(left, right, to_activate);
311}
312
313void WindowManagerImpl::OnWindowDestroying(aura::Window* window) {
314  if (window == container_)
315    container_.reset();
316}
317
318bool WindowManagerImpl::IsCommandEnabled(int command_id) const {
319  return true;
320}
321
322bool WindowManagerImpl::OnAcceleratorFired(int command_id,
323                                           const ui::Accelerator& accelerator) {
324  switch (command_id) {
325    case CMD_TOGGLE_OVERVIEW:
326      ToggleOverview();
327      break;
328    case CMD_TOGGLE_SPLIT_VIEW:
329      ToggleSplitView();
330      break;
331  }
332  return true;
333}
334
335aura::Window* WindowManagerImpl::GetWindowBehind(aura::Window* window) {
336  const aura::Window::Windows& windows = window_list_provider_->GetWindowList();
337  aura::Window::Windows::const_reverse_iterator iter =
338      std::find(windows.rbegin(), windows.rend(), window);
339  CHECK(iter != windows.rend());
340  ++iter;
341  aura::Window* behind = NULL;
342  if (iter != windows.rend())
343    behind = *iter++;
344
345  if (split_view_controller_->IsSplitViewModeActive()) {
346    aura::Window* left = split_view_controller_->left_window();
347    aura::Window* right = split_view_controller_->right_window();
348    CHECK(window == left || window == right);
349    if (behind == left || behind == right)
350      behind = (iter == windows.rend()) ? NULL : *iter;
351  }
352
353  return behind;
354}
355
356void WindowManagerImpl::OnTitleDragStarted(aura::Window* window) {
357  aura::Window* next_window = GetWindowBehind(window);
358  if (!next_window)
359    return;
360  // Make sure |window| is active.
361  wm::ActivateWindow(window);
362
363  // Make sure |next_window| is visibile.
364  next_window->Show();
365
366  // Position |next_window| correctly (left aligned if it's larger than
367  // |window|, and center aligned otherwise).
368  int dx = window->bounds().x() - next_window->bounds().x();
369  if (next_window->bounds().width() < window->bounds().width())
370    dx -= (next_window->bounds().width() - window->bounds().width()) / 2;
371
372  if (dx) {
373    gfx::Transform transform;
374    transform.Translate(dx, 0);
375    next_window->SetTransform(transform);
376  }
377}
378
379void WindowManagerImpl::OnTitleDragCompleted(aura::Window* window) {
380  aura::Window* next_window = GetWindowBehind(window);
381  if (!next_window)
382    return;
383  if (split_view_controller_->IsSplitViewModeActive()) {
384    split_view_controller_->ReplaceWindow(window, next_window);
385  } else {
386    ui::ScopedLayerAnimationSettings
387        settings(next_window->layer()->GetAnimator());
388    settings.AddObserver(new ui::ClosureAnimationObserver(
389        base::Bind(&SetWindowState,
390                   base::Unretained(next_window),
391                   window->bounds(),
392                   gfx::Transform())));
393
394    gfx::Transform transform;
395    transform.Scale(window->bounds().width() / next_window->bounds().width(),
396                    window->bounds().height() / next_window->bounds().height());
397    transform.Translate(window->bounds().x() - next_window->bounds().x(), 0);
398    next_window->SetTransform(transform);
399
400    wm::ActivateWindow(next_window);
401  }
402  window->Hide();
403}
404
405void WindowManagerImpl::OnTitleDragCanceled(aura::Window* window) {
406  aura::Window* next_window = GetWindowBehind(window);
407  if (!next_window)
408    return;
409  next_window->SetTransform(gfx::Transform());
410  next_window->Hide();
411}
412
413// static
414WindowManager* WindowManager::Create() {
415  DCHECK(!instance);
416  new WindowManagerImpl;
417  DCHECK(instance);
418  return instance;
419}
420
421// static
422void WindowManager::Shutdown() {
423  DCHECK(instance);
424  delete instance;
425  DCHECK(!instance);
426}
427
428// static
429WindowManager* WindowManager::Get() {
430  DCHECK(instance);
431  return instance;
432}
433
434}  // namespace athena
435