1 // Copyright 2014 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.
5 #include "chrome/browser/android/compositor/layer/tab_layer.h"
7 #include "base/i18n/rtl.h"
8 #include "cc/layers/image_layer.h"
9 #include "cc/layers/layer.h"
10 #include "cc/layers/layer_lists.h"
11 #include "cc/layers/nine_patch_layer.h"
12 #include "cc/layers/solid_color_layer.h"
13 #include "cc/layers/ui_resource_layer.h"
14 #include "chrome/browser/android/compositor/decoration_title.h"
15 #include "chrome/browser/android/compositor/layer/content_layer.h"
16 #include "chrome/browser/android/compositor/layer/toolbar_layer.h"
17 #include "chrome/browser/android/compositor/layer_title_cache.h"
18 #include "chrome/browser/android/compositor/tab_content_manager.h"
19 #include "ui/android/resources/resource_manager.h"
20 #include "ui/android/resources/ui_resource_android.h"
21 #include "ui/base/l10n/l10n_util_android.h"
22 #include "ui/gfx/geometry/insets_f.h"
23 #include "ui/gfx/geometry/point_f.h"
24 #include "ui/gfx/geometry/rect_f.h"
25 #include "ui/gfx/geometry/safe_integer_conversions.h"
26 #include "ui/gfx/geometry/size.h"
27 #include "ui/gfx/transform.h"
33 scoped_refptr
<TabLayer
> TabLayer::Create(
35 ui::ResourceManager
* resource_manager
,
36 LayerTitleCache
* layer_title_cache
,
37 TabContentManager
* tab_content_manager
) {
38 return make_scoped_refptr(new TabLayer(
39 incognito
, resource_manager
, layer_title_cache
, tab_content_manager
));
42 static gfx::Rect
ComputePaddingPosition(const gfx::Size
& bounds
,
43 const gfx::Size
& desired_bounds
) {
44 gfx::Rect padding_rect
;
45 if (bounds
.width() == 0 || bounds
.height() == 0) {
46 padding_rect
.set_size(desired_bounds
);
47 } else if (bounds
.width() < desired_bounds
.width()) {
48 padding_rect
.set_x(bounds
.width());
49 padding_rect
.set_width(desired_bounds
.width() - bounds
.width());
50 padding_rect
.set_height(std::min(bounds
.height(), desired_bounds
.height()));
51 } else if (bounds
.height() < desired_bounds
.height()) {
52 padding_rect
.set_y(bounds
.height());
53 padding_rect
.set_width(std::min(bounds
.width(), desired_bounds
.width()));
54 padding_rect
.set_height(desired_bounds
.height() - bounds
.height());
59 void TabLayer::SetProperties(int id
,
60 bool can_use_live_layer
,
61 bool can_use_ntp_fallback
,
62 int toolbar_resource_id
,
63 int close_button_resource_id
,
64 int shadow_resource_id
,
65 int contour_resource_id
,
66 int back_logo_resource_id
,
67 int border_resource_id
,
68 int default_background_color
,
91 float close_btn_width
,
92 float static_to_view_blend
,
98 bool anonymize_toolbar
,
100 float toolbar_y_offset
,
101 float side_border_scale
,
105 layer_
->SetHideLayerAndSubtree(true);
109 layer_
->SetHideLayerAndSubtree(false);
111 // Grab required resources
112 ui::ResourceManager::Resource
* border_resource
=
113 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
115 ui::ResourceManager::Resource
* shadow_resource
=
116 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
118 ui::ResourceManager::Resource
* contour_resource
=
119 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
120 contour_resource_id
);
121 ui::ResourceManager::Resource
* toolbar_resource
=
122 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_DYNAMIC
,
123 toolbar_resource_id
);
124 ui::ResourceManager::Resource
* close_btn_resource
=
125 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
126 close_button_resource_id
);
127 ui::ResourceManager::Resource
* back_logo_resource
=
128 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
129 back_logo_resource_id
);
131 DecorationTitle
* title_layer
= nullptr;
133 //----------------------------------------------------------------------------
134 // Handle Border Scaling (Upscale/Downscale everything until final scaling)
135 //----------------------------------------------------------------------------
136 width
/= border_scale
;
137 height
/= border_scale
;
138 shadow_x
/= border_scale
;
139 shadow_y
/= border_scale
;
140 shadow_width
/= border_scale
;
141 shadow_height
/= border_scale
;
143 //----------------------------------------------------------------------------
144 // Precalculate Helper Values
145 //----------------------------------------------------------------------------
146 const gfx::RectF
border_padding(border_resource
->padding
);
147 const gfx::RectF
shadow_padding(shadow_resource
->padding
);
148 const gfx::RectF
contour_padding(contour_resource
->padding
);
149 const gfx::RectF
toolbar_padding(toolbar_resource
->padding
);
151 // If we're in portrait and we're RTL, the close button is on the left.
152 // Similarly if we're in landscape and we're in LTR, the close button is on
154 const bool close_button_on_left
= is_portrait
== l10n_util::IsLayoutRtl();
155 const bool back_visible
= cos(rotation_x
* SK_MScalarPI
/ 180.0f
) < 0 ||
156 cos(rotation_y
* SK_MScalarPI
/ 180.0f
) < 0;
158 const float content_scale
= width
/ content_width
;
159 gfx::RectF
content_area(0.f
, 0.f
, content_width
, content_height
);
160 gfx::RectF
scaled_local_content_area(shadow_x
, shadow_y
, shadow_width
,
162 gfx::RectF
descaled_local_content_area(
163 scaled_local_content_area
.x() / content_scale
,
164 scaled_local_content_area
.y() / content_scale
,
165 scaled_local_content_area
.width() / content_scale
,
166 scaled_local_content_area
.height() / content_scale
);
168 const gfx::Size
shadow_padding_size(
169 shadow_resource
->size
.width() - shadow_padding
.width(),
170 shadow_resource
->size
.height() - shadow_padding
.height());
171 const gfx::Size
border_padding_size(
172 border_resource
->size
.width() - border_padding
.width(),
173 border_resource
->size
.height() - border_padding
.height());
174 const gfx::Size
contour_padding_size(
175 contour_resource
->size
.width() - contour_padding
.width(),
176 contour_resource
->size
.height() - contour_padding
.height());
178 gfx::Size
toolbar_impact_size(toolbar_padding
.size().width(),
179 toolbar_padding
.size().height());
180 if (!show_toolbar
|| back_visible
)
181 toolbar_impact_size
.SetSize(0, 0);
182 const float close_btn_effective_width
= close_btn_width
* close_alpha
;
184 //----------------------------------------------------------------------------
185 // Update Resource Ids For Layers That Impact Layout
186 //----------------------------------------------------------------------------
187 toolbar_layer_
->PushResource(toolbar_resource
, anonymize_toolbar
, incognito_
,
190 //----------------------------------------------------------------------------
191 // Compute Alpha and Visibility
192 //----------------------------------------------------------------------------
193 border_alpha
*= alpha
;
194 contour_alpha
*= alpha
;
195 shadow_alpha
*= alpha
;
196 close_alpha
*= alpha
;
197 toolbar_alpha
*= alpha
;
202 bool border_visible
= border_alpha
> 0.f
;
203 bool contour_visible
= border_alpha
< contour_alpha
&& contour_alpha
> 0.f
;
204 bool shadow_visible
= shadow_alpha
> 0.f
&& border_alpha
> 0.f
;
206 //----------------------------------------------------------------------------
207 // Compute Layer Sizes
208 //----------------------------------------------------------------------------
209 gfx::Size
shadow_size(width
+ shadow_padding_size
.width() * side_border_scale
,
210 height
+ shadow_padding_size
.height());
211 gfx::Size
border_size(width
+ border_padding_size
.width() * side_border_scale
,
212 height
+ border_padding_size
.height());
213 gfx::Size
contour_size(
214 width
+ contour_padding_size
.width() * side_border_scale
,
215 height
+ contour_padding_size
.height());
216 gfx::Size
close_button_size(close_btn_width
, border_padding
.y());
217 gfx::Size
title_size(width
- close_btn_effective_width
, border_padding
.y());
218 gfx::Size back_logo_size
;
219 // TODO(clholgat): Figure out why the back logo is null sometimes.
220 if (back_logo_resource
)
221 back_logo_size
= back_logo_resource
->size
;
223 // Store this size at a point as it might go negative during the inset
225 gfx::Point
desired_content_size_pt(
226 descaled_local_content_area
.width(),
227 descaled_local_content_area
.height() - toolbar_impact_size
.height());
229 // Shrink the toolbar layer so we properly clip if it's offset.
230 gfx::Size
toolbar_size(
231 toolbar_layer_
->layer()->bounds().width(),
232 toolbar_layer_
->layer()->bounds().height() - toolbar_y_offset
);
234 //----------------------------------------------------------------------------
235 // Compute Layer Positions
236 //----------------------------------------------------------------------------
237 gfx::PointF
shadow_position(-shadow_padding
.x() * side_border_scale
,
238 -shadow_padding
.y());
239 gfx::PointF
border_position(-border_padding
.x() * side_border_scale
,
240 -border_padding
.y());
241 gfx::PointF
contour_position(-contour_padding
.x() * side_border_scale
,
242 -contour_padding
.y());
243 gfx::PointF
toolbar_position(
244 0.f
, toolbar_layer_
->layer()->bounds().height() - toolbar_size
.height());
245 gfx::PointF
content_position(0.f
, toolbar_impact_size
.height());
246 gfx::PointF
back_logo_position(
247 ((descaled_local_content_area
.width() - back_logo_
->bounds().width()) *
250 ((descaled_local_content_area
.height() - back_logo_
->bounds().height()) *
253 gfx::PointF close_button_position
;
254 gfx::PointF title_position
;
256 close_button_position
.set_y(-border_padding
.y());
257 title_position
.set_y(-border_padding
.y());
258 if (!close_button_on_left
)
259 close_button_position
.set_x(width
- close_button_size
.width());
261 title_position
.set_x(close_btn_effective_width
);
263 //----------------------------------------------------------------------------
264 // Center Specific Assets in the Rects
265 //----------------------------------------------------------------------------
266 close_button_position
.Offset(
267 (close_button_size
.width() - close_btn_resource
->size
.width()) / 2.f
,
268 (close_button_size
.height() - close_btn_resource
->size
.height()) / 2.f
);
269 close_button_size
.SetSize(close_btn_resource
->size
.width(),
270 close_btn_resource
->size
.height());
272 //----------------------------------------------------------------------------
273 // Handle Insetting the Top Border Component
274 //----------------------------------------------------------------------------
276 float inset_diff
= inset_border
? border_padding
.y() : 0.f
;
277 descaled_local_content_area
.set_height(
278 descaled_local_content_area
.height() - inset_diff
);
279 scaled_local_content_area
.set_height(scaled_local_content_area
.height() -
280 inset_diff
* content_scale
);
281 shadow_size
.set_height(shadow_size
.height() - inset_diff
);
282 border_size
.set_height(border_size
.height() - inset_diff
);
283 contour_size
.set_height(contour_size
.height() - inset_diff
);
284 shadow_position
.set_y(shadow_position
.y() + inset_diff
);
285 border_position
.set_y(border_position
.y() + inset_diff
);
286 contour_position
.set_y(contour_position
.y() + inset_diff
);
287 close_button_position
.set_y(close_button_position
.y() + inset_diff
);
288 title_position
.set_y(title_position
.y() + inset_diff
);
290 // Scaled eventually, so have to descale the size difference first.
291 toolbar_position
.set_y(toolbar_position
.y() + inset_diff
/ content_scale
);
292 content_position
.set_y(content_position
.y() + inset_diff
/ content_scale
);
293 desired_content_size_pt
.set_y(desired_content_size_pt
.y() -
294 inset_diff
/ content_scale
);
297 const bool inset_toolbar
= !inset_border
;
298 if (!inset_toolbar
) {
299 float inset_diff
= toolbar_impact_size
.height();
300 toolbar_position
.set_y(toolbar_position
.y() - inset_diff
);
301 content_position
.set_y(content_position
.y() - inset_diff
);
302 desired_content_size_pt
.set_y(desired_content_size_pt
.y() + inset_diff
);
305 // Finally build the sizes that might have calculations that go negative.
306 gfx::Size
desired_content_size(desired_content_size_pt
.x(),
307 desired_content_size_pt
.y());
309 //----------------------------------------------------------------------------
310 // Calculate Content Visibility
311 //----------------------------------------------------------------------------
312 // Check if the rect we are drawing is larger than the content rect.
313 bool content_visible
= desired_content_size
.GetArea() > 0.f
;
315 // TODO(dtrainor): Improve these calculations to prune these layers out.
316 bool title_visible
= border_alpha
> 0.f
&& !back_visible
;
317 bool close_btn_visible
= title_visible
;
318 bool toolbar_visible
= show_toolbar
&& toolbar_alpha
> 0.f
&& !back_visible
;
320 //----------------------------------------------------------------------------
322 //----------------------------------------------------------------------------
323 border_position
.Offset(0.5f
, 0.5f
);
324 shadow_position
.Offset(0.5f
, 0.5f
);
325 contour_position
.Offset(0.5f
, 0.5f
);
326 title_position
.Offset(0.5f
, 0.5f
);
327 close_button_position
.Offset(0.5f
, 0.5f
);
329 border_size
.Enlarge(-1.f
, -1.f
);
330 shadow_size
.Enlarge(-1.f
, -1.f
);
332 //----------------------------------------------------------------------------
333 // Update Resource Ids
334 //----------------------------------------------------------------------------
335 shadow_
->SetUIResourceId(shadow_resource
->ui_resource
->id());
336 shadow_
->SetBorder(shadow_resource
->Border(shadow_size
));
337 shadow_
->SetAperture(shadow_resource
->aperture
);
339 contour_shadow_
->SetUIResourceId(contour_resource
->ui_resource
->id());
340 contour_shadow_
->SetBorder(contour_resource
->Border(contour_size
));
341 contour_shadow_
->SetAperture(contour_resource
->aperture
);
343 front_border_
->SetUIResourceId(border_resource
->ui_resource
->id());
344 front_border_
->SetAperture(border_resource
->aperture
);
345 front_border_
->SetBorder(border_resource
->Border(
347 gfx::InsetsF(1.f
, side_border_scale
, 1.f
, side_border_scale
)));
349 padding_
->SetBackgroundColor(back_visible
? back_logo_color
350 : default_background_color
);
352 if (title_visible
&& layer_title_cache_
)
353 title_layer
= layer_title_cache_
->GetTitleLayer(id
);
354 SetTitle(title_layer
);
356 close_button_
->SetUIResourceId(close_btn_resource
->ui_resource
->id());
359 gfx::Rect
rounded_descaled_content_area(
360 round(descaled_local_content_area
.x()),
361 round(descaled_local_content_area
.y()),
362 round(desired_content_size
.width()),
363 round(desired_content_size
.height()));
365 content_
->SetProperties(id
, can_use_live_layer
, can_use_ntp_fallback
,
366 static_to_view_blend
, true, alpha
, saturation
,
367 rounded_descaled_content_area
,
368 gfx::Size(content_width
, content_height
));
369 } else if (back_logo_resource
) {
370 back_logo_
->SetUIResourceId(back_logo_resource
->ui_resource
->id());
373 //----------------------------------------------------------------------------
374 // Push Size, Position, Alpha and Transformations to Layers
375 //----------------------------------------------------------------------------
376 shadow_
->SetHideLayerAndSubtree(!shadow_visible
);
377 if (shadow_visible
) {
378 shadow_
->SetPosition(shadow_position
);
379 shadow_
->SetBounds(shadow_size
);
380 shadow_
->SetOpacity(shadow_alpha
);
383 contour_shadow_
->SetHideLayerAndSubtree(!contour_visible
);
384 if (contour_visible
) {
385 contour_shadow_
->SetPosition(contour_position
);
386 contour_shadow_
->SetBounds(contour_size
);
387 contour_shadow_
->SetOpacity(contour_alpha
);
390 front_border_
->SetHideLayerAndSubtree(!border_visible
);
391 if (border_visible
) {
392 front_border_
->SetPosition(border_position
);
393 front_border_
->SetBounds(border_size
);
394 front_border_
->SetOpacity(border_alpha
);
397 toolbar_layer_
->layer()->SetHideLayerAndSubtree(!toolbar_visible
);
398 if (toolbar_visible
) {
399 // toolbar_ Transform
400 gfx::Transform transform
;
401 transform
.Scale(content_scale
, content_scale
);
402 transform
.Translate(toolbar_position
.x(), toolbar_position
.y());
403 toolbar_layer_
->layer()->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
404 toolbar_layer_
->layer()->SetTransform(transform
);
405 toolbar_layer_
->layer()->SetOpacity(toolbar_alpha
);
407 toolbar_layer_
->layer()->SetMasksToBounds(
408 toolbar_layer_
->layer()->bounds() != toolbar_size
);
409 toolbar_layer_
->layer()->SetBounds(toolbar_size
);
413 gfx::PointF
vertically_centered_position(
416 (title_size
.height() - title_layer
->size().height()) / 2.f
);
418 title_
->SetPosition(vertically_centered_position
);
419 title_layer
->setBounds(title_size
);
420 title_layer
->setOpacity(border_alpha
);
423 close_button_
->SetHideLayerAndSubtree(!close_btn_visible
);
424 if (close_btn_visible
) {
425 close_button_
->SetPosition(close_button_position
);
426 close_button_
->SetBounds(close_button_size
);
427 // Non-linear alpha looks better.
428 close_button_
->SetOpacity(close_alpha
* close_alpha
* border_alpha
);
431 if (content_visible
&& attach_content
) {
433 // content_ and back_logo_ Transforms
434 gfx::Transform transform
;
435 transform
.Scale(content_scale
, content_scale
);
436 transform
.Translate(content_position
.x(), content_position
.y());
437 transform
.Translate(descaled_local_content_area
.x(),
438 descaled_local_content_area
.y());
440 content_
->layer()->SetHideLayerAndSubtree(back_visible
);
441 back_logo_
->SetHideLayerAndSubtree(!back_visible
);
444 content_
->layer()->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
445 content_
->layer()->SetTransform(transform
);
447 back_logo_
->SetPosition(back_logo_position
);
448 back_logo_
->SetBounds(back_logo_size
);
449 back_logo_
->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
450 back_logo_
->SetTransform(transform
);
451 // TODO: Set back logo alpha on leaf.
456 // padding_ Transform
457 gfx::Size content_bounds
;
459 content_bounds
= content_
->layer()->bounds();
461 gfx::Rect
padding_rect(
462 ComputePaddingPosition(content_bounds
, desired_content_size
));
463 padding_
->SetHideLayerAndSubtree(padding_rect
.IsEmpty());
464 padding_
->SetBounds(padding_rect
.size());
465 padding_
->SetOpacity(alpha
);
467 gfx::Transform transform
;
468 transform
.Scale(content_scale
, content_scale
);
469 transform
.Translate(padding_rect
.x() + content_position
.x(),
470 padding_rect
.y() + content_position
.y());
471 transform
.Translate(descaled_local_content_area
.x(),
472 descaled_local_content_area
.y());
473 padding_
->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
474 padding_
->SetTransform(transform
);
477 back_logo_
->SetHideLayerAndSubtree(true);
478 padding_
->SetHideLayerAndSubtree(true);
479 content_
->layer()->SetHideLayerAndSubtree(true);
484 gfx::PointF
pivot_origin(pivot_y
, pivot_x
);
486 gfx::Transform transform
;
488 if (rotation_x
!= 0 || rotation_y
!= 0) {
489 // Apply screen perspective if there are rotations.
490 transform
.Translate(content_width
/ 2, content_height
/ 2);
491 transform
.ApplyPerspectiveDepth(
492 content_width
> content_height
? content_width
: content_height
);
493 transform
.Translate(-content_width
/ 2, -content_height
/ 2);
495 // Translate to correct position on the screen
496 transform
.Translate(x
, y
);
498 // Apply pivot rotations
499 transform
.Translate(pivot_origin
.x(), pivot_origin
.y());
500 transform
.RotateAboutYAxis(rotation_y
);
501 transform
.RotateAboutXAxis(-rotation_x
);
502 transform
.Translate(-pivot_origin
.x(), -pivot_origin
.y());
504 // Translate to correct position on the screen
505 transform
.Translate(x
, y
);
507 transform
.Scale(border_scale
, border_scale
);
508 layer_
->SetTransform(transform
);
511 // Only applies the brightness filter if the value has changed and is less
513 if (brightness
!= brightness_
) {
514 brightness_
= brightness
;
515 cc::FilterOperations filters
;
516 if (brightness_
< 1.f
)
517 filters
.Append(cc::FilterOperation::CreateBrightnessFilter(brightness_
));
518 layer_
->SetFilters(filters
);
522 scoped_refptr
<cc::Layer
> TabLayer::layer() {
526 TabLayer::TabLayer(bool incognito
,
527 ui::ResourceManager
* resource_manager
,
528 LayerTitleCache
* layer_title_cache
,
529 TabContentManager
* tab_content_manager
)
530 : incognito_(incognito
),
531 resource_manager_(resource_manager
),
532 layer_title_cache_(layer_title_cache
),
533 layer_(cc::Layer::Create()),
534 toolbar_layer_(ToolbarLayer::Create()),
535 title_(cc::Layer::Create()),
536 content_(ContentLayer::Create(tab_content_manager
)),
537 padding_(cc::SolidColorLayer::Create()),
538 close_button_(cc::UIResourceLayer::Create()),
539 front_border_(cc::NinePatchLayer::Create()),
540 contour_shadow_(cc::NinePatchLayer::Create()),
541 shadow_(cc::NinePatchLayer::Create()),
542 back_logo_(cc::UIResourceLayer::Create()),
544 layer_
->AddChild(shadow_
);
545 layer_
->AddChild(contour_shadow_
);
546 layer_
->AddChild(padding_
);
547 layer_
->AddChild(content_
->layer());
548 layer_
->AddChild(back_logo_
);
549 layer_
->AddChild(front_border_
);
550 layer_
->AddChild(title_
.get());
551 layer_
->AddChild(close_button_
);
552 layer_
->AddChild(toolbar_layer_
->layer());
554 contour_shadow_
->SetIsDrawable(true);
555 padding_
->SetIsDrawable(true);
556 front_border_
->SetIsDrawable(true);
557 shadow_
->SetIsDrawable(true);
558 close_button_
->SetIsDrawable(true);
559 back_logo_
->SetIsDrawable(true);
562 TabLayer::~TabLayer() {
565 void TabLayer::SetTitle(DecorationTitle
* title
) {
566 scoped_refptr
<cc::Layer
> layer
= title
? title
->layer() : nullptr;
569 title_
->RemoveAllChildren();
571 const cc::LayerList
& children
= title_
->children();
572 if (children
.size() == 0 || children
[0]->id() != layer
->id()) {
573 title_
->RemoveAllChildren();
574 title_
->AddChild(layer
);
579 title
->SetUIResourceIds();
582 } // namespace android
583 } // namespace chrome