Home | History | Annotate | Download | only in corewm
      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 <algorithm>
      8 #include <vector>
      9 
     10 #include "base/memory/scoped_ptr.h"
     11 #include "ui/aura/client/activation_client.h"
     12 #include "ui/aura/root_window.h"
     13 #include "ui/aura/test/aura_test_base.h"
     14 #include "ui/aura/window.h"
     15 #include "ui/compositor/layer.h"
     16 #include "ui/views/corewm/shadow.h"
     17 #include "ui/views/corewm/shadow_types.h"
     18 
     19 namespace views {
     20 namespace corewm {
     21 
     22 class ShadowControllerTest : public aura::test::AuraTestBase {
     23  public:
     24   ShadowControllerTest() {}
     25   virtual ~ShadowControllerTest() {}
     26 
     27   virtual void SetUp() OVERRIDE {
     28     AuraTestBase::SetUp();
     29     aura::client::ActivationClient* activation_client =
     30         aura::client::GetActivationClient(root_window());
     31     shadow_controller_.reset(new ShadowController(activation_client));
     32   }
     33   virtual void TearDown() OVERRIDE {
     34     shadow_controller_.reset();
     35     AuraTestBase::TearDown();
     36   }
     37 
     38  protected:
     39   ShadowController* shadow_controller() { return shadow_controller_.get(); }
     40 
     41   void ActivateWindow(aura::Window* window) {
     42     DCHECK(window);
     43     DCHECK(window->GetRootWindow());
     44     aura::client::GetActivationClient(window->GetRootWindow())->ActivateWindow(
     45         window);
     46   }
     47 
     48  private:
     49   scoped_ptr<ShadowController> shadow_controller_;
     50 
     51   DISALLOW_COPY_AND_ASSIGN(ShadowControllerTest);
     52 };
     53 
     54 // Tests that various methods in Window update the Shadow object as expected.
     55 TEST_F(ShadowControllerTest, Shadow) {
     56   scoped_ptr<aura::Window> window(new aura::Window(NULL));
     57   window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     58   window->Init(ui::LAYER_TEXTURED);
     59   SetDefaultParentByPrimaryRootWindow(window.get());
     60 
     61   // We should create the shadow before the window is visible (the shadow's
     62   // layer won't get drawn yet since it's a child of the window's layer).
     63   ShadowController::TestApi api(shadow_controller());
     64   const Shadow* shadow = api.GetShadowForWindow(window.get());
     65   ASSERT_TRUE(shadow != NULL);
     66   EXPECT_TRUE(shadow->layer()->visible());
     67 
     68   // The shadow should remain visible after window visibility changes.
     69   window->Show();
     70   EXPECT_TRUE(shadow->layer()->visible());
     71   window->Hide();
     72   EXPECT_TRUE(shadow->layer()->visible());
     73 
     74   // If the shadow is disabled, it should be hidden.
     75   SetShadowType(window.get(), SHADOW_TYPE_NONE);
     76   window->Show();
     77   EXPECT_FALSE(shadow->layer()->visible());
     78   SetShadowType(window.get(), SHADOW_TYPE_RECTANGULAR);
     79   EXPECT_TRUE(shadow->layer()->visible());
     80 
     81   // The shadow's layer should be a child of the window's layer.
     82   EXPECT_EQ(window->layer(), shadow->layer()->parent());
     83 
     84   window->parent()->RemoveChild(window.get());
     85   aura::Window* window_ptr = window.get();
     86   window.reset();
     87   EXPECT_TRUE(api.GetShadowForWindow(window_ptr) == NULL);
     88 }
     89 
     90 // Tests that the window's shadow's bounds are updated correctly.
     91 TEST_F(ShadowControllerTest, ShadowBounds) {
     92   scoped_ptr<aura::Window> window(new aura::Window(NULL));
     93   window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     94   window->Init(ui::LAYER_TEXTURED);
     95   SetDefaultParentByPrimaryRootWindow(window.get());
     96   window->Show();
     97 
     98   const gfx::Rect kOldBounds(20, 30, 400, 300);
     99   window->SetBounds(kOldBounds);
    100 
    101   // When the shadow is first created, it should use the window's size (but
    102   // remain at the origin, since it's a child of the window's layer).
    103   SetShadowType(window.get(), SHADOW_TYPE_RECTANGULAR);
    104   ShadowController::TestApi api(shadow_controller());
    105   const Shadow* shadow = api.GetShadowForWindow(window.get());
    106   ASSERT_TRUE(shadow != NULL);
    107   EXPECT_EQ(gfx::Rect(kOldBounds.size()).ToString(),
    108             shadow->content_bounds().ToString());
    109 
    110   // When we change the window's bounds, the shadow's should be updated too.
    111   gfx::Rect kNewBounds(50, 60, 500, 400);
    112   window->SetBounds(kNewBounds);
    113   EXPECT_EQ(gfx::Rect(kNewBounds.size()).ToString(),
    114             shadow->content_bounds().ToString());
    115 }
    116 
    117 // Tests that activating a window changes the shadow style.
    118 TEST_F(ShadowControllerTest, ShadowStyle) {
    119   ShadowController::TestApi api(shadow_controller());
    120 
    121   scoped_ptr<aura::Window> window1(new aura::Window(NULL));
    122   window1->SetType(aura::client::WINDOW_TYPE_NORMAL);
    123   window1->Init(ui::LAYER_TEXTURED);
    124   SetDefaultParentByPrimaryRootWindow(window1.get());
    125   window1->SetBounds(gfx::Rect(10, 20, 300, 400));
    126   window1->Show();
    127   ActivateWindow(window1.get());
    128 
    129   // window1 is active, so style should have active appearance.
    130   Shadow* shadow1 = api.GetShadowForWindow(window1.get());
    131   ASSERT_TRUE(shadow1 != NULL);
    132   EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style());
    133 
    134   // Create another window and activate it.
    135   scoped_ptr<aura::Window> window2(new aura::Window(NULL));
    136   window2->SetType(aura::client::WINDOW_TYPE_NORMAL);
    137   window2->Init(ui::LAYER_TEXTURED);
    138   SetDefaultParentByPrimaryRootWindow(window2.get());
    139   window2->SetBounds(gfx::Rect(11, 21, 301, 401));
    140   window2->Show();
    141   ActivateWindow(window2.get());
    142 
    143   // window1 is now inactive, so shadow should go inactive.
    144   Shadow* shadow2 = api.GetShadowForWindow(window2.get());
    145   ASSERT_TRUE(shadow2 != NULL);
    146   EXPECT_EQ(Shadow::STYLE_INACTIVE, shadow1->style());
    147   EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow2->style());
    148 }
    149 
    150 // Tests that we use smaller shadows for tooltips and menus.
    151 TEST_F(ShadowControllerTest, SmallShadowsForTooltipsAndMenus) {
    152   ShadowController::TestApi api(shadow_controller());
    153 
    154   scoped_ptr<aura::Window> tooltip_window(new aura::Window(NULL));
    155   tooltip_window->SetType(aura::client::WINDOW_TYPE_TOOLTIP);
    156   tooltip_window->Init(ui::LAYER_TEXTURED);
    157   SetDefaultParentByPrimaryRootWindow(tooltip_window.get());
    158   tooltip_window->SetBounds(gfx::Rect(10, 20, 300, 400));
    159   tooltip_window->Show();
    160 
    161   Shadow* tooltip_shadow = api.GetShadowForWindow(tooltip_window.get());
    162   ASSERT_TRUE(tooltip_shadow != NULL);
    163   EXPECT_EQ(Shadow::STYLE_SMALL, tooltip_shadow->style());
    164 
    165   scoped_ptr<aura::Window> menu_window(new aura::Window(NULL));
    166   menu_window->SetType(aura::client::WINDOW_TYPE_MENU);
    167   menu_window->Init(ui::LAYER_TEXTURED);
    168   SetDefaultParentByPrimaryRootWindow(menu_window.get());
    169   menu_window->SetBounds(gfx::Rect(10, 20, 300, 400));
    170   menu_window->Show();
    171 
    172   Shadow* menu_shadow = api.GetShadowForWindow(tooltip_window.get());
    173   ASSERT_TRUE(menu_shadow != NULL);
    174   EXPECT_EQ(Shadow::STYLE_SMALL, menu_shadow->style());
    175 }
    176 
    177 // http://crbug.com/120210 - transient parents of certain types of transients
    178 // should not lose their shadow when they lose activation to the transient.
    179 TEST_F(ShadowControllerTest, TransientParentKeepsActiveShadow) {
    180   ShadowController::TestApi api(shadow_controller());
    181 
    182   scoped_ptr<aura::Window> window1(new aura::Window(NULL));
    183   window1->SetType(aura::client::WINDOW_TYPE_NORMAL);
    184   window1->Init(ui::LAYER_TEXTURED);
    185   SetDefaultParentByPrimaryRootWindow(window1.get());
    186   window1->SetBounds(gfx::Rect(10, 20, 300, 400));
    187   window1->Show();
    188   ActivateWindow(window1.get());
    189 
    190   // window1 is active, so style should have active appearance.
    191   Shadow* shadow1 = api.GetShadowForWindow(window1.get());
    192   ASSERT_TRUE(shadow1 != NULL);
    193   EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style());
    194 
    195   // Create a window that is transient to window1, and that has the 'hide on
    196   // deactivate' property set. Upon activation, window1 should still have an
    197   // active shadow.
    198   scoped_ptr<aura::Window> window2(new aura::Window(NULL));
    199   window2->SetType(aura::client::WINDOW_TYPE_NORMAL);
    200   window2->Init(ui::LAYER_TEXTURED);
    201   SetDefaultParentByPrimaryRootWindow(window2.get());
    202   window2->SetBounds(gfx::Rect(11, 21, 301, 401));
    203   window1->AddTransientChild(window2.get());
    204   aura::client::SetHideOnDeactivate(window2.get(), true);
    205   window2->Show();
    206   ActivateWindow(window2.get());
    207 
    208   // window1 is now inactive, but its shadow should still appear active.
    209   EXPECT_EQ(Shadow::STYLE_ACTIVE, shadow1->style());
    210 }
    211 
    212 }  // namespace corewm
    213 }  // namespace views
    214