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