#include "cc/layers/scrollbar_layer_impl_base.h"
#include <algorithm>
#include "cc/base/math_util.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/scroll_node.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
ScrollbarLayerImplBase::ScrollbarLayerImplBase(
LayerTreeImpl* tree_impl,
int id,
ScrollbarOrientation orientation,
bool is_left_side_vertical_scrollbar,
bool is_overlay)
: LayerImpl(tree_impl, id),
is_overlay_scrollbar_(is_overlay),
thumb_thickness_scale_factor_(1.f),
current_pos_(0.f),
clip_layer_length_(0.f),
scroll_layer_length_(0.f),
orientation_(orientation),
is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar),
vertical_adjust_(0.f),
has_find_in_page_tickmarks_(false) {}
ScrollbarLayerImplBase::~ScrollbarLayerImplBase() {
layer_tree_impl()->UnregisterScrollbar(this);
}
void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) {
LayerImpl::PushPropertiesTo(layer);
DCHECK(layer->IsScrollbarLayer());
ScrollbarLayerImplBase* scrollbar_layer = ToScrollbarLayer(layer);
scrollbar_layer->SetHasFindInPageTickmarks(has_find_in_page_tickmarks_);
scrollbar_layer->set_is_overlay_scrollbar(is_overlay_scrollbar_);
scrollbar_layer->set_is_web_test(is_web_test_);
scrollbar_layer->SetScrollElementId(scroll_element_id());
}
DamageReasonSet ScrollbarLayerImplBase::GetDamageReasons() const {
DamageReasonSet reasons;
if (opacity_changed_for_fade_out_animation_) {
reasons.Put(DamageReason::kScrollbarFadeOutAnimation);
}
if (property_changed_for_other_reasons_ || !update_rect().IsEmpty()) {
reasons.Put(DamageReason::kCompositorScroll);
}
return reasons;
}
void ScrollbarLayerImplBase::ResetChangeTracking() {
LayerImpl::ResetChangeTracking();
opacity_changed_for_fade_out_animation_ = false;
property_changed_for_other_reasons_ = false;
}
bool ScrollbarLayerImplBase::IsScrollbarLayer() const {
return true;
}
void ScrollbarLayerImplBase::SetScrollElementId(ElementId scroll_element_id) {
if (scroll_element_id_ == scroll_element_id)
return;
layer_tree_impl()->UnregisterScrollbar(this);
scroll_element_id_ = scroll_element_id;
layer_tree_impl()->RegisterScrollbar(this);
}
bool ScrollbarLayerImplBase::SetCurrentPos(float current_pos) {
if (current_pos_ == current_pos)
return false;
current_pos_ = current_pos;
property_changed_for_other_reasons_ = true;
NoteLayerPropertyChanged();
return true;
}
bool ScrollbarLayerImplBase::CanScrollOrientation() const {
PropertyTrees* property_trees = layer_tree_impl()->property_trees();
const auto* scroll_node =
property_trees->scroll_tree().FindNodeFromElementId(scroll_element_id_);
DCHECK(scroll_node);
if (!scroll_node)
return false;
if (orientation() == ScrollbarOrientation::kHorizontal) {
if (!scroll_node->user_scrollable_horizontal)
return false;
} else {
if (!scroll_node->user_scrollable_vertical)
return false;
}
return !MathUtil::IsFloatNearlyTheSame(clip_layer_length(),
scroll_layer_length()) &&
clip_layer_length() < scroll_layer_length();
}
void ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
if (vertical_adjust_ == vertical_adjust)
return;
vertical_adjust_ = vertical_adjust;
property_changed_for_other_reasons_ = true;
NoteLayerPropertyChanged();
}
void ScrollbarLayerImplBase::SetClipLayerLength(float clip_layer_length) {
if (clip_layer_length_ == clip_layer_length)
return;
clip_layer_length_ = clip_layer_length;
property_changed_for_other_reasons_ = true;
NoteLayerPropertyChanged();
if (GetScrollbarAnimator() == LayerTreeSettings::AURA_OVERLAY) {
layer_tree_impl()->RequestShowScrollbars(scroll_element_id_);
}
}
void ScrollbarLayerImplBase::SetScrollLayerLength(float scroll_layer_length) {
if (scroll_layer_length_ == scroll_layer_length)
return;
scroll_layer_length_ = scroll_layer_length;
property_changed_for_other_reasons_ = true;
NoteLayerPropertyChanged();
if (GetScrollbarAnimator() == LayerTreeSettings::AURA_OVERLAY &&
!IsFluentOverlayScrollbarEnabled()) {
layer_tree_impl()->RequestShowScrollbars(scroll_element_id_);
}
}
void ScrollbarLayerImplBase::SetThumbThicknessScaleFactor(float factor) {
if (thumb_thickness_scale_factor_ == factor)
return;
thumb_thickness_scale_factor_ = factor;
property_changed_for_other_reasons_ = true;
NoteLayerPropertyChanged();
}
gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRectWithThumbThicknessScale(
float thumb_thickness_scale_factor) const {
float track_length = TrackLength();
int thumb_length = ThumbLength();
int thumb_thickness = ThumbThickness();
float maximum = std::max(scroll_layer_length() - clip_layer_length(), 0.0f);
float clamped_current_pos = std::clamp(current_pos(), 0.0f, maximum);
int thumb_offset = TrackStart();
if (maximum > 0) {
float ratio = clamped_current_pos / maximum;
float max_offset = track_length - thumb_length;
thumb_offset += static_cast<int>(ratio * max_offset);
}
float thumb_thickness_adjustment =
thumb_thickness * (1.f - thumb_thickness_scale_factor);
gfx::RectF thumb_rect;
if (orientation_ == ScrollbarOrientation::kHorizontal) {
thumb_rect = gfx::RectF(thumb_offset,
vertical_adjust_ + thumb_thickness_adjustment,
thumb_length,
thumb_thickness - thumb_thickness_adjustment);
} else {
thumb_rect = gfx::RectF(
is_left_side_vertical_scrollbar_
? bounds().width() - thumb_thickness
: thumb_thickness_adjustment,
thumb_offset,
thumb_thickness - thumb_thickness_adjustment,
thumb_length);
}
return gfx::ToEnclosingRect(thumb_rect);
}
gfx::Rect ScrollbarLayerImplBase::ComputeHitTestableExpandedThumbQuadRect()
const {
DCHECK(is_overlay_scrollbar());
return ComputeThumbQuadRectWithThumbThicknessScale(1.f);
}
gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const {
return ComputeThumbQuadRectWithThumbThicknessScale(
thumb_thickness_scale_factor_);
}
gfx::Rect ScrollbarLayerImplBase::ComputeHitTestableThumbQuadRect() const {
return ComputeThumbQuadRect();
}
void ScrollbarLayerImplBase::SetOverlayScrollbarLayerOpacityAnimated(
float opacity,
bool fade_out_animation) {
DCHECK(is_overlay_scrollbar());
if (!layer_tree_impl())
return;
if (layer_tree_impl()->settings().trees_in_viz_in_viz_process &&
!layer_tree_impl()->settings().TreeAnimationsInVizInVizProcess()) {
return;
}
PropertyTrees* property_trees = layer_tree_impl()->property_trees();
EffectNode* node =
property_trees->effect_tree_mutable().Node(effect_tree_index());
if (node->opacity == opacity) {
return;
}
node->opacity = opacity;
node->effect_changed = true;
property_trees->set_changed(true);
property_trees->effect_tree_mutable().set_needs_update(true);
layer_tree_impl()->set_needs_update_draw_properties();
opacity_changed_for_fade_out_animation_ |= fade_out_animation;
property_changed_for_other_reasons_ |= !fade_out_animation;
}
LayerTreeSettings::ScrollbarAnimator
ScrollbarLayerImplBase::GetScrollbarAnimator() const {
return layer_tree_impl()->settings().scrollbar_animator;
}
float ScrollbarLayerImplBase::GetIdleThicknessScale() const {
return layer_tree_impl()->settings().idle_thickness_scale;
}
void ScrollbarLayerImplBase::SetHasFindInPageTickmarks(
bool has_find_in_page_tickmarks) {
if (has_find_in_page_tickmarks_ == has_find_in_page_tickmarks) {
return;
}
has_find_in_page_tickmarks_ = has_find_in_page_tickmarks;
property_changed_for_other_reasons_ = true;
NoteLayerPropertyChanged();
}
float ScrollbarLayerImplBase::OverlayScrollbarOpacity() const {
return Opacity();
}
bool ScrollbarLayerImplBase::SupportsDragSnapBack() const {
return false;
}
bool ScrollbarLayerImplBase::JumpOnTrackClick() const {
return false;
}
bool ScrollbarLayerImplBase::IsFluentScrollbarEnabled() const {
return layer_tree_impl()->settings().enable_fluent_scrollbar;
}
bool ScrollbarLayerImplBase::IsFluentOverlayScrollbarEnabled() const {
return layer_tree_impl()->settings().enable_fluent_overlay_scrollbar;
}
gfx::Rect ScrollbarLayerImplBase::BackButtonRect() const {
return gfx::Rect(0, 0);
}
gfx::Rect ScrollbarLayerImplBase::ForwardButtonRect() const {
return gfx::Rect(0, 0);
}
gfx::Rect ScrollbarLayerImplBase::BackTrackRect() const {
return gfx::Rect(0, 0);
}
gfx::Rect ScrollbarLayerImplBase::ForwardTrackRect() const {
return gfx::Rect(0, 0);
}
ScrollbarPart ScrollbarLayerImplBase::IdentifyScrollbarPart(
const gfx::PointF position_in_widget) const {
const gfx::Point pointer_location(position_in_widget.x(),
position_in_widget.y());
if (BackButtonRect().Contains(pointer_location))
return ScrollbarPart::kBackButton;
if (ForwardButtonRect().Contains(pointer_location))
return ScrollbarPart::kForwardButton;
if (ComputeHitTestableThumbQuadRect().Contains(pointer_location))
return ScrollbarPart::kThumb;
if (BackTrackRect().Contains(pointer_location))
return ScrollbarPart::kBackTrack;
if (ForwardTrackRect().Contains(pointer_location))
return ScrollbarPart::kForwardTrack;
return ScrollbarPart::kNoPart;
}
}