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 "grit/ui_resources.h"
8 #include "ui/base/resource/resource_bundle.h"
9 #include "ui/compositor/scoped_layer_animation_settings.h"
10 #include "ui/wm/core/image_grid.h"
11
12 namespace {
13
14 // Shadow opacity for different styles.
15 const float kActiveShadowOpacity = 1.0f;
16 const float kInactiveShadowOpacity = 0.2f;
17 const float kSmallShadowOpacity = 1.0f;
18
19 // Interior inset for different styles.
20 const int kActiveInteriorInset = 0;
21 const int kInactiveInteriorInset = 0;
22 const int kSmallInteriorInset = 5;
23
24 // Duration for opacity animation in milliseconds.
25 const int kShadowAnimationDurationMs = 100;
26
GetOpacityForStyle(wm::Shadow::Style style)27 float GetOpacityForStyle(wm::Shadow::Style style) {
28 switch (style) {
29 case wm::Shadow::STYLE_ACTIVE:
30 return kActiveShadowOpacity;
31 case wm::Shadow::STYLE_INACTIVE:
32 return kInactiveShadowOpacity;
33 case wm::Shadow::STYLE_SMALL:
34 return kSmallShadowOpacity;
35 }
36 return 1.0f;
37 }
38
GetInteriorInsetForStyle(wm::Shadow::Style style)39 int GetInteriorInsetForStyle(wm::Shadow::Style style) {
40 switch (style) {
41 case wm::Shadow::STYLE_ACTIVE:
42 return kActiveInteriorInset;
43 case wm::Shadow::STYLE_INACTIVE:
44 return kInactiveInteriorInset;
45 case wm::Shadow::STYLE_SMALL:
46 return kSmallInteriorInset;
47 }
48 return 0;
49 }
50
51 } // namespace
52
53 namespace wm {
54
Shadow()55 Shadow::Shadow() : style_(STYLE_ACTIVE), interior_inset_(0) {
56 }
57
~Shadow()58 Shadow::~Shadow() {
59 }
60
Init(Style style)61 void Shadow::Init(Style style) {
62 style_ = style;
63 image_grid_.reset(new ImageGrid);
64 UpdateImagesForStyle();
65 image_grid_->layer()->set_name("Shadow");
66 image_grid_->layer()->SetOpacity(GetOpacityForStyle(style_));
67 }
68
SetContentBounds(const gfx::Rect & content_bounds)69 void Shadow::SetContentBounds(const gfx::Rect& content_bounds) {
70 content_bounds_ = content_bounds;
71 UpdateImageGridBounds();
72 }
73
layer() const74 ui::Layer* Shadow::layer() const {
75 return image_grid_->layer();
76 }
77
SetStyle(Style style)78 void Shadow::SetStyle(Style style) {
79 if (style_ == style)
80 return;
81
82 Style old_style = style_;
83 style_ = style;
84
85 // Stop waiting for any as yet unfinished implicit animations.
86 StopObservingImplicitAnimations();
87
88 // If we're switching to or from the small style, don't bother with
89 // animations.
90 if (style == STYLE_SMALL || old_style == STYLE_SMALL) {
91 UpdateImagesForStyle();
92 image_grid_->layer()->SetOpacity(GetOpacityForStyle(style));
93 return;
94 }
95
96 // If we're becoming active, switch images now. Because the inactive image
97 // has a very low opacity the switch isn't noticeable and this approach
98 // allows us to use only a single set of shadow images at a time.
99 if (style == STYLE_ACTIVE) {
100 UpdateImagesForStyle();
101 // Opacity was baked into inactive image, start opacity low to match.
102 image_grid_->layer()->SetOpacity(kInactiveShadowOpacity);
103 }
104
105 {
106 // Property sets within this scope will be implicitly animated.
107 ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
108 settings.AddObserver(this);
109 settings.SetTransitionDuration(
110 base::TimeDelta::FromMilliseconds(kShadowAnimationDurationMs));
111 switch (style_) {
112 case STYLE_ACTIVE:
113 image_grid_->layer()->SetOpacity(kActiveShadowOpacity);
114 break;
115 case STYLE_INACTIVE:
116 image_grid_->layer()->SetOpacity(kInactiveShadowOpacity);
117 break;
118 default:
119 NOTREACHED() << "Unhandled style " << style_;
120 break;
121 }
122 }
123 }
124
OnImplicitAnimationsCompleted()125 void Shadow::OnImplicitAnimationsCompleted() {
126 // If we just finished going inactive, switch images. This doesn't cause
127 // a visual pop because the inactive image opacity is so low.
128 if (style_ == STYLE_INACTIVE) {
129 UpdateImagesForStyle();
130 // Opacity is baked into inactive image, so set fully opaque.
131 image_grid_->layer()->SetOpacity(1.0f);
132 }
133 }
134
UpdateImagesForStyle()135 void Shadow::UpdateImagesForStyle() {
136 ResourceBundle& res = ResourceBundle::GetSharedInstance();
137 switch (style_) {
138 case STYLE_ACTIVE:
139 image_grid_->SetImages(
140 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP_LEFT),
141 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP),
142 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_TOP_RIGHT),
143 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_LEFT),
144 NULL,
145 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_RIGHT),
146 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM_LEFT),
147 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM),
148 &res.GetImageNamed(IDR_AURA_SHADOW_ACTIVE_BOTTOM_RIGHT));
149 break;
150 case STYLE_INACTIVE:
151 image_grid_->SetImages(
152 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP_LEFT),
153 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP),
154 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_TOP_RIGHT),
155 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_LEFT),
156 NULL,
157 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_RIGHT),
158 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM_LEFT),
159 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM),
160 &res.GetImageNamed(IDR_AURA_SHADOW_INACTIVE_BOTTOM_RIGHT));
161 break;
162 case STYLE_SMALL:
163 image_grid_->SetImages(
164 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_LEFT),
165 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP),
166 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_RIGHT),
167 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_LEFT),
168 NULL,
169 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_RIGHT),
170 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_LEFT),
171 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM),
172 &res.GetImageNamed(IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_RIGHT));
173 break;
174 default:
175 NOTREACHED() << "Unhandled style " << style_;
176 break;
177 }
178
179 // Update interior inset for style.
180 interior_inset_ = GetInteriorInsetForStyle(style_);
181
182 // Image sizes may have changed.
183 UpdateImageGridBounds();
184 }
185
UpdateImageGridBounds()186 void Shadow::UpdateImageGridBounds() {
187 // Update bounds based on content bounds and image sizes.
188 gfx::Rect image_grid_bounds = content_bounds_;
189 image_grid_bounds.Inset(interior_inset_, interior_inset_);
190 image_grid_->SetContentBounds(image_grid_bounds);
191 }
192
193 } // namespace wm
194