1// Copyright (c) 2012 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 "ui/views/corewm/shadow_controller.h"
6
7#include <utility>
8
9#include "base/command_line.h"
10#include "base/logging.h"
11#include "base/memory/linked_ptr.h"
12#include "base/scoped_observer.h"
13#include "ui/aura/client/activation_client.h"
14#include "ui/aura/env.h"
15#include "ui/aura/env_observer.h"
16#include "ui/aura/root_window.h"
17#include "ui/aura/window.h"
18#include "ui/aura/window_observer.h"
19#include "ui/compositor/layer.h"
20#include "ui/views/corewm/shadow.h"
21#include "ui/views/corewm/shadow_types.h"
22
23using std::make_pair;
24
25namespace views {
26namespace corewm {
27
28namespace {
29
30ShadowType GetShadowTypeFromWindow(aura::Window* window) {
31  switch (window->type()) {
32    case aura::client::WINDOW_TYPE_NORMAL:
33    case aura::client::WINDOW_TYPE_PANEL:
34    case aura::client::WINDOW_TYPE_MENU:
35    case aura::client::WINDOW_TYPE_TOOLTIP:
36      return SHADOW_TYPE_RECTANGULAR;
37    default:
38      break;
39  }
40  return SHADOW_TYPE_NONE;
41}
42
43bool ShouldUseSmallShadowForWindow(aura::Window* window) {
44  switch (window->type()) {
45    case aura::client::WINDOW_TYPE_MENU:
46    case aura::client::WINDOW_TYPE_TOOLTIP:
47      return true;
48    default:
49      break;
50  }
51  return false;
52}
53
54// Returns the shadow style to be applied to |losing_active| when it is losing
55// active to |gaining_active|. |gaining_active| may be of a type that hides when
56// inactive, and as such we do not want to render |losing_active| as inactive.
57Shadow::Style GetShadowStyleForWindowLosingActive(
58    aura::Window* losing_active,
59    aura::Window* gaining_active) {
60  if (gaining_active && aura::client::GetHideOnDeactivate(gaining_active)) {
61    aura::Window::Windows::const_iterator it =
62        std::find(losing_active->transient_children().begin(),
63                  losing_active->transient_children().end(),
64                  gaining_active);
65    if (it != losing_active->transient_children().end())
66      return Shadow::STYLE_ACTIVE;
67  }
68  return Shadow::STYLE_INACTIVE;
69}
70
71}  // namespace
72
73// ShadowController::Impl ------------------------------------------------------
74
75// Real implementation of the ShadowController. ShadowController observes
76// ActivationChangeObserver, which are per ActivationClient, where as there is
77// only a single Impl (as it observes all window creation by way of an
78// EnvObserver).
79class ShadowController::Impl :
80      public aura::EnvObserver,
81      public aura::WindowObserver,
82      public base::RefCounted<Impl> {
83 public:
84  // Returns the singleton instance, destroyed when there are no more refs.
85  static Impl* GetInstance();
86
87  // aura::EnvObserver override:
88  virtual void OnWindowInitialized(aura::Window* window) OVERRIDE;
89
90  // aura::WindowObserver overrides:
91  virtual void OnWindowPropertyChanged(
92      aura::Window* window, const void* key, intptr_t old) OVERRIDE;
93  virtual void OnWindowBoundsChanged(
94      aura::Window* window,
95      const gfx::Rect& old_bounds,
96      const gfx::Rect& new_bounds) OVERRIDE;
97  virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
98
99 private:
100  friend class base::RefCounted<Impl>;
101  friend class ShadowController;
102  friend class ShadowController::TestApi;
103
104  typedef std::map<aura::Window*, linked_ptr<Shadow> > WindowShadowMap;
105
106  Impl();
107  virtual ~Impl();
108
109  // Forwarded from ShadowController.
110  void OnWindowActivated(aura::Window* gained_active,
111                         aura::Window* lost_active);
112
113  // Checks if |window| is visible and contains a property requesting a shadow.
114  bool ShouldShowShadowForWindow(aura::Window* window) const;
115
116  // Returns |window|'s shadow from |window_shadows_|, or NULL if no shadow
117  // exists.
118  Shadow* GetShadowForWindow(aura::Window* window);
119
120  // Updates the shadow styles for windows when activation changes.
121  void HandleWindowActivationChange(aura::Window* gaining_active,
122                                    aura::Window* losing_active);
123
124  // Shows or hides |window|'s shadow as needed (creating the shadow if
125  // necessary).
126  void HandlePossibleShadowVisibilityChange(aura::Window* window);
127
128  // Creates a new shadow for |window| and stores it in |window_shadows_|.  The
129  // shadow's bounds are initialized and it is added to the window's layer.
130  void CreateShadowForWindow(aura::Window* window);
131
132  WindowShadowMap window_shadows_;
133
134  ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_;
135
136  static Impl* instance_;
137
138  DISALLOW_COPY_AND_ASSIGN(Impl);
139};
140
141// static
142ShadowController::Impl* ShadowController::Impl::instance_ = NULL;
143
144// static
145ShadowController::Impl* ShadowController::Impl::GetInstance() {
146  if (!instance_)
147    instance_ = new Impl();
148  return instance_;
149}
150
151void ShadowController::Impl::OnWindowInitialized(aura::Window* window) {
152  observer_manager_.Add(window);
153  SetShadowType(window, GetShadowTypeFromWindow(window));
154  HandlePossibleShadowVisibilityChange(window);
155}
156
157void ShadowController::Impl::OnWindowPropertyChanged(aura::Window* window,
158                                                     const void* key,
159                                                     intptr_t old) {
160  if (key == kShadowTypeKey) {
161    HandlePossibleShadowVisibilityChange(window);
162    return;
163  }
164}
165
166void ShadowController::Impl::OnWindowBoundsChanged(
167    aura::Window* window,
168    const gfx::Rect& old_bounds,
169    const gfx::Rect& new_bounds) {
170  Shadow* shadow = GetShadowForWindow(window);
171  if (shadow)
172    shadow->SetContentBounds(gfx::Rect(new_bounds.size()));
173}
174
175void ShadowController::Impl::OnWindowDestroyed(aura::Window* window) {
176  window_shadows_.erase(window);
177  observer_manager_.Remove(window);
178}
179
180void ShadowController::Impl::OnWindowActivated(aura::Window* gained_active,
181                                               aura::Window* lost_active) {
182  if (gained_active) {
183    Shadow* shadow = GetShadowForWindow(gained_active);
184    if (shadow && !ShouldUseSmallShadowForWindow(gained_active))
185      shadow->SetStyle(Shadow::STYLE_ACTIVE);
186  }
187  if (lost_active) {
188    Shadow* shadow = GetShadowForWindow(lost_active);
189    if (shadow && !ShouldUseSmallShadowForWindow(lost_active)) {
190      shadow->SetStyle(GetShadowStyleForWindowLosingActive(lost_active,
191                                                           gained_active));
192    }
193  }
194}
195
196bool ShadowController::Impl::ShouldShowShadowForWindow(
197    aura::Window* window) const {
198  const ShadowType type = GetShadowType(window);
199  switch (type) {
200    case SHADOW_TYPE_NONE:
201      return false;
202    case SHADOW_TYPE_RECTANGULAR:
203      return true;
204    default:
205      NOTREACHED() << "Unknown shadow type " << type;
206      return false;
207  }
208}
209
210Shadow* ShadowController::Impl::GetShadowForWindow(aura::Window* window) {
211  WindowShadowMap::const_iterator it = window_shadows_.find(window);
212  return it != window_shadows_.end() ? it->second.get() : NULL;
213}
214
215void ShadowController::Impl::HandlePossibleShadowVisibilityChange(
216    aura::Window* window) {
217  const bool should_show = ShouldShowShadowForWindow(window);
218  Shadow* shadow = GetShadowForWindow(window);
219  if (shadow)
220    shadow->layer()->SetVisible(should_show);
221  else if (should_show && !shadow)
222    CreateShadowForWindow(window);
223}
224
225void ShadowController::Impl::CreateShadowForWindow(aura::Window* window) {
226  linked_ptr<Shadow> shadow(new Shadow());
227  window_shadows_.insert(make_pair(window, shadow));
228
229  shadow->Init(ShouldUseSmallShadowForWindow(window) ?
230               Shadow::STYLE_SMALL : Shadow::STYLE_ACTIVE);
231  shadow->SetContentBounds(gfx::Rect(window->bounds().size()));
232  shadow->layer()->SetVisible(ShouldShowShadowForWindow(window));
233  window->layer()->Add(shadow->layer());
234}
235
236ShadowController::Impl::Impl()
237    : observer_manager_(this) {
238  aura::Env::GetInstance()->AddObserver(this);
239}
240
241ShadowController::Impl::~Impl() {
242  DCHECK_EQ(instance_, this);
243  aura::Env::GetInstance()->RemoveObserver(this);
244  instance_ = NULL;
245}
246
247// ShadowController ------------------------------------------------------------
248
249ShadowController::ShadowController(
250    aura::client::ActivationClient* activation_client)
251    : activation_client_(activation_client),
252      impl_(Impl::GetInstance()) {
253  // Watch for window activation changes.
254  activation_client_->AddObserver(this);
255}
256
257ShadowController::~ShadowController() {
258  activation_client_->RemoveObserver(this);
259}
260
261void ShadowController::OnWindowActivated(aura::Window* gained_active,
262                                         aura::Window* lost_active) {
263  impl_->OnWindowActivated(gained_active, lost_active);
264}
265
266// ShadowController::TestApi ---------------------------------------------------
267
268Shadow* ShadowController::TestApi::GetShadowForWindow(aura::Window* window) {
269  return controller_->impl_->GetShadowForWindow(window);
270}
271
272}  // namespace corewm
273}  // namespace views
274