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/layer.h"
9 #include "cc/layers/layer_lists.h"
10 #include "cc/layers/nine_patch_layer.h"
11 #include "cc/layers/solid_color_layer.h"
12 #include "cc/layers/ui_resource_layer.h"
13 #include "chrome/browser/android/compositor/decoration_title.h"
14 #include "chrome/browser/android/compositor/layer/content_layer.h"
15 #include "chrome/browser/android/compositor/layer/toolbar_layer.h"
16 #include "chrome/browser/android/compositor/layer_title_cache.h"
17 #include "chrome/browser/android/compositor/tab_content_manager.h"
18 #include "content/public/browser/android/compositor.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
= nullptr;
129 DecorationTitle
* title_layer
= nullptr;
131 //----------------------------------------------------------------------------
132 // Handle Border Scaling (Upscale/Downscale everything until final scaling)
133 //----------------------------------------------------------------------------
134 width
/= border_scale
;
135 height
/= border_scale
;
136 shadow_x
/= border_scale
;
137 shadow_y
/= border_scale
;
138 shadow_width
/= border_scale
;
139 shadow_height
/= border_scale
;
141 //----------------------------------------------------------------------------
142 // Precalculate Helper Values
143 //----------------------------------------------------------------------------
144 const gfx::RectF
border_padding(border_resource
->padding
);
145 const gfx::RectF
shadow_padding(shadow_resource
->padding
);
146 const gfx::RectF
contour_padding(contour_resource
->padding
);
148 // If we're in portrait and we're RTL, the close button is on the left.
149 // Similarly if we're in landscape and we're in LTR, the close button is on
151 const bool close_button_on_left
= is_portrait
== l10n_util::IsLayoutRtl();
152 const bool back_visible
= cos(rotation_x
* SK_MScalarPI
/ 180.0f
) < 0 ||
153 cos(rotation_y
* SK_MScalarPI
/ 180.0f
) < 0;
155 const float content_scale
= width
/ content_width
;
156 gfx::RectF
content_area(0.f
, 0.f
, content_width
, content_height
);
157 gfx::RectF
scaled_local_content_area(shadow_x
, shadow_y
, shadow_width
,
159 gfx::RectF
descaled_local_content_area(
160 scaled_local_content_area
.x() / content_scale
,
161 scaled_local_content_area
.y() / content_scale
,
162 scaled_local_content_area
.width() / content_scale
,
163 scaled_local_content_area
.height() / content_scale
);
165 const gfx::Size
shadow_padding_size(
166 shadow_resource
->size
.width() - shadow_padding
.width(),
167 shadow_resource
->size
.height() - shadow_padding
.height());
168 const gfx::Size
border_padding_size(
169 border_resource
->size
.width() - border_padding
.width(),
170 border_resource
->size
.height() - border_padding
.height());
171 const gfx::Size
contour_padding_size(
172 contour_resource
->size
.width() - contour_padding
.width(),
173 contour_resource
->size
.height() - contour_padding
.height());
175 const float close_btn_effective_width
= close_btn_width
* close_alpha
;
177 float toolbar_impact_height
= 0;
178 if (toolbar_resource
) {
179 //--------------------------------------------------------------------------
180 // Update Resource Ids For Layers That Impact Layout
181 //--------------------------------------------------------------------------
182 toolbar_layer_
->PushResource(toolbar_resource
, nullptr, anonymize_toolbar
,
184 if (show_toolbar
&& !back_visible
)
185 toolbar_impact_height
= toolbar_resource
->padding
.height();
188 //----------------------------------------------------------------------------
189 // Compute Alpha and Visibility
190 //----------------------------------------------------------------------------
191 border_alpha
*= alpha
;
192 contour_alpha
*= alpha
;
193 shadow_alpha
*= alpha
;
194 close_alpha
*= alpha
;
195 toolbar_alpha
*= alpha
;
200 bool border_visible
= border_alpha
> 0.f
;
201 bool contour_visible
= border_alpha
< contour_alpha
&& contour_alpha
> 0.f
;
202 bool shadow_visible
= shadow_alpha
> 0.f
&& border_alpha
> 0.f
;
204 //----------------------------------------------------------------------------
205 // Compute Layer Sizes
206 //----------------------------------------------------------------------------
207 gfx::Size
shadow_size(width
+ shadow_padding_size
.width() * side_border_scale
,
208 height
+ shadow_padding_size
.height());
209 gfx::Size
border_size(width
+ border_padding_size
.width() * side_border_scale
,
210 height
+ border_padding_size
.height());
211 gfx::Size
contour_size(
212 width
+ contour_padding_size
.width() * side_border_scale
,
213 height
+ contour_padding_size
.height());
214 gfx::Size
close_button_size(close_btn_width
, border_padding
.y());
215 gfx::Size
title_size(width
- close_btn_effective_width
, border_padding
.y());
216 gfx::Size back_logo_size
;
217 // TODO(clholgat): Figure out why the back logo is null sometimes.
220 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
221 back_logo_resource_id
);
222 if (back_logo_resource
)
223 back_logo_size
= back_logo_resource
->size
;
226 // Store this size at a point as it might go negative during the inset
228 gfx::Point
desired_content_size_pt(
229 descaled_local_content_area
.width(),
230 descaled_local_content_area
.height() - toolbar_impact_height
);
232 // Shrink the toolbar layer so we properly clip if it's offset.
233 gfx::Size
toolbar_size(
234 toolbar_layer_
->layer()->bounds().width(),
235 toolbar_layer_
->layer()->bounds().height() - toolbar_y_offset
);
237 //----------------------------------------------------------------------------
238 // Compute Layer Positions
239 //----------------------------------------------------------------------------
240 gfx::PointF
shadow_position(-shadow_padding
.x() * side_border_scale
,
241 -shadow_padding
.y());
242 gfx::PointF
border_position(-border_padding
.x() * side_border_scale
,
243 -border_padding
.y());
244 gfx::PointF
contour_position(-contour_padding
.x() * side_border_scale
,
245 -contour_padding
.y());
246 gfx::PointF
toolbar_position(
247 0.f
, toolbar_layer_
->layer()->bounds().height() - toolbar_size
.height());
248 gfx::PointF
content_position(0.f
, toolbar_impact_height
);
249 gfx::PointF
back_logo_position(
250 ((descaled_local_content_area
.width() - back_logo_
->bounds().width()) *
253 ((descaled_local_content_area
.height() - back_logo_
->bounds().height()) *
256 gfx::PointF close_button_position
;
257 gfx::PointF title_position
;
259 close_button_position
.set_y(-border_padding
.y());
260 title_position
.set_y(-border_padding
.y());
261 if (!close_button_on_left
)
262 close_button_position
.set_x(width
- close_button_size
.width());
264 title_position
.set_x(close_btn_effective_width
);
266 //----------------------------------------------------------------------------
267 // Center Specific Assets in the Rects
268 //----------------------------------------------------------------------------
269 close_button_position
.Offset(
270 (close_button_size
.width() - close_btn_resource
->size
.width()) / 2.f
,
271 (close_button_size
.height() - close_btn_resource
->size
.height()) / 2.f
);
272 close_button_size
.SetSize(close_btn_resource
->size
.width(),
273 close_btn_resource
->size
.height());
275 //----------------------------------------------------------------------------
276 // Handle Insetting the Top Border Component
277 //----------------------------------------------------------------------------
279 float inset_diff
= inset_border
? border_padding
.y() : 0.f
;
280 descaled_local_content_area
.set_height(
281 descaled_local_content_area
.height() - inset_diff
);
282 scaled_local_content_area
.set_height(scaled_local_content_area
.height() -
283 inset_diff
* content_scale
);
284 shadow_size
.set_height(shadow_size
.height() - inset_diff
);
285 border_size
.set_height(border_size
.height() - inset_diff
);
286 contour_size
.set_height(contour_size
.height() - inset_diff
);
287 shadow_position
.set_y(shadow_position
.y() + inset_diff
);
288 border_position
.set_y(border_position
.y() + inset_diff
);
289 contour_position
.set_y(contour_position
.y() + inset_diff
);
290 close_button_position
.set_y(close_button_position
.y() + inset_diff
);
291 title_position
.set_y(title_position
.y() + inset_diff
);
293 // Scaled eventually, so have to descale the size difference first.
294 toolbar_position
.set_y(toolbar_position
.y() + inset_diff
/ content_scale
);
295 content_position
.set_y(content_position
.y() + inset_diff
/ content_scale
);
296 desired_content_size_pt
.set_y(desired_content_size_pt
.y() -
297 inset_diff
/ content_scale
);
300 const bool inset_toolbar
= !inset_border
;
301 if (!inset_toolbar
) {
302 float inset_diff
= toolbar_impact_height
;
303 toolbar_position
.set_y(toolbar_position
.y() - inset_diff
);
304 content_position
.set_y(content_position
.y() - inset_diff
);
305 desired_content_size_pt
.set_y(desired_content_size_pt
.y() + inset_diff
);
308 // Finally build the sizes that might have calculations that go negative.
309 gfx::Size
desired_content_size(desired_content_size_pt
.x(),
310 desired_content_size_pt
.y());
312 //----------------------------------------------------------------------------
313 // Calculate Content Visibility
314 //----------------------------------------------------------------------------
315 // Check if the rect we are drawing is larger than the content rect.
316 bool content_visible
= desired_content_size
.GetArea() > 0.f
;
318 // TODO(dtrainor): Improve these calculations to prune these layers out.
319 bool title_visible
= border_alpha
> 0.f
&& !back_visible
;
320 bool close_btn_visible
= title_visible
;
321 bool toolbar_visible
= show_toolbar
&& toolbar_alpha
> 0.f
&& !back_visible
;
323 //----------------------------------------------------------------------------
325 //----------------------------------------------------------------------------
326 border_position
.Offset(0.5f
, 0.5f
);
327 shadow_position
.Offset(0.5f
, 0.5f
);
328 contour_position
.Offset(0.5f
, 0.5f
);
329 title_position
.Offset(0.5f
, 0.5f
);
330 close_button_position
.Offset(0.5f
, 0.5f
);
332 border_size
.Enlarge(-1.f
, -1.f
);
333 shadow_size
.Enlarge(-1.f
, -1.f
);
335 //----------------------------------------------------------------------------
336 // Update Resource Ids
337 //----------------------------------------------------------------------------
338 shadow_
->SetUIResourceId(shadow_resource
->ui_resource
->id());
339 shadow_
->SetBorder(shadow_resource
->Border(shadow_size
));
340 shadow_
->SetAperture(shadow_resource
->aperture
);
342 contour_shadow_
->SetUIResourceId(contour_resource
->ui_resource
->id());
343 contour_shadow_
->SetBorder(contour_resource
->Border(contour_size
));
344 contour_shadow_
->SetAperture(contour_resource
->aperture
);
346 front_border_
->SetUIResourceId(border_resource
->ui_resource
->id());
347 front_border_
->SetAperture(border_resource
->aperture
);
348 front_border_
->SetBorder(border_resource
->Border(
350 gfx::InsetsF(1.f
, side_border_scale
, 1.f
, side_border_scale
)));
352 padding_
->SetBackgroundColor(back_visible
? back_logo_color
353 : default_background_color
);
355 if (title_visible
&& layer_title_cache_
)
356 title_layer
= layer_title_cache_
->GetTitleLayer(id
);
357 SetTitle(title_layer
);
359 close_button_
->SetUIResourceId(close_btn_resource
->ui_resource
->id());
362 gfx::Rect
rounded_descaled_content_area(
363 round(descaled_local_content_area
.x()),
364 round(descaled_local_content_area
.y()),
365 round(desired_content_size
.width()),
366 round(desired_content_size
.height()));
368 content_
->SetProperties(id
, can_use_live_layer
, can_use_ntp_fallback
,
369 static_to_view_blend
, true, alpha
, saturation
,
370 brightness
, rounded_descaled_content_area
,
371 gfx::Size(content_width
, content_height
));
372 } else if (back_logo_resource
) {
373 back_logo_
->SetUIResourceId(back_logo_resource
->ui_resource
->id());
376 //----------------------------------------------------------------------------
377 // Push Size, Position, Alpha and Transformations to Layers
378 //----------------------------------------------------------------------------
379 shadow_
->SetHideLayerAndSubtree(!shadow_visible
);
380 if (shadow_visible
) {
381 shadow_
->SetPosition(shadow_position
);
382 shadow_
->SetBounds(shadow_size
);
383 shadow_
->SetOpacity(shadow_alpha
);
386 contour_shadow_
->SetHideLayerAndSubtree(!contour_visible
);
387 if (contour_visible
) {
388 contour_shadow_
->SetPosition(contour_position
);
389 contour_shadow_
->SetBounds(contour_size
);
390 contour_shadow_
->SetOpacity(contour_alpha
);
393 front_border_
->SetHideLayerAndSubtree(!border_visible
);
394 if (border_visible
) {
395 front_border_
->SetPosition(border_position
);
396 front_border_
->SetBounds(border_size
);
397 front_border_
->SetOpacity(border_alpha
);
400 toolbar_layer_
->layer()->SetHideLayerAndSubtree(!toolbar_visible
);
401 if (toolbar_visible
) {
402 // toolbar_ Transform
403 gfx::Transform transform
;
404 transform
.Scale(content_scale
, content_scale
);
405 transform
.Translate(toolbar_position
.x(), toolbar_position
.y());
406 toolbar_layer_
->layer()->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
407 toolbar_layer_
->layer()->SetTransform(transform
);
408 toolbar_layer_
->layer()->SetOpacity(toolbar_alpha
);
410 toolbar_layer_
->layer()->SetMasksToBounds(
411 toolbar_layer_
->layer()->bounds() != toolbar_size
);
412 toolbar_layer_
->layer()->SetBounds(toolbar_size
);
416 gfx::PointF
vertically_centered_position(
419 (title_size
.height() - title_layer
->size().height()) / 2.f
);
421 title_
->SetPosition(vertically_centered_position
);
422 title_layer
->setBounds(title_size
);
423 title_layer
->setOpacity(border_alpha
);
426 close_button_
->SetHideLayerAndSubtree(!close_btn_visible
);
427 if (close_btn_visible
) {
428 close_button_
->SetPosition(close_button_position
);
429 close_button_
->SetBounds(close_button_size
);
430 // Non-linear alpha looks better.
431 close_button_
->SetOpacity(close_alpha
* close_alpha
* border_alpha
);
434 if (content_visible
&& attach_content
) {
436 // content_ and back_logo_ Transforms
437 gfx::Transform transform
;
438 transform
.Scale(content_scale
, content_scale
);
439 transform
.Translate(content_position
.x(), content_position
.y());
440 transform
.Translate(descaled_local_content_area
.x(),
441 descaled_local_content_area
.y());
443 content_
->layer()->SetHideLayerAndSubtree(back_visible
);
444 back_logo_
->SetHideLayerAndSubtree(!back_visible
);
447 content_
->layer()->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
448 content_
->layer()->SetTransform(transform
);
450 back_logo_
->SetPosition(back_logo_position
);
451 back_logo_
->SetBounds(back_logo_size
);
452 back_logo_
->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
453 back_logo_
->SetTransform(transform
);
454 // TODO: Set back logo alpha on leaf.
459 // padding_ Transform
460 gfx::Size content_bounds
;
462 content_bounds
= content_
->layer()->bounds();
464 gfx::Rect
padding_rect(
465 ComputePaddingPosition(content_bounds
, desired_content_size
));
466 padding_
->SetHideLayerAndSubtree(padding_rect
.IsEmpty());
467 padding_
->SetBounds(padding_rect
.size());
468 padding_
->SetOpacity(alpha
);
470 gfx::Transform transform
;
471 transform
.Scale(content_scale
, content_scale
);
472 transform
.Translate(padding_rect
.x() + content_position
.x(),
473 padding_rect
.y() + content_position
.y());
474 transform
.Translate(descaled_local_content_area
.x(),
475 descaled_local_content_area
.y());
476 padding_
->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
477 padding_
->SetTransform(transform
);
480 back_logo_
->SetHideLayerAndSubtree(true);
481 padding_
->SetHideLayerAndSubtree(true);
482 content_
->layer()->SetHideLayerAndSubtree(true);
487 gfx::PointF
pivot_origin(pivot_y
, pivot_x
);
489 gfx::Transform transform
;
491 if (rotation_x
!= 0 || rotation_y
!= 0) {
492 // Apply screen perspective if there are rotations.
493 transform
.Translate(content_width
/ 2, content_height
/ 2);
494 transform
.ApplyPerspectiveDepth(
495 content_width
> content_height
? content_width
: content_height
);
496 transform
.Translate(-content_width
/ 2, -content_height
/ 2);
498 // Translate to correct position on the screen
499 transform
.Translate(x
, y
);
501 // Apply pivot rotations
502 transform
.Translate(pivot_origin
.x(), pivot_origin
.y());
503 transform
.RotateAboutYAxis(rotation_y
);
504 transform
.RotateAboutXAxis(-rotation_x
);
505 transform
.Translate(-pivot_origin
.x(), -pivot_origin
.y());
507 // Translate to correct position on the screen
508 transform
.Translate(x
, y
);
510 transform
.Scale(border_scale
, border_scale
);
511 layer_
->SetTransform(transform
);
514 // Only applies the brightness filter if the value has changed and is less
516 if (brightness
!= brightness_
) {
517 brightness_
= brightness
;
518 cc::FilterOperations filters
;
519 if (brightness_
< 1.f
)
520 filters
.Append(cc::FilterOperation::CreateBrightnessFilter(brightness_
));
521 layer_
->SetFilters(filters
);
525 scoped_refptr
<cc::Layer
> TabLayer::layer() {
529 TabLayer::TabLayer(bool incognito
,
530 ui::ResourceManager
* resource_manager
,
531 LayerTitleCache
* layer_title_cache
,
532 TabContentManager
* tab_content_manager
)
533 : incognito_(incognito
),
534 resource_manager_(resource_manager
),
535 layer_title_cache_(layer_title_cache
),
536 layer_(cc::Layer::Create(content::Compositor::LayerSettings())),
537 toolbar_layer_(ToolbarLayer::Create()),
538 title_(cc::Layer::Create(content::Compositor::LayerSettings())),
539 content_(ContentLayer::Create(tab_content_manager
)),
541 cc::SolidColorLayer::Create(content::Compositor::LayerSettings())),
543 cc::UIResourceLayer::Create(content::Compositor::LayerSettings())),
545 cc::NinePatchLayer::Create(content::Compositor::LayerSettings())),
547 cc::NinePatchLayer::Create(content::Compositor::LayerSettings())),
548 shadow_(cc::NinePatchLayer::Create(content::Compositor::LayerSettings())),
550 cc::UIResourceLayer::Create(content::Compositor::LayerSettings())),
552 layer_
->AddChild(shadow_
);
553 layer_
->AddChild(contour_shadow_
);
554 layer_
->AddChild(padding_
);
555 layer_
->AddChild(content_
->layer());
556 layer_
->AddChild(back_logo_
);
557 layer_
->AddChild(front_border_
);
558 layer_
->AddChild(title_
.get());
559 layer_
->AddChild(close_button_
);
560 layer_
->AddChild(toolbar_layer_
->layer());
562 contour_shadow_
->SetIsDrawable(true);
563 padding_
->SetIsDrawable(true);
564 front_border_
->SetIsDrawable(true);
565 shadow_
->SetIsDrawable(true);
566 close_button_
->SetIsDrawable(true);
567 back_logo_
->SetIsDrawable(true);
570 TabLayer::~TabLayer() {
573 void TabLayer::SetTitle(DecorationTitle
* title
) {
574 scoped_refptr
<cc::Layer
> layer
= title
? title
->layer() : nullptr;
577 title_
->RemoveAllChildren();
579 const cc::LayerList
& children
= title_
->children();
580 if (children
.size() == 0 || children
[0]->id() != layer
->id()) {
581 title_
->RemoveAllChildren();
582 title_
->AddChild(layer
);
587 title
->SetUIResourceIds();
590 } // namespace android
591 } // namespace chrome