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 <algorithm> 8 #include <vector> 9 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" 22 23 namespace wm { 24 25 class ShadowControllerTest : public aura::test::AuraTestBase { 26 public: 27 ShadowControllerTest() {} 28 virtual ~ShadowControllerTest() {} 29 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)); 37 } 38 virtual void TearDown() OVERRIDE { 39 shadow_controller_.reset(); 40 AuraTestBase::TearDown(); 41 wm_state_.reset(); 42 } 43 44 protected: 45 ShadowController* shadow_controller() { return shadow_controller_.get(); } 46 47 void ActivateWindow(aura::Window* window) { 48 DCHECK(window); 49 DCHECK(window->GetRootWindow()); 50 aura::client::GetActivationClient(window->GetRootWindow())->ActivateWindow( 51 window); 52 } 53 54 private: 55 scoped_ptr<ShadowController> shadow_controller_; 56 scoped_ptr<wm::WMState> wm_state_; 57 58 DISALLOW_COPY_AND_ASSIGN(ShadowControllerTest); 59 }; 60 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()); 67 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()); 74 75 // The shadow should remain visible after window visibility changes. 76 window->Show(); 77 EXPECT_TRUE(shadow->layer()->visible()); 78 window->Hide(); 79 EXPECT_TRUE(shadow->layer()->visible()); 80 81 // If the shadow is disabled, it should be hidden. 82 SetShadowType(window.get(), SHADOW_TYPE_NONE); 83 window->Show(); 84 EXPECT_FALSE(shadow->layer()->visible()); 85 SetShadowType(window.get(), SHADOW_TYPE_RECTANGULAR); 86 EXPECT_TRUE(shadow->layer()->visible()); 87 88 // The shadow's layer should be a child of the window's layer. 89 EXPECT_EQ(window->layer(), shadow->layer()->parent()); 90 91 window->parent()->RemoveChild(window.get()); 92 aura::Window* window_ptr = window.get(); 93 window.reset(); 94 EXPECT_TRUE(api.GetShadowForWindow(window_ptr) == NULL); 95 } 96 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()); 103 window->Show(); 104 105 const gfx::Rect kOldBounds(20, 30, 400, 300); 106 window->SetBounds(kOldBounds); 107 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()); 116 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()); 122 } 123 124 // Tests that activating a window changes the shadow style. 125 TEST_F(ShadowControllerTest, ShadowStyle) { 126 ShadowController::TestApi api(shadow_controller()); 127 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)); 133 window1->Show(); 134 ActivateWindow(window1.get()); 135 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()); 140 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)); 147 window2->Show(); 148 ActivateWindow(window2.get()); 149 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()); 155 } 156 157 // Tests that we use smaller shadows for tooltips and menus. 158 TEST_F(ShadowControllerTest, SmallShadowsForTooltipsAndMenus) { 159 ShadowController::TestApi api(shadow_controller()); 160 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(); 167 168 Shadow* tooltip_shadow = api.GetShadowForWindow(tooltip_window.get()); 169 ASSERT_TRUE(tooltip_shadow != NULL); 170 EXPECT_EQ(Shadow::STYLE_SMALL, tooltip_shadow->style()); 171 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)); 177 menu_window->Show(); 178 179 Shadow* menu_shadow = api.GetShadowForWindow(tooltip_window.get()); 180 ASSERT_TRUE(menu_shadow != NULL); 181 EXPECT_EQ(Shadow::STYLE_SMALL, menu_shadow->style()); 182 } 183 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()); 188 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)); 194 window1->Show(); 195 ActivateWindow(window1.get()); 196 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()); 201 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 204 // active shadow. 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); 212 window2->Show(); 213 ActivateWindow(window2.get()); 214 215 // window1 is now inactive, but its shadow should still appear active. 216 EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style()); 217 } 218 219 TEST_F(ShadowControllerTest, AlwaysActive) { 220 ShadowController::TestApi api(shadow_controller()); 221 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); 228 window1->Show(); 229 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()); 234 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)); 240 window2->Show(); 241 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()); 249 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()); 256 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()); 262 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()); 271 } 272 273 } // namespace wm 274