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
,
99 int toolbar_textbox_background_color
,
101 float toolbar_y_offset
,
102 float side_border_scale
,
106 layer_
->SetHideLayerAndSubtree(true);
110 layer_
->SetHideLayerAndSubtree(false);
112 // Grab required resources
113 ui::ResourceManager::Resource
* border_resource
=
114 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
116 ui::ResourceManager::Resource
* shadow_resource
=
117 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
119 ui::ResourceManager::Resource
* contour_resource
=
120 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
121 contour_resource_id
);
122 ui::ResourceManager::Resource
* toolbar_resource
=
123 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_DYNAMIC
,
124 toolbar_resource_id
);
125 ui::ResourceManager::Resource
* close_btn_resource
=
126 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
127 close_button_resource_id
);
128 ui::ResourceManager::Resource
* back_logo_resource
= nullptr;
130 DecorationTitle
* title_layer
= nullptr;
132 //----------------------------------------------------------------------------
133 // Handle Border Scaling (Upscale/Downscale everything until final scaling)
134 //----------------------------------------------------------------------------
135 width
/= border_scale
;
136 height
/= border_scale
;
137 shadow_x
/= border_scale
;
138 shadow_y
/= border_scale
;
139 shadow_width
/= border_scale
;
140 shadow_height
/= border_scale
;
142 //----------------------------------------------------------------------------
143 // Precalculate Helper Values
144 //----------------------------------------------------------------------------
145 const gfx::RectF
border_padding(border_resource
->padding
);
146 const gfx::RectF
shadow_padding(shadow_resource
->padding
);
147 const gfx::RectF
contour_padding(contour_resource
->padding
);
149 // If we're in portrait and we're RTL, the close button is on the left.
150 // Similarly if we're in landscape and we're in LTR, the close button is on
152 const bool close_button_on_left
= is_portrait
== l10n_util::IsLayoutRtl();
153 const bool back_visible
= cos(rotation_x
* SK_MScalarPI
/ 180.0f
) < 0 ||
154 cos(rotation_y
* SK_MScalarPI
/ 180.0f
) < 0;
156 const float content_scale
= width
/ content_width
;
157 gfx::RectF
content_area(0.f
, 0.f
, content_width
, content_height
);
158 gfx::RectF
scaled_local_content_area(shadow_x
, shadow_y
, shadow_width
,
160 gfx::RectF
descaled_local_content_area(
161 scaled_local_content_area
.x() / content_scale
,
162 scaled_local_content_area
.y() / content_scale
,
163 scaled_local_content_area
.width() / content_scale
,
164 scaled_local_content_area
.height() / content_scale
);
166 const gfx::Size
shadow_padding_size(
167 shadow_resource
->size
.width() - shadow_padding
.width(),
168 shadow_resource
->size
.height() - shadow_padding
.height());
169 const gfx::Size
border_padding_size(
170 border_resource
->size
.width() - border_padding
.width(),
171 border_resource
->size
.height() - border_padding
.height());
172 const gfx::Size
contour_padding_size(
173 contour_resource
->size
.width() - contour_padding
.width(),
174 contour_resource
->size
.height() - contour_padding
.height());
176 const float close_btn_effective_width
= close_btn_width
* close_alpha
;
178 float toolbar_impact_height
= 0;
179 if (toolbar_resource
) {
180 //--------------------------------------------------------------------------
181 // Update Resource Ids For Layers That Impact Layout
182 //--------------------------------------------------------------------------
184 // TODO(kkimlabs): Tab switcher doesn't show the progress bar.
185 toolbar_layer_
->PushResource(toolbar_resource
, anonymize_toolbar
,
186 toolbar_textbox_background_color
, false, 1.f
);
187 toolbar_layer_
->UpdateProgressBar(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
189 if (show_toolbar
&& !back_visible
)
190 toolbar_impact_height
= toolbar_resource
->padding
.height();
193 //----------------------------------------------------------------------------
194 // Compute Alpha and Visibility
195 //----------------------------------------------------------------------------
196 border_alpha
*= alpha
;
197 contour_alpha
*= alpha
;
198 shadow_alpha
*= alpha
;
199 close_alpha
*= alpha
;
200 toolbar_alpha
*= alpha
;
205 bool border_visible
= border_alpha
> 0.f
;
206 bool contour_visible
= border_alpha
< contour_alpha
&& contour_alpha
> 0.f
;
207 bool shadow_visible
= shadow_alpha
> 0.f
&& border_alpha
> 0.f
;
209 //----------------------------------------------------------------------------
210 // Compute Layer Sizes
211 //----------------------------------------------------------------------------
212 gfx::Size
shadow_size(width
+ shadow_padding_size
.width() * side_border_scale
,
213 height
+ shadow_padding_size
.height());
214 gfx::Size
border_size(width
+ border_padding_size
.width() * side_border_scale
,
215 height
+ border_padding_size
.height());
216 gfx::Size
contour_size(
217 width
+ contour_padding_size
.width() * side_border_scale
,
218 height
+ contour_padding_size
.height());
219 gfx::Size
close_button_size(close_btn_width
, border_padding
.y());
220 gfx::Size
title_size(width
- close_btn_effective_width
, border_padding
.y());
221 gfx::Size back_logo_size
;
222 // TODO(clholgat): Figure out why the back logo is null sometimes.
225 resource_manager_
->GetResource(ui::ANDROID_RESOURCE_TYPE_STATIC
,
226 back_logo_resource_id
);
227 if (back_logo_resource
)
228 back_logo_size
= back_logo_resource
->size
;
231 // Store this size at a point as it might go negative during the inset
233 gfx::Point
desired_content_size_pt(
234 descaled_local_content_area
.width(),
235 descaled_local_content_area
.height() - toolbar_impact_height
);
237 // Shrink the toolbar layer so we properly clip if it's offset.
238 gfx::Size
toolbar_size(
239 toolbar_layer_
->layer()->bounds().width(),
240 toolbar_layer_
->layer()->bounds().height() - toolbar_y_offset
);
242 //----------------------------------------------------------------------------
243 // Compute Layer Positions
244 //----------------------------------------------------------------------------
245 gfx::PointF
shadow_position(-shadow_padding
.x() * side_border_scale
,
246 -shadow_padding
.y());
247 gfx::PointF
border_position(-border_padding
.x() * side_border_scale
,
248 -border_padding
.y());
249 gfx::PointF
contour_position(-contour_padding
.x() * side_border_scale
,
250 -contour_padding
.y());
251 gfx::PointF
toolbar_position(
252 0.f
, toolbar_layer_
->layer()->bounds().height() - toolbar_size
.height());
253 gfx::PointF
content_position(0.f
, toolbar_impact_height
);
254 gfx::PointF
back_logo_position(
255 ((descaled_local_content_area
.width() - back_logo_
->bounds().width()) *
258 ((descaled_local_content_area
.height() - back_logo_
->bounds().height()) *
261 gfx::PointF close_button_position
;
262 gfx::PointF title_position
;
264 close_button_position
.set_y(-border_padding
.y());
265 title_position
.set_y(-border_padding
.y());
266 if (!close_button_on_left
)
267 close_button_position
.set_x(width
- close_button_size
.width());
269 title_position
.set_x(close_btn_effective_width
);
271 //----------------------------------------------------------------------------
272 // Center Specific Assets in the Rects
273 //----------------------------------------------------------------------------
274 close_button_position
.Offset(
275 (close_button_size
.width() - close_btn_resource
->size
.width()) / 2.f
,
276 (close_button_size
.height() - close_btn_resource
->size
.height()) / 2.f
);
277 close_button_size
.SetSize(close_btn_resource
->size
.width(),
278 close_btn_resource
->size
.height());
280 //----------------------------------------------------------------------------
281 // Handle Insetting the Top Border Component
282 //----------------------------------------------------------------------------
284 float inset_diff
= inset_border
? border_padding
.y() : 0.f
;
285 descaled_local_content_area
.set_height(
286 descaled_local_content_area
.height() - inset_diff
);
287 scaled_local_content_area
.set_height(scaled_local_content_area
.height() -
288 inset_diff
* content_scale
);
289 shadow_size
.set_height(shadow_size
.height() - inset_diff
);
290 border_size
.set_height(border_size
.height() - inset_diff
);
291 contour_size
.set_height(contour_size
.height() - inset_diff
);
292 shadow_position
.set_y(shadow_position
.y() + inset_diff
);
293 border_position
.set_y(border_position
.y() + inset_diff
);
294 contour_position
.set_y(contour_position
.y() + inset_diff
);
295 close_button_position
.set_y(close_button_position
.y() + inset_diff
);
296 title_position
.set_y(title_position
.y() + inset_diff
);
298 // Scaled eventually, so have to descale the size difference first.
299 toolbar_position
.set_y(toolbar_position
.y() + inset_diff
/ content_scale
);
300 content_position
.set_y(content_position
.y() + inset_diff
/ content_scale
);
301 desired_content_size_pt
.set_y(desired_content_size_pt
.y() -
302 inset_diff
/ content_scale
);
305 const bool inset_toolbar
= !inset_border
;
306 if (!inset_toolbar
) {
307 float inset_diff
= toolbar_impact_height
;
308 toolbar_position
.set_y(toolbar_position
.y() - inset_diff
);
309 content_position
.set_y(content_position
.y() - inset_diff
);
310 desired_content_size_pt
.set_y(desired_content_size_pt
.y() + inset_diff
);
313 // Finally build the sizes that might have calculations that go negative.
314 gfx::Size
desired_content_size(desired_content_size_pt
.x(),
315 desired_content_size_pt
.y());
317 //----------------------------------------------------------------------------
318 // Calculate Content Visibility
319 //----------------------------------------------------------------------------
320 // Check if the rect we are drawing is larger than the content rect.
321 bool content_visible
= desired_content_size
.GetArea() > 0.f
;
323 // TODO(dtrainor): Improve these calculations to prune these layers out.
324 bool title_visible
= border_alpha
> 0.f
&& !back_visible
;
325 bool close_btn_visible
= title_visible
;
326 bool toolbar_visible
= show_toolbar
&& toolbar_alpha
> 0.f
&& !back_visible
;
328 //----------------------------------------------------------------------------
330 //----------------------------------------------------------------------------
331 border_position
.Offset(0.5f
, 0.5f
);
332 shadow_position
.Offset(0.5f
, 0.5f
);
333 contour_position
.Offset(0.5f
, 0.5f
);
334 title_position
.Offset(0.5f
, 0.5f
);
335 close_button_position
.Offset(0.5f
, 0.5f
);
337 border_size
.Enlarge(-1.f
, -1.f
);
338 shadow_size
.Enlarge(-1.f
, -1.f
);
340 //----------------------------------------------------------------------------
341 // Update Resource Ids
342 //----------------------------------------------------------------------------
343 shadow_
->SetUIResourceId(shadow_resource
->ui_resource
->id());
344 shadow_
->SetBorder(shadow_resource
->Border(shadow_size
));
345 shadow_
->SetAperture(shadow_resource
->aperture
);
347 contour_shadow_
->SetUIResourceId(contour_resource
->ui_resource
->id());
348 contour_shadow_
->SetBorder(contour_resource
->Border(contour_size
));
349 contour_shadow_
->SetAperture(contour_resource
->aperture
);
351 front_border_
->SetUIResourceId(border_resource
->ui_resource
->id());
352 front_border_
->SetAperture(border_resource
->aperture
);
353 front_border_
->SetBorder(border_resource
->Border(
355 gfx::InsetsF(1.f
, side_border_scale
, 1.f
, side_border_scale
)));
357 padding_
->SetBackgroundColor(back_visible
? back_logo_color
358 : default_background_color
);
360 if (title_visible
&& layer_title_cache_
)
361 title_layer
= layer_title_cache_
->GetTitleLayer(id
);
362 SetTitle(title_layer
);
364 close_button_
->SetUIResourceId(close_btn_resource
->ui_resource
->id());
367 gfx::Rect
rounded_descaled_content_area(
368 round(descaled_local_content_area
.x()),
369 round(descaled_local_content_area
.y()),
370 round(desired_content_size
.width()),
371 round(desired_content_size
.height()));
373 content_
->SetProperties(id
, can_use_live_layer
, can_use_ntp_fallback
,
374 static_to_view_blend
, true, alpha
, saturation
,
375 rounded_descaled_content_area
,
376 gfx::Size(content_width
, content_height
));
377 } else if (back_logo_resource
) {
378 back_logo_
->SetUIResourceId(back_logo_resource
->ui_resource
->id());
381 //----------------------------------------------------------------------------
382 // Push Size, Position, Alpha and Transformations to Layers
383 //----------------------------------------------------------------------------
384 shadow_
->SetHideLayerAndSubtree(!shadow_visible
);
385 if (shadow_visible
) {
386 shadow_
->SetPosition(shadow_position
);
387 shadow_
->SetBounds(shadow_size
);
388 shadow_
->SetOpacity(shadow_alpha
);
391 contour_shadow_
->SetHideLayerAndSubtree(!contour_visible
);
392 if (contour_visible
) {
393 contour_shadow_
->SetPosition(contour_position
);
394 contour_shadow_
->SetBounds(contour_size
);
395 contour_shadow_
->SetOpacity(contour_alpha
);
398 front_border_
->SetHideLayerAndSubtree(!border_visible
);
399 if (border_visible
) {
400 front_border_
->SetPosition(border_position
);
401 front_border_
->SetBounds(border_size
);
402 front_border_
->SetOpacity(border_alpha
);
405 toolbar_layer_
->layer()->SetHideLayerAndSubtree(!toolbar_visible
);
406 if (toolbar_visible
) {
407 // toolbar_ Transform
408 gfx::Transform transform
;
409 transform
.Scale(content_scale
, content_scale
);
410 transform
.Translate(toolbar_position
.x(), toolbar_position
.y());
411 toolbar_layer_
->layer()->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
412 toolbar_layer_
->layer()->SetTransform(transform
);
413 toolbar_layer_
->layer()->SetOpacity(toolbar_alpha
);
415 toolbar_layer_
->layer()->SetMasksToBounds(
416 toolbar_layer_
->layer()->bounds() != toolbar_size
);
417 toolbar_layer_
->layer()->SetBounds(toolbar_size
);
421 gfx::PointF
vertically_centered_position(
424 (title_size
.height() - title_layer
->size().height()) / 2.f
);
426 title_
->SetPosition(vertically_centered_position
);
427 title_layer
->setBounds(title_size
);
428 title_layer
->setOpacity(border_alpha
);
431 close_button_
->SetHideLayerAndSubtree(!close_btn_visible
);
432 if (close_btn_visible
) {
433 close_button_
->SetPosition(close_button_position
);
434 close_button_
->SetBounds(close_button_size
);
435 // Non-linear alpha looks better.
436 close_button_
->SetOpacity(close_alpha
* close_alpha
* border_alpha
);
439 if (content_visible
&& attach_content
) {
441 // content_ and back_logo_ Transforms
442 gfx::Transform transform
;
443 transform
.Scale(content_scale
, content_scale
);
444 transform
.Translate(content_position
.x(), content_position
.y());
445 transform
.Translate(descaled_local_content_area
.x(),
446 descaled_local_content_area
.y());
448 content_
->layer()->SetHideLayerAndSubtree(back_visible
);
449 back_logo_
->SetHideLayerAndSubtree(!back_visible
);
452 content_
->layer()->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
453 content_
->layer()->SetTransform(transform
);
455 back_logo_
->SetPosition(back_logo_position
);
456 back_logo_
->SetBounds(back_logo_size
);
457 back_logo_
->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
458 back_logo_
->SetTransform(transform
);
459 // TODO: Set back logo alpha on leaf.
464 // padding_ Transform
465 gfx::Size content_bounds
;
467 content_bounds
= content_
->layer()->bounds();
469 gfx::Rect
padding_rect(
470 ComputePaddingPosition(content_bounds
, desired_content_size
));
471 padding_
->SetHideLayerAndSubtree(padding_rect
.IsEmpty());
472 padding_
->SetBounds(padding_rect
.size());
473 padding_
->SetOpacity(alpha
);
475 gfx::Transform transform
;
476 transform
.Scale(content_scale
, content_scale
);
477 transform
.Translate(padding_rect
.x() + content_position
.x(),
478 padding_rect
.y() + content_position
.y());
479 transform
.Translate(descaled_local_content_area
.x(),
480 descaled_local_content_area
.y());
481 padding_
->SetTransformOrigin(gfx::Point3F(0.f
, 0.f
, 0.f
));
482 padding_
->SetTransform(transform
);
485 back_logo_
->SetHideLayerAndSubtree(true);
486 padding_
->SetHideLayerAndSubtree(true);
487 content_
->layer()->SetHideLayerAndSubtree(true);
492 gfx::PointF
pivot_origin(pivot_y
, pivot_x
);
494 gfx::Transform transform
;
496 if (rotation_x
!= 0 || rotation_y
!= 0) {
497 // Apply screen perspective if there are rotations.
498 transform
.Translate(content_width
/ 2, content_height
/ 2);
499 transform
.ApplyPerspectiveDepth(
500 content_width
> content_height
? content_width
: content_height
);
501 transform
.Translate(-content_width
/ 2, -content_height
/ 2);
503 // Translate to correct position on the screen
504 transform
.Translate(x
, y
);
506 // Apply pivot rotations
507 transform
.Translate(pivot_origin
.x(), pivot_origin
.y());
508 transform
.RotateAboutYAxis(rotation_y
);
509 transform
.RotateAboutXAxis(-rotation_x
);
510 transform
.Translate(-pivot_origin
.x(), -pivot_origin
.y());
512 // Translate to correct position on the screen
513 transform
.Translate(x
, y
);
515 transform
.Scale(border_scale
, border_scale
);
516 layer_
->SetTransform(transform
);
519 // Only applies the brightness filter if the value has changed and is less
521 if (brightness
!= brightness_
) {
522 brightness_
= brightness
;
523 cc::FilterOperations filters
;
524 if (brightness_
< 1.f
)
525 filters
.Append(cc::FilterOperation::CreateBrightnessFilter(brightness_
));
526 layer_
->SetFilters(filters
);
530 scoped_refptr
<cc::Layer
> TabLayer::layer() {
534 TabLayer::TabLayer(bool incognito
,
535 ui::ResourceManager
* resource_manager
,
536 LayerTitleCache
* layer_title_cache
,
537 TabContentManager
* tab_content_manager
)
538 : incognito_(incognito
),
539 resource_manager_(resource_manager
),
540 layer_title_cache_(layer_title_cache
),
541 layer_(cc::Layer::Create(content::Compositor::LayerSettings())),
542 toolbar_layer_(ToolbarLayer::Create()),
543 title_(cc::Layer::Create(content::Compositor::LayerSettings())),
544 content_(ContentLayer::Create(tab_content_manager
)),
546 cc::SolidColorLayer::Create(content::Compositor::LayerSettings())),
548 cc::UIResourceLayer::Create(content::Compositor::LayerSettings())),
550 cc::NinePatchLayer::Create(content::Compositor::LayerSettings())),
552 cc::NinePatchLayer::Create(content::Compositor::LayerSettings())),
553 shadow_(cc::NinePatchLayer::Create(content::Compositor::LayerSettings())),
555 cc::UIResourceLayer::Create(content::Compositor::LayerSettings())),
557 layer_
->AddChild(shadow_
);
558 layer_
->AddChild(contour_shadow_
);
559 layer_
->AddChild(padding_
);
560 layer_
->AddChild(content_
->layer());
561 layer_
->AddChild(back_logo_
);
562 layer_
->AddChild(front_border_
);
563 layer_
->AddChild(title_
.get());
564 layer_
->AddChild(close_button_
);
565 layer_
->AddChild(toolbar_layer_
->layer());
567 contour_shadow_
->SetIsDrawable(true);
568 padding_
->SetIsDrawable(true);
569 front_border_
->SetIsDrawable(true);
570 shadow_
->SetIsDrawable(true);
571 close_button_
->SetIsDrawable(true);
572 back_logo_
->SetIsDrawable(true);
575 TabLayer::~TabLayer() {
578 void TabLayer::SetTitle(DecorationTitle
* title
) {
579 scoped_refptr
<cc::Layer
> layer
= title
? title
->layer() : nullptr;
582 title_
->RemoveAllChildren();
584 const cc::LayerList
& children
= title_
->children();
585 if (children
.size() == 0 || children
[0]->id() != layer
->id()) {
586 title_
->RemoveAllChildren();
587 title_
->AddChild(layer
);
592 title
->SetUIResourceIds();
595 } // namespace android
596 } // namespace chrome