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