Home | History | Annotate | Download | only in core
      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.h"
      6 
      7 #include "third_party/skia/include/core/SkBitmap.h"
      8 #include "ui/base/resource/resource_bundle.h"
      9 #include "ui/compositor/layer.h"
     10 #include "ui/compositor/scoped_layer_animation_settings.h"
     11 #include "ui/resources/grit/ui_resources.h"
     12 
     13 namespace {
     14 
     15 // Shadow opacity for different styles.
     16 const float kActiveShadowOpacity = 1.0f;
     17 const float kInactiveShadowOpacity = 0.2f;
     18 const float kSmallShadowOpacity = 1.0f;
     19 
     20 // Shadow aperture for different styles.
     21 // Note that this may be greater than interior inset to allow shadows with
     22 // curved corners that extend inwards beyond a window's borders.
     23 const int kActiveInteriorAperture = 134;
     24 const int kInactiveInteriorAperture = 134;
     25 const int kSmallInteriorAperture = 9;
     26 
     27 // Interior inset for different styles.
     28 const int kActiveInteriorInset = 64;
     29 const int kInactiveInteriorInset = 64;
     30 const int kSmallInteriorInset = 4;
     31 
     32 // Duration for opacity animation in milliseconds.
     33 const int kShadowAnimationDurationMs = 100;
     34 
     35 float GetOpacityForStyle(wm::Shadow::Style style) {
     36   switch (style) {
     37     case wm::Shadow::STYLE_ACTIVE:
     38       return kActiveShadowOpacity;
     39     case wm::Shadow::STYLE_INACTIVE:
     40       return kInactiveShadowOpacity;
     41     case wm::Shadow::STYLE_SMALL:
     42       return kSmallShadowOpacity;
     43   }
     44   return 1.0f;
     45 }
     46 
     47 int GetShadowApertureForStyle(wm::Shadow::Style style) {
     48   switch (style) {
     49     case wm::Shadow::STYLE_ACTIVE:
     50       return kActiveInteriorAperture;
     51     case wm::Shadow::STYLE_INACTIVE:
     52       return kInactiveInteriorAperture;
     53     case wm::Shadow::STYLE_SMALL:
     54       return kSmallInteriorAperture;
     55   }
     56   return 0;
     57 }
     58 
     59 int GetInteriorInsetForStyle(wm::Shadow::Style style) {
     60   switch (style) {
     61     case wm::Shadow::STYLE_ACTIVE:
     62       return kActiveInteriorInset;
     63     case wm::Shadow::STYLE_INACTIVE:
     64       return kInactiveInteriorInset;
     65     case wm::Shadow::STYLE_SMALL:
     66       return kSmallInteriorInset;
     67   }
     68   return 0;
     69 }
     70 
     71 }  // namespace
     72 
     73 namespace wm {
     74 
     75 Shadow::Shadow() : style_(STYLE_ACTIVE), interior_inset_(0) {
     76 }
     77 
     78 Shadow::~Shadow() {
     79 }
     80 
     81 void Shadow::Init(Style style) {
     82   style_ = style;
     83 
     84   layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN));
     85   shadow_layer_.reset(new ui::Layer(ui::LAYER_NINE_PATCH));
     86   layer()->Add(shadow_layer_.get());
     87 
     88   UpdateImagesForStyle();
     89   shadow_layer_->set_name("Shadow");
     90   shadow_layer_->SetVisible(true);
     91   shadow_layer_->SetFillsBoundsOpaquely(false);
     92   shadow_layer_->SetOpacity(GetOpacityForStyle(style_));
     93 }
     94 
     95 void Shadow::SetContentBounds(const gfx::Rect& content_bounds) {
     96   content_bounds_ = content_bounds;
     97   UpdateLayerBounds();
     98 }
     99 
    100 void Shadow::SetStyle(Style style) {
    101   if (style_ == style)
    102     return;
    103 
    104   Style old_style = style_;
    105   style_ = style;
    106 
    107   // Stop waiting for any as yet unfinished implicit animations.
    108   StopObservingImplicitAnimations();
    109 
    110   // If we're switching to or from the small style, don't bother with
    111   // animations.
    112   if (style == STYLE_SMALL || old_style == STYLE_SMALL) {
    113     UpdateImagesForStyle();
    114     shadow_layer_->SetOpacity(GetOpacityForStyle(style));
    115     return;
    116   }
    117 
    118   // If we're becoming active, switch images now.  Because the inactive image
    119   // has a very low opacity the switch isn't noticeable and this approach
    120   // allows us to use only a single set of shadow images at a time.
    121   if (style == STYLE_ACTIVE) {
    122     UpdateImagesForStyle();
    123     // Opacity was baked into inactive image, start opacity low to match.
    124     shadow_layer_->SetOpacity(kInactiveShadowOpacity);
    125   }
    126 
    127   {
    128     // Property sets within this scope will be implicitly animated.
    129     ui::ScopedLayerAnimationSettings settings(shadow_layer_->GetAnimator());
    130     settings.AddObserver(this);
    131     settings.SetTransitionDuration(
    132         base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs));
    133     switch (style_) {
    134       case STYLE_ACTIVE:
    135         shadow_layer_->SetOpacity(kActiveShadowOpacity);
    136         break;
    137       case STYLE_INACTIVE:
    138         shadow_layer_->SetOpacity(kInactiveShadowOpacity);
    139         break;
    140       default:
    141         NOTREACHED() << "Unhandled style " << style_;
    142         break;
    143     }
    144   }
    145 }
    146 
    147 void Shadow::OnImplicitAnimationsCompleted() {
    148   // If we just finished going inactive, switch images.  This doesn't cause
    149   // a visual pop because the inactive image opacity is so low.
    150   if (style_ == STYLE_INACTIVE) {
    151     UpdateImagesForStyle();
    152     // Opacity is baked into inactive image, so set fully opaque.
    153     shadow_layer_->SetOpacity(1.0f);
    154   }
    155 }
    156 
    157 void Shadow::UpdateImagesForStyle() {
    158   ResourceBundle& res = ResourceBundle::GetSharedInstance();
    159   gfx::Image image;
    160   switch (style_) {
    161     case STYLE_ACTIVE:
    162       image = res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE);
    163       break;
    164     case STYLE_INACTIVE:
    165       image = res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE);
    166       break;
    167     case STYLE_SMALL:
    168       image = res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL);
    169       break;
    170     default:
    171       NOTREACHED() << "Unhandled style " << style_;
    172       break;
    173   }
    174 
    175   shadow_layer_->UpdateNinePatchLayerBitmap(image.AsBitmap());
    176   image_size_ = image.Size();
    177   interior_inset_ = GetInteriorInsetForStyle(style_);
    178 
    179   // Image sizes may have changed.
    180   UpdateLayerBounds();
    181 }
    182 
    183 void Shadow::UpdateLayerBounds() {
    184   // Update bounds based on content bounds and interior inset.
    185   gfx::Rect layer_bounds = content_bounds_;
    186   layer_bounds.Inset(-interior_inset_, -interior_inset_);
    187   layer()->SetBounds(layer_bounds);
    188   shadow_layer_->SetBounds(gfx::Rect(layer_bounds.size()));
    189 
    190   // Update the shadow aperture and border for style. Note that border is in
    191   // layer space and it cannot exceed the bounds of the layer.
    192   int aperture = GetShadowApertureForStyle(style_);
    193   int aperture_x = std::min(aperture, layer_bounds.width() / 2);
    194   int aperture_y = std::min(aperture, layer_bounds.height() / 2);
    195   shadow_layer_->UpdateNinePatchLayerAperture(
    196       gfx::Rect(aperture_x, aperture_y,
    197                 image_size_.width() - aperture_x * 2,
    198                 image_size_.height() - aperture_y * 2));
    199   shadow_layer_->UpdateNinePatchLayerBorder(
    200       gfx::Rect(aperture_x, aperture_y, aperture_x * 2, aperture_y * 2));
    201 }
    202 
    203 }  // namespace wm
    204