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.
5 #include "ui/wm/core/shadow_controller.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "ui/aura/client/window_tree_client.h"
12 #include "ui/aura/test/aura_test_base.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_event_dispatcher.h"
15 #include "ui/compositor/layer.h"
16 #include "ui/wm/core/default_activation_client.h"
17 #include "ui/wm/core/shadow.h"
18 #include "ui/wm/core/shadow_types.h"
19 #include "ui/wm/core/window_util.h"
20 #include "ui/wm/core/wm_state.h"
21 #include "ui/wm/public/activation_client.h"
25 class ShadowControllerTest
: public aura::test::AuraTestBase
{
27 ShadowControllerTest() {}
28 virtual ~ShadowControllerTest() {}
30 virtual void SetUp() OVERRIDE
{
31 wm_state_
.reset(new wm::WMState
);
32 AuraTestBase::SetUp();
33 new wm::DefaultActivationClient(root_window());
34 aura::client::ActivationClient
* activation_client
=
35 aura::client::GetActivationClient(root_window());
36 shadow_controller_
.reset(new ShadowController(activation_client
));
38 virtual void TearDown() OVERRIDE
{
39 shadow_controller_
.reset();
40 AuraTestBase::TearDown();
45 ShadowController
* shadow_controller() { return shadow_controller_
.get(); }
47 void ActivateWindow(aura::Window
* window
) {
49 DCHECK(window
->GetRootWindow());
50 aura::client::GetActivationClient(window
->GetRootWindow())->ActivateWindow(
55 scoped_ptr
<ShadowController
> shadow_controller_
;
56 scoped_ptr
<wm::WMState
> wm_state_
;
58 DISALLOW_COPY_AND_ASSIGN(ShadowControllerTest
);
61 // Tests that various methods in Window update the Shadow object as expected.
62 TEST_F(ShadowControllerTest
, Shadow
) {
63 scoped_ptr
<aura::Window
> window(new aura::Window(NULL
));
64 window
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
65 window
->Init(aura::WINDOW_LAYER_TEXTURED
);
66 ParentWindow(window
.get());
68 // We should create the shadow before the window is visible (the shadow's
69 // layer won't get drawn yet since it's a child of the window's layer).
70 ShadowController::TestApi
api(shadow_controller());
71 const Shadow
* shadow
= api
.GetShadowForWindow(window
.get());
72 ASSERT_TRUE(shadow
!= NULL
);
73 EXPECT_TRUE(shadow
->layer()->visible());
75 // The shadow should remain visible after window visibility changes.
77 EXPECT_TRUE(shadow
->layer()->visible());
79 EXPECT_TRUE(shadow
->layer()->visible());
81 // If the shadow is disabled, it should be hidden.
82 SetShadowType(window
.get(), SHADOW_TYPE_NONE
);
84 EXPECT_FALSE(shadow
->layer()->visible());
85 SetShadowType(window
.get(), SHADOW_TYPE_RECTANGULAR
);
86 EXPECT_TRUE(shadow
->layer()->visible());
88 // The shadow's layer should be a child of the window's layer.
89 EXPECT_EQ(window
->layer(), shadow
->layer()->parent());
91 window
->parent()->RemoveChild(window
.get());
92 aura::Window
* window_ptr
= window
.get();
94 EXPECT_TRUE(api
.GetShadowForWindow(window_ptr
) == NULL
);
97 // Tests that the window's shadow's bounds are updated correctly.
98 TEST_F(ShadowControllerTest
, ShadowBounds
) {
99 scoped_ptr
<aura::Window
> window(new aura::Window(NULL
));
100 window
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
101 window
->Init(aura::WINDOW_LAYER_TEXTURED
);
102 ParentWindow(window
.get());
105 const gfx::Rect
kOldBounds(20, 30, 400, 300);
106 window
->SetBounds(kOldBounds
);
108 // When the shadow is first created, it should use the window's size (but
109 // remain at the origin, since it's a child of the window's layer).
110 SetShadowType(window
.get(), SHADOW_TYPE_RECTANGULAR
);
111 ShadowController::TestApi
api(shadow_controller());
112 const Shadow
* shadow
= api
.GetShadowForWindow(window
.get());
113 ASSERT_TRUE(shadow
!= NULL
);
114 EXPECT_EQ(gfx::Rect(kOldBounds
.size()).ToString(),
115 shadow
->content_bounds().ToString());
117 // When we change the window's bounds, the shadow's should be updated too.
118 gfx::Rect
kNewBounds(50, 60, 500, 400);
119 window
->SetBounds(kNewBounds
);
120 EXPECT_EQ(gfx::Rect(kNewBounds
.size()).ToString(),
121 shadow
->content_bounds().ToString());
124 // Tests that activating a window changes the shadow style.
125 TEST_F(ShadowControllerTest
, ShadowStyle
) {
126 ShadowController::TestApi
api(shadow_controller());
128 scoped_ptr
<aura::Window
> window1(new aura::Window(NULL
));
129 window1
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
130 window1
->Init(aura::WINDOW_LAYER_TEXTURED
);
131 ParentWindow(window1
.get());
132 window1
->SetBounds(gfx::Rect(10, 20, 300, 400));
134 ActivateWindow(window1
.get());
136 // window1 is active, so style should have active appearance.
137 Shadow
* shadow1
= api
.GetShadowForWindow(window1
.get());
138 ASSERT_TRUE(shadow1
!= NULL
);
139 EXPECT_EQ(Shadow::STYLE_ACTIVE
, shadow1
->style());
141 // Create another window and activate it.
142 scoped_ptr
<aura::Window
> window2(new aura::Window(NULL
));
143 window2
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
144 window2
->Init(aura::WINDOW_LAYER_TEXTURED
);
145 ParentWindow(window2
.get());
146 window2
->SetBounds(gfx::Rect(11, 21, 301, 401));
148 ActivateWindow(window2
.get());
150 // window1 is now inactive, so shadow should go inactive.
151 Shadow
* shadow2
= api
.GetShadowForWindow(window2
.get());
152 ASSERT_TRUE(shadow2
!= NULL
);
153 EXPECT_EQ(Shadow::STYLE_INACTIVE
, shadow1
->style());
154 EXPECT_EQ(Shadow::STYLE_ACTIVE
, shadow2
->style());
157 // Tests that we use smaller shadows for tooltips and menus.
158 TEST_F(ShadowControllerTest
, SmallShadowsForTooltipsAndMenus
) {
159 ShadowController::TestApi
api(shadow_controller());
161 scoped_ptr
<aura::Window
> tooltip_window(new aura::Window(NULL
));
162 tooltip_window
->SetType(ui::wm::WINDOW_TYPE_TOOLTIP
);
163 tooltip_window
->Init(aura::WINDOW_LAYER_TEXTURED
);
164 ParentWindow(tooltip_window
.get());
165 tooltip_window
->SetBounds(gfx::Rect(10, 20, 300, 400));
166 tooltip_window
->Show();
168 Shadow
* tooltip_shadow
= api
.GetShadowForWindow(tooltip_window
.get());
169 ASSERT_TRUE(tooltip_shadow
!= NULL
);
170 EXPECT_EQ(Shadow::STYLE_SMALL
, tooltip_shadow
->style());
172 scoped_ptr
<aura::Window
> menu_window(new aura::Window(NULL
));
173 menu_window
->SetType(ui::wm::WINDOW_TYPE_MENU
);
174 menu_window
->Init(aura::WINDOW_LAYER_TEXTURED
);
175 ParentWindow(menu_window
.get());
176 menu_window
->SetBounds(gfx::Rect(10, 20, 300, 400));
179 Shadow
* menu_shadow
= api
.GetShadowForWindow(tooltip_window
.get());
180 ASSERT_TRUE(menu_shadow
!= NULL
);
181 EXPECT_EQ(Shadow::STYLE_SMALL
, menu_shadow
->style());
184 // http://crbug.com/120210 - transient parents of certain types of transients
185 // should not lose their shadow when they lose activation to the transient.
186 TEST_F(ShadowControllerTest
, TransientParentKeepsActiveShadow
) {
187 ShadowController::TestApi
api(shadow_controller());
189 scoped_ptr
<aura::Window
> window1(new aura::Window(NULL
));
190 window1
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
191 window1
->Init(aura::WINDOW_LAYER_TEXTURED
);
192 ParentWindow(window1
.get());
193 window1
->SetBounds(gfx::Rect(10, 20, 300, 400));
195 ActivateWindow(window1
.get());
197 // window1 is active, so style should have active appearance.
198 Shadow
* shadow1
= api
.GetShadowForWindow(window1
.get());
199 ASSERT_TRUE(shadow1
!= NULL
);
200 EXPECT_EQ(Shadow::STYLE_ACTIVE
, shadow1
->style());
202 // Create a window that is transient to window1, and that has the 'hide on
203 // deactivate' property set. Upon activation, window1 should still have an
205 scoped_ptr
<aura::Window
> window2(new aura::Window(NULL
));
206 window2
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
207 window2
->Init(aura::WINDOW_LAYER_TEXTURED
);
208 ParentWindow(window2
.get());
209 window2
->SetBounds(gfx::Rect(11, 21, 301, 401));
210 AddTransientChild(window1
.get(), window2
.get());
211 aura::client::SetHideOnDeactivate(window2
.get(), true);
213 ActivateWindow(window2
.get());
215 // window1 is now inactive, but its shadow should still appear active.
216 EXPECT_EQ(Shadow::STYLE_ACTIVE
, shadow1
->style());
219 TEST_F(ShadowControllerTest
, AlwaysActive
) {
220 ShadowController::TestApi
api(shadow_controller());
222 scoped_ptr
<aura::Window
> window1(new aura::Window(NULL
));
223 window1
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
224 window1
->Init(aura::WINDOW_LAYER_TEXTURED
);
225 ParentWindow(window1
.get());
226 window1
->SetBounds(gfx::Rect(10, 20, 300, 400));
227 SetShadowType(window1
.get(), SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE
);
230 // Showing the window with SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE should
231 // have active shadow.
232 EXPECT_EQ(Shadow::STYLE_ACTIVE
,
233 api
.GetShadowForWindow(window1
.get())->style());
235 scoped_ptr
<aura::Window
> window2(new aura::Window(NULL
));
236 window2
->SetType(ui::wm::WINDOW_TYPE_NORMAL
);
237 window2
->Init(aura::WINDOW_LAYER_TEXTURED
);
238 ParentWindow(window2
.get());
239 window2
->SetBounds(gfx::Rect(11, 21, 301, 401));
242 // Setting SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE to the visible window
243 // should set the active shadow.
244 EXPECT_EQ(Shadow::STYLE_INACTIVE
,
245 api
.GetShadowForWindow(window2
.get())->style());
246 SetShadowType(window2
.get(), SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE
);
247 EXPECT_EQ(Shadow::STYLE_ACTIVE
,
248 api
.GetShadowForWindow(window2
.get())->style());
250 // Activation should not change the shadow style.
251 ActivateWindow(window2
.get());
252 EXPECT_EQ(Shadow::STYLE_ACTIVE
,
253 api
.GetShadowForWindow(window1
.get())->style());
254 EXPECT_EQ(Shadow::STYLE_ACTIVE
,
255 api
.GetShadowForWindow(window2
.get())->style());
257 ActivateWindow(window1
.get());
258 EXPECT_EQ(Shadow::STYLE_ACTIVE
,
259 api
.GetShadowForWindow(window1
.get())->style());
260 EXPECT_EQ(Shadow::STYLE_ACTIVE
,
261 api
.GetShadowForWindow(window2
.get())->style());
263 // Restore the style to plain RECTANGULAR and make sure the inactive window
264 // gets the inactive shadow.
265 SetShadowType(window1
.get(), SHADOW_TYPE_RECTANGULAR
);
266 SetShadowType(window2
.get(), SHADOW_TYPE_RECTANGULAR
);
267 EXPECT_EQ(Shadow::STYLE_ACTIVE
,
268 api
.GetShadowForWindow(window1
.get())->style());
269 EXPECT_EQ(Shadow::STYLE_INACTIVE
,
270 api
.GetShadowForWindow(window2
.get())->style());