1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/input/browser_controls_offset_manager.h"
#include <stdint.h>
#include <algorithm>
#include <utility>
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/types/optional_ref.h"
#include "cc/input/browser_controls_offset_manager_client.h"
#include "cc/input/browser_controls_offset_tag_modifications.h"
#include "cc/trees/layer_tree_impl.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/quads/offset_tag.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
namespace {
// These constants were chosen empirically for their visually pleasant behavior.
// Contact tedchoc@chromium.org for questions about changing these values.
const int64_t kShowHideMaxDurationMs = 200;
// TODO(sinansahin): Temporary value, pending UX guidance probably.
const int64_t kHeightChangeDurationMs = 200;
}
// static
std::unique_ptr<BrowserControlsOffsetManager>
BrowserControlsOffsetManager::Create(BrowserControlsOffsetManagerClient* client,
float controls_show_threshold,
float controls_hide_threshold) {
return base::WrapUnique(new BrowserControlsOffsetManager(
client, controls_show_threshold, controls_hide_threshold));
}
BrowserControlsOffsetManager::BrowserControlsOffsetManager(
BrowserControlsOffsetManagerClient* client,
float controls_show_threshold,
float controls_hide_threshold)
: client_(client),
permitted_state_(BrowserControlsState::kBoth),
accumulated_scroll_delta_(0.f),
baseline_top_content_offset_(0.f),
baseline_bottom_content_offset_(0.f),
controls_show_threshold_(controls_hide_threshold),
controls_hide_threshold_(controls_show_threshold),
pinch_gesture_active_(false),
constraint_changed_since_commit_(false),
top_min_height_change_in_progress_(false),
bottom_min_height_change_in_progress_(false),
top_controls_min_height_offset_(0.f),
bottom_controls_min_height_offset_(0.f) {
CHECK(client_);
UpdateOldBrowserControlsParams();
}
BrowserControlsOffsetManager::~BrowserControlsOffsetManager() = default;
float BrowserControlsOffsetManager::ControlsTopOffset() const {
return ContentTopOffset() - TopControlsHeight();
}
float BrowserControlsOffsetManager::ContentTopOffset() const {
return TopControlsHeight() > 0
? TopControlsShownRatio() * TopControlsHeight() : 0.0f;
}
float BrowserControlsOffsetManager::TopControlsShownRatio() const {
return client_->CurrentTopControlsShownRatio();
}
float BrowserControlsOffsetManager::TopControlsHeight() const {
return client_->TopControlsHeight();
}
float BrowserControlsOffsetManager::TopControlsMinHeight() const {
return client_->TopControlsMinHeight();
}
int BrowserControlsOffsetManager::TopControlsAdditionalHeight() const {
return offset_tag_modifications_.top_controls_additional_height;
}
int BrowserControlsOffsetManager::BottomControlsAdditionalHeight() const {
return offset_tag_modifications_.bottom_controls_additional_height;
}
viz::OffsetTag BrowserControlsOffsetManager::BottomControlsOffsetTag() const {
return offset_tag_modifications_.tags.bottom_controls_offset_tag;
}
viz::OffsetTag BrowserControlsOffsetManager::ContentOffsetTag() const {
return offset_tag_modifications_.tags.content_offset_tag;
}
viz::OffsetTag BrowserControlsOffsetManager::TopControlsOffsetTag() const {
return offset_tag_modifications_.tags.top_controls_offset_tag;
}
float BrowserControlsOffsetManager::TopControlsMinShownRatio() const {
return TopControlsHeight() ? TopControlsMinHeight() / TopControlsHeight()
: 0.f;
}
float BrowserControlsOffsetManager::BottomControlsHeight() const {
return client_->BottomControlsHeight();
}
float BrowserControlsOffsetManager::BottomControlsMinHeight() const {
return client_->BottomControlsMinHeight();
}
float BrowserControlsOffsetManager::BottomControlsMinShownRatio() const {
return BottomControlsHeight()
? BottomControlsMinHeight() / BottomControlsHeight()
: 0.f;
}
float BrowserControlsOffsetManager::ContentBottomOffset() const {
return BottomControlsHeight() > 0
? BottomControlsShownRatio() * BottomControlsHeight() : 0.0f;
}
float BrowserControlsOffsetManager::BottomControlsShownRatio() const {
return client_->CurrentBottomControlsShownRatio();
}
float BrowserControlsOffsetManager::TopControlsMinHeightOffset() const {
return top_controls_min_height_offset_;
}
float BrowserControlsOffsetManager::BottomControlsMinHeightOffset() const {
return bottom_controls_min_height_offset_;
}
bool BrowserControlsOffsetManager::HasOffsetTag() const {
return BottomControlsOffsetTag() || ContentOffsetTag() ||
TopControlsOffsetTag();
}
std::pair<float, float>
BrowserControlsOffsetManager::TopControlsShownRatioRange() {
if (top_controls_animation_.IsInitialized())
return std::make_pair(top_controls_animation_.min_value(),
top_controls_animation_.max_value());
return std::make_pair(0.f, 1.f);
}
std::pair<float, float>
BrowserControlsOffsetManager::BottomControlsShownRatioRange() {
if (bottom_controls_animation_.IsInitialized())
return std::make_pair(bottom_controls_animation_.min_value(),
bottom_controls_animation_.max_value());
return std::make_pair(0.f, 1.f);
}
void BrowserControlsOffsetManager::UpdateBrowserControlsState(
BrowserControlsState constraints,
BrowserControlsState current,
bool animate,
base::optional_ref<const BrowserControlsOffsetTagModifications>
offset_tag_modifications) {
DCHECK(!(constraints == BrowserControlsState::kShown &&
current == BrowserControlsState::kHidden));
DCHECK(!(constraints == BrowserControlsState::kHidden &&
current == BrowserControlsState::kShown));
TRACE_EVENT2("cc", "BrowserControlsOffsetManager::UpdateBrowserControlsState",
"constraints", static_cast<int>(constraints), "current",
static_cast<int>(current));
// If the constraints have changed we need to inform Blink about it since
// that'll affect main thread scrolling as well as layout.
if (permitted_state_ != constraints) {
constraint_changed_since_commit_ = true;
client_->SetNeedsCommit();
}
permitted_state_ = constraints;
if (offset_tag_modifications.has_value()) {
offset_tag_modifications_ = offset_tag_modifications.value();
}
// Don't do anything if it doesn't matter which state the controls are in.
if (constraints == BrowserControlsState::kBoth &&
current == BrowserControlsState::kBoth)
return;
// Don't do anything if there is no change in offset.
float final_top_shown_ratio = 1.f;
float final_bottom_shown_ratio = 1.f;
AnimationDirection direction = AnimationDirection::kShowingControls;
if (constraints == BrowserControlsState::kHidden ||
current == BrowserControlsState::kHidden) {
final_top_shown_ratio = TopControlsMinShownRatio();
final_bottom_shown_ratio = BottomControlsMinShownRatio();
direction = AnimationDirection::kHidingControls;
}
if (final_top_shown_ratio == TopControlsShownRatio() &&
final_bottom_shown_ratio == BottomControlsShownRatio()) {
TRACE_EVENT_INSTANT0("cc", "Ratios Unchanged", TRACE_EVENT_SCOPE_THREAD);
ResetAnimations();
return;
}
// Don't do anything if the currently running animations end in our desired
// state.
float animated_top_shown_ratio = top_controls_animation_.IsInitialized()
? top_controls_animation_.FinalValue()
: TopControlsShownRatio();
float animated_bottom_shown_ratio =
bottom_controls_animation_.IsInitialized()
? bottom_controls_animation_.FinalValue()
: BottomControlsShownRatio();
if (animated_top_shown_ratio == final_top_shown_ratio &&
animated_bottom_shown_ratio == final_bottom_shown_ratio) {
return;
}
ResetAnimations();
// If we're about to animate the controls in, then restart the animation after
// the scroll completes. We don't know if a scroll is in progress, but that's
// okay; the flag will be reset when a scroll starts next in that case.
if (animate && direction == AnimationDirection::kShowingControls) {
show_controls_when_scroll_completes_ = true;
}
if (animate)
SetupAnimation(direction);
else
client_->SetCurrentBrowserControlsShownRatio(final_top_shown_ratio,
final_bottom_shown_ratio);
}
BrowserControlsState BrowserControlsOffsetManager::PullConstraintForMainThread(
bool* out_changed_since_commit) {
DCHECK(out_changed_since_commit);
*out_changed_since_commit = constraint_changed_since_commit_;
return permitted_state_;
}
void BrowserControlsOffsetManager::NotifyConstraintSyncedToMainThread() {
constraint_changed_since_commit_ = false;
}
void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
bool animate_changes) {
if (old_browser_controls_params_.top_controls_height == TopControlsHeight() &&
old_browser_controls_params_.top_controls_min_height ==
TopControlsMinHeight() &&
old_browser_controls_params_.bottom_controls_height ==
BottomControlsHeight() &&
old_browser_controls_params_.bottom_controls_min_height ==
BottomControlsMinHeight()) {
return;
}
// We continue to update both top and bottom controls even if one has a height
// of 0 so that animations work properly. So here, we should preserve the
// ratios even if the controls height is 0.
float old_top_height = old_browser_controls_params_.top_controls_height;
float new_top_ratio =
TopControlsHeight()
? TopControlsShownRatio() * old_top_height / TopControlsHeight()
: TopControlsShownRatio();
float old_bottom_height = old_browser_controls_params_.bottom_controls_height;
float new_bottom_ratio = BottomControlsHeight()
? BottomControlsShownRatio() *
old_bottom_height / BottomControlsHeight()
: BottomControlsShownRatio();
if (!animate_changes) {
// If the min-heights changed when the controls were at the min-height, the
// shown ratios need to be snapped to the new min-shown-ratio to keep the
// controls at the min height. If the controls were fully shown, we want to
// keep them fully shown even after the heights changed. For any other
// cases, we should update the shown ratio so the visible height remains the
// same.
bool min_height_changed =
TopControlsMinHeight() !=
old_browser_controls_params_.top_controls_min_height;
if (min_height_changed &&
TopControlsShownRatio() == OldTopControlsMinShownRatio()) {
new_top_ratio = TopControlsMinShownRatio();
} else if (TopControlsShownRatio() == 1.f) {
new_top_ratio = 1.f;
}
if (BottomControlsShownRatio() == OldBottomControlsMinShownRatio())
new_bottom_ratio = BottomControlsMinShownRatio();
else if (BottomControlsShownRatio() == 1.f)
new_bottom_ratio = 1.f;
}
// Browser controls height change animations
// If the browser controls heights (and/or min-heights) changed and need to be
// animated, the setup is done here. All the animations done in this class
// involve changing the shown ratios smoothly.
//
// There are several cases to handle:
// 1- The controls shown ratio was at the minimum ratio
// - If the min-height changed, we will run an animation from
// old-min-height / new-total-height to new-min-height / new-total-height
// - If the min-height didn't change, we should update the shown ratio to
// min-height / new-total-height so that the controls keep the same
// visible height and don't jump. No animation needed in this case.
// 2- The controls shown ratio was at the highest ratio (should be 1 here)
// - If the total height changed, we will run an animation from
// old-total-height / new-total-height to 1.
// - If the total height didn't change, we don't need to do anything.
// 3- The controls shown ratio is between the minimum and the maximum.
// - If an animation is running to the old min-height, start a new
// animation to min-height / total-height.
// - Otherwise don't start an animation. We're either animating the
// controls to their expanded state, in which case we can let that
// animation continue, or we're scrolling and no animation is needed.
// Update the shown ratio so the visible height remains the same (see
// new_{top,bottom}_ratio above).
//
// When this method is called as a result of a height change,
// TopControlsHeight(), TopControlsMinHeight(), BottomControlsHeight(), and
// BottomControlsMinHeight() will already be returning the new values.
// However, the shown ratios aren't updated.
bool top_controls_need_animation = animate_changes;
bool bottom_controls_need_animation = animate_changes;
// To handle the case where the min-height changes while we're animating to
// the previous min-height, base our "are we at the minimum shown ratio"
// check on the post-animation ratio if an animation is running, rather than
// its current value.
float effective_top_shown_ratio = TopControlsShownRatio();
if (top_controls_animation_.IsInitialized()) {
effective_top_shown_ratio = top_controls_animation_.FinalValue();
}
float effective_bottom_shown_ratio = BottomControlsShownRatio();
if (bottom_controls_animation_.IsInitialized()) {
effective_bottom_shown_ratio = bottom_controls_animation_.FinalValue();
}
float top_target_ratio;
// We can't animate if we don't have top controls.
if (!TopControlsHeight()) {
top_controls_need_animation = false;
// If the top controls height changed when they were fully shown.
} else if (TopControlsShownRatio() == 1.f &&
TopControlsHeight() != old_top_height) {
top_target_ratio = 1.f; // i.e. new_height / new_height
// If the top controls min-height changed when they were at the minimum
// shown ratio. For example, the min height changed from 0 to a positive
// value while the top controls were completely hidden.
} else if (effective_top_shown_ratio == OldTopControlsMinShownRatio() &&
TopControlsMinHeight() !=
old_browser_controls_params_.top_controls_min_height) {
top_target_ratio = TopControlsMinShownRatio();
} else {
top_controls_need_animation = false;
}
float bottom_target_ratio;
// We can't animate if we don't have bottom controls.
if (!BottomControlsHeight()) {
bottom_controls_need_animation = false;
// If the bottom controls height changed when they were fully shown.
} else if (BottomControlsShownRatio() == 1.f &&
BottomControlsHeight() != old_bottom_height) {
bottom_target_ratio = 1.f; // i.e. new_height / new_height
// If the bottom controls min-height changed when they were at the minimum
// shown ratio.
} else if (effective_bottom_shown_ratio == OldBottomControlsMinShownRatio() &&
BottomControlsMinHeight() !=
old_browser_controls_params_.bottom_controls_min_height) {
bottom_target_ratio = BottomControlsMinShownRatio();
} else {
bottom_controls_need_animation = false;
}
if (top_controls_need_animation) {
InitAnimationForHeightChange(&top_controls_animation_, new_top_ratio,
top_target_ratio);
if (old_browser_controls_params_.top_controls_min_height !=
TopControlsMinHeight()) {
top_controls_min_height_offset_ =
old_browser_controls_params_.top_controls_min_height;
top_min_height_change_in_progress_ = true;
SetTopMinHeightOffsetAnimationRange(top_controls_min_height_offset_,
TopControlsMinHeight());
}
} else {
top_controls_min_height_offset_ = TopControlsMinHeight();
}
if (bottom_controls_need_animation) {
InitAnimationForHeightChange(&bottom_controls_animation_, new_bottom_ratio,
bottom_target_ratio);
if (old_browser_controls_params_.bottom_controls_min_height !=
BottomControlsMinHeight()) {
bottom_controls_min_height_offset_ =
old_browser_controls_params_.bottom_controls_min_height;
int height_delta = BottomControlsHeight() - old_bottom_height;
int min_height_delta =
BottomControlsMinHeight() -
old_browser_controls_params_.bottom_controls_min_height;
// Currently, browser controls animate purely based on the change in the
// height, not on the change in minHeight. This works fine when that
// change is the same, but causes issues if the minHeight has been changed
// by a different value than the height has. This is mitigated by
// "stepping up" or "down" the starting min height offset such that the
// effective change is the same for both the height and minHeight.
if (min_height_delta > height_delta) {
bottom_controls_min_height_offset_ += min_height_delta - height_delta;
}
bottom_min_height_change_in_progress_ = true;
SetBottomMinHeightOffsetAnimationRange(bottom_controls_min_height_offset_,
BottomControlsMinHeight());
}
} else {
bottom_controls_min_height_offset_ = BottomControlsMinHeight();
}
// We won't run any animations if the controls are in an in-between state.
// Examples: a show/hide animation is running, shown ratio is some value
// between min-shown-ratio and 1 because of a scroll event.
UpdateOldBrowserControlsParams();
client_->SetCurrentBrowserControlsShownRatio(new_top_ratio, new_bottom_ratio);
}
void BrowserControlsOffsetManager::ScrollBegin() {
if (pinch_gesture_active_)
return;
// If an animation to show the controls is in progress, re-order the animation
// to start after the scroll completes. This ensures that the user doesn't
// accidentally hide the controls with a gesture that would not normally be
// enough to hide them.
show_controls_when_scroll_completes_ = IsAnimatingToShowControls();
ResetAnimations();
ResetBaseline();
}
gfx::Vector2dF BrowserControlsOffsetManager::ScrollBy(
const gfx::Vector2dF& pending_delta) {
// If one or both of the top/bottom controls are showing, the shown ratio
// needs to be computed.
if (!TopControlsHeight() && !BottomControlsHeight())
return pending_delta;
if (pinch_gesture_active_)
return pending_delta;
if ((permitted_state_ == BrowserControlsState::kShown &&
pending_delta.y() > 0) ||
(permitted_state_ == BrowserControlsState::kHidden &&
pending_delta.y() < 0))
return pending_delta;
// Scroll the page up before expanding the browser controls if
// OnlyExpandTopControlsAtPageTop() returns true.
float viewport_offset_y = client_->ViewportScrollOffset().y();
if (client_->OnlyExpandTopControlsAtPageTop() && pending_delta.y() < 0 &&
viewport_offset_y > 0) {
// Reset the baseline so the controls will immediately begin to scroll
// once we're at the top.
ResetBaseline();
// Only scroll the controls by the amount remaining after the page contents
// have been scrolled to the top.
accumulated_scroll_delta_ =
std::min(0.f, pending_delta.y() + viewport_offset_y);
} else {
accumulated_scroll_delta_ += pending_delta.y();
}
// We want to base our calculations on top or bottom controls. After consuming
// the scroll delta, we will calculate a shown ratio for the controls. The
// top controls have the priority because they need to visually be in sync
// with the web contents.
bool base_on_top_controls = TopControlsHeight();
float old_top_offset = ContentTopOffset();
float baseline_content_offset = base_on_top_controls
? baseline_top_content_offset_
: baseline_bottom_content_offset_;
// The top and bottom controls ratios can be calculated independently.
// However, we want the (normalized) ratios to be equal when scrolling.
// Having normalized ratios in this context means the top and bottom controls
// reach the min and max ratios at the same time when scrolling or during
// the usual show/hide animations, but they can have different shown ratios at
// any time.
float shown_ratio =
(baseline_content_offset - accumulated_scroll_delta_) /
(base_on_top_controls ? TopControlsHeight() : BottomControlsHeight());
float min_ratio = base_on_top_controls ? TopControlsMinShownRatio()
: BottomControlsMinShownRatio();
float normalized_shown_ratio;
if (min_ratio == 1.f) {
normalized_shown_ratio = 1.f;
} else {
normalized_shown_ratio =
(std::clamp(shown_ratio, min_ratio, 1.f) - min_ratio) /
(1.f - min_ratio);
}
// Even though the real shown ratios (shown height / total height) of the top
// and bottom controls can be different, they share the same
// relative/normalized ratio to keep them in sync.
client_->SetCurrentBrowserControlsShownRatio(
TopControlsMinShownRatio() +
normalized_shown_ratio * (1.f - TopControlsMinShownRatio()),
BottomControlsMinShownRatio() +
normalized_shown_ratio * (1.f - BottomControlsMinShownRatio()));
// If the controls are fully visible, treat the current position as the
// new baseline even if the gesture didn't end.
if (TopControlsShownRatio() == 1.f && BottomControlsShownRatio() == 1.f) {
ResetBaseline();
// Once the controls are fully visible, then any cancelled animation to show
// them isn't relevant; the user definitely sees the controls and can decide
// if they'd like to keep them.
show_controls_when_scroll_completes_ = false;
}
ResetAnimations();
// applied_delta will negate any scroll on the content if the top browser
// controls are showing in favor of hiding the controls and resizing the
// content. If the top controls have no height, the content should scroll
// immediately.
gfx::Vector2dF applied_delta(0.f, old_top_offset - ContentTopOffset());
return pending_delta - applied_delta;
}
void BrowserControlsOffsetManager::ScrollEnd() {
if (pinch_gesture_active_)
return;
// See if we should animate the top bar in, in case there was a race between
// chrome showing the controls and the user performing a scroll. We only need
// to animate the top control if it's not fully shown.
if (show_controls_when_scroll_completes_ && TopControlsShownRatio() != 1.f) {
SetupAnimation(AnimationDirection::kShowingControls);
return;
}
StartAnimationIfNecessary();
}
void BrowserControlsOffsetManager::PinchBegin() {
DCHECK(!pinch_gesture_active_);
pinch_gesture_active_ = true;
StartAnimationIfNecessary();
}
void BrowserControlsOffsetManager::PinchEnd() {
DCHECK(pinch_gesture_active_);
// Pinch{Begin,End} will always occur within the scope of Scroll{Begin,End},
// so return to a state expected by the remaining scroll sequence.
pinch_gesture_active_ = false;
ScrollBegin();
}
gfx::Vector2dF BrowserControlsOffsetManager::Animate(
base::TimeTicks monotonic_time) {
if (!HasAnimation() || !client_->HaveRootScrollNode())
return gfx::Vector2dF();
float old_top_offset = ContentTopOffset();
float old_bottom_offset = ContentBottomOffset();
std::optional<float> new_top_ratio =
top_controls_animation_.Tick(monotonic_time);
if (!new_top_ratio.has_value())
new_top_ratio = TopControlsShownRatio();
std::optional<float> new_bottom_ratio =
bottom_controls_animation_.Tick(monotonic_time);
if (!new_bottom_ratio.has_value())
new_bottom_ratio = BottomControlsShownRatio();
client_->SetCurrentBrowserControlsShownRatio(new_top_ratio.value(),
new_bottom_ratio.value());
float top_offset_delta = ContentTopOffset() - old_top_offset;
float bottom_offset_delta = ContentBottomOffset() - old_bottom_offset;
if (top_min_height_change_in_progress_) {
// The change in top offset may be larger than the min-height, resulting in
// too low or too high |top_controls_min_height_offset_| values. So, we
// should clamp it to a valid range.
top_controls_min_height_offset_ =
std::clamp(top_controls_min_height_offset_ + top_offset_delta,
top_min_height_offset_animation_range_->first,
top_min_height_offset_animation_range_->second);
// Ticking the animation might reset it if it's at the final value.
top_min_height_change_in_progress_ =
top_controls_animation_.IsInitialized();
}
if (bottom_min_height_change_in_progress_) {
// The change in bottom offset may be larger than the min-height, resulting
// in too low or too high |bottom_controls_min_height_offset_| values. So,
// we should clamp it to a valid range.
bottom_controls_min_height_offset_ =
std::clamp(bottom_controls_min_height_offset_ + bottom_offset_delta,
bottom_min_height_offset_animation_range_->first,
bottom_min_height_offset_animation_range_->second);
// Ticking the animation might reset it if it's at the final value.
bottom_min_height_change_in_progress_ =
bottom_controls_animation_.IsInitialized();
// When shrinking the bottom controls, there may be a remaining offset
// mistake if the min height was decreased by more than the height was. This
// can be fixed by simply "resetting" the offset to the final minHeight
// value at the end of the animation. This only applies to shrinking
// animations, since this adjustment happens at the beginning for growing
// animations. This is done to avoid the bottom controls "lagging behind"
// the changes to the web content and exposing a blank space right above the
// bottom controls.
if (!bottom_min_height_change_in_progress_) {
bottom_controls_min_height_offset_ = BottomControlsMinHeight();
}
}
gfx::Vector2dF scroll_delta(0.f, top_offset_delta);
return scroll_delta;
}
bool BrowserControlsOffsetManager::HasAnimation() {
return top_controls_animation_.IsInitialized() ||
bottom_controls_animation_.IsInitialized();
}
void BrowserControlsOffsetManager::ResetAnimations() {
// If the animation doesn't need to jump to the end, Animation::Reset() will
// return |std::nullopt|.
std::optional<float> top_ratio = top_controls_animation_.Reset();
std::optional<float> bottom_ratio = bottom_controls_animation_.Reset();
if (top_ratio.has_value() || bottom_ratio.has_value()) {
client_->SetCurrentBrowserControlsShownRatio(
top_ratio.has_value() ? top_ratio.value() : TopControlsShownRatio(),
bottom_ratio.has_value() ? bottom_ratio.value()
: BottomControlsShownRatio());
if (top_min_height_change_in_progress_) {
DCHECK(top_ratio.has_value());
top_controls_min_height_offset_ = TopControlsMinHeight();
}
if (bottom_min_height_change_in_progress_) {
DCHECK(bottom_ratio.has_value());
bottom_controls_min_height_offset_ = BottomControlsMinHeight();
}
}
top_min_height_change_in_progress_ = false;
bottom_min_height_change_in_progress_ = false;
top_min_height_offset_animation_range_.reset();
bottom_min_height_offset_animation_range_.reset();
}
void BrowserControlsOffsetManager::SetupAnimation(
AnimationDirection direction) {
DCHECK_NE(AnimationDirection::kNoAnimation, direction);
DCHECK(direction != AnimationDirection::kHidingControls ||
TopControlsShownRatio() > 0.f || BottomControlsShownRatio() > 0.f);
DCHECK(direction != AnimationDirection::kShowingControls ||
TopControlsShownRatio() < 1.f || BottomControlsShownRatio() < 1.f);
if (top_controls_animation_.IsInitialized() &&
top_controls_animation_.Direction() == direction &&
bottom_controls_animation_.IsInitialized() &&
bottom_controls_animation_.Direction() == direction) {
return;
}
if (!TopControlsHeight() && !BottomControlsHeight()) {
float ratio = direction == AnimationDirection::kHidingControls ? 0.f : 1.f;
client_->SetCurrentBrowserControlsShownRatio(ratio, ratio);
return;
}
// Providing artificially larger/smaller stop ratios to make the animation
// faster if the start ratio is closer to stop ratio.
const float max_stop_ratio =
direction == AnimationDirection::kShowingControls ? 1 : -1;
float top_start_ratio = TopControlsShownRatio();
float top_stop_ratio = top_start_ratio + max_stop_ratio;
top_controls_animation_.Initialize(direction, top_start_ratio, top_stop_ratio,
kShowHideMaxDurationMs,
/*jump_to_end_on_reset=*/false);
top_controls_animation_.SetBounds(TopControlsMinShownRatio(), 1.f);
float bottom_start_ratio = BottomControlsShownRatio();
float bottom_stop_ratio = bottom_start_ratio + max_stop_ratio;
bottom_controls_animation_.Initialize(
direction, bottom_start_ratio, bottom_stop_ratio, kShowHideMaxDurationMs,
/*jump_to_end_on_reset=*/false);
bottom_controls_animation_.SetBounds(BottomControlsMinShownRatio(), 1.f);
client_->DidChangeBrowserControlsPosition();
}
void BrowserControlsOffsetManager::StartAnimationIfNecessary() {
if ((TopControlsShownRatio() == TopControlsMinShownRatio() ||
TopControlsShownRatio() == 1.f) &&
(BottomControlsShownRatio() == BottomControlsMinShownRatio() ||
BottomControlsShownRatio() == 1.f))
return;
float normalized_top_ratio =
(TopControlsShownRatio() - TopControlsMinShownRatio()) /
(1.f - TopControlsMinShownRatio());
if (normalized_top_ratio >= 1.f - controls_hide_threshold_) {
// If we're showing so much that the hide threshold won't trigger, show.
SetupAnimation(AnimationDirection::kShowingControls);
} else if (normalized_top_ratio <= controls_show_threshold_) {
// If we're showing so little that the show threshold won't trigger, hide.
SetupAnimation(AnimationDirection::kHidingControls);
} else {
// If we could be either showing or hiding, we determine which one to
// do based on whether or not the total scroll delta was moving up or
// down.
SetupAnimation(accumulated_scroll_delta_ <= 0.f
? AnimationDirection::kShowingControls
: AnimationDirection::kHidingControls);
}
}
void BrowserControlsOffsetManager::ResetBaseline() {
accumulated_scroll_delta_ = 0.f;
baseline_top_content_offset_ = ContentTopOffset();
baseline_bottom_content_offset_ = ContentBottomOffset();
}
void BrowserControlsOffsetManager::InitAnimationForHeightChange(
Animation* animation,
float start_ratio,
float stop_ratio) {
AnimationDirection direction = start_ratio < stop_ratio
? AnimationDirection::kShowingControls
: AnimationDirection::kHidingControls;
animation->Initialize(direction, start_ratio, stop_ratio,
kHeightChangeDurationMs, /*jump_to_end_on_reset=*/true);
}
float BrowserControlsOffsetManager::OldTopControlsMinShownRatio() {
return old_browser_controls_params_.top_controls_height
? old_browser_controls_params_.top_controls_min_height /
old_browser_controls_params_.top_controls_height
: 0.f;
}
float BrowserControlsOffsetManager::OldBottomControlsMinShownRatio() {
return old_browser_controls_params_.bottom_controls_height
? old_browser_controls_params_.bottom_controls_min_height /
old_browser_controls_params_.bottom_controls_height
: 0.f;
}
void BrowserControlsOffsetManager::UpdateOldBrowserControlsParams() {
// No need to update the other two bool members as they aren't useful for this
// class.
old_browser_controls_params_.top_controls_height = TopControlsHeight();
old_browser_controls_params_.top_controls_min_height = TopControlsMinHeight();
old_browser_controls_params_.bottom_controls_height = BottomControlsHeight();
old_browser_controls_params_.bottom_controls_min_height =
BottomControlsMinHeight();
}
void BrowserControlsOffsetManager::SetTopMinHeightOffsetAnimationRange(
float from,
float to) {
top_min_height_offset_animation_range_ =
std::make_pair(std::min(from, to), std::max(from, to));
}
void BrowserControlsOffsetManager::SetBottomMinHeightOffsetAnimationRange(
float from,
float to) {
bottom_min_height_offset_animation_range_ =
std::make_pair(std::min(from, to), std::max(from, to));
}
double BrowserControlsOffsetManager::PredictViewportBoundsDelta(
double current_bounds_delta,
gfx::Vector2dF scroll_distance) {
double adjustment = current_bounds_delta;
if (scroll_distance.y() > 0 && adjustment > 0) {
// We're scrolling down and started to hide controls. Let's assume they're
// going to be fully hidden by the end of the fling.
if (TopControlsShownRatio() < 1) {
adjustment += ContentTopOffset();
}
if (BottomControlsShownRatio() < 1) {
adjustment += ContentBottomOffset();
}
}
if (scroll_distance.y() < 0 && adjustment < 0) {
// We're scrolling up and started to show controls. Let's assume they're
// going to be fully shown by the end of the fling.
if (TopControlsShownRatio() > 0) {
adjustment -= TopControlsHeight() - ContentTopOffset();
}
if (BottomControlsShownRatio() > 0) {
adjustment -= BottomControlsHeight() - ContentBottomOffset();
}
}
return adjustment;
}
// class Animation
BrowserControlsOffsetManager::Animation::Animation() {}
void BrowserControlsOffsetManager::Animation::Initialize(
AnimationDirection direction,
float start_value,
float stop_value,
int64_t duration,
bool jump_to_end_on_reset) {
direction_ = direction;
start_value_ = start_value;
stop_value_ = stop_value;
duration_ = base::Milliseconds(duration);
initialized_ = true;
jump_to_end_on_reset_ = jump_to_end_on_reset;
SetBounds(std::min(start_value_, stop_value_),
std::max(start_value_, stop_value_));
}
std::optional<float> BrowserControlsOffsetManager::Animation::Tick(
base::TimeTicks monotonic_time) {
if (!IsInitialized())
return std::nullopt;
if (!started_) {
start_time_ = monotonic_time;
stop_time_ = start_time_ + duration_;
started_ = true;
}
float value = gfx::Tween::ClampedFloatValueBetween(
monotonic_time, start_time_, start_value_, stop_time_, stop_value_);
if (IsComplete(value)) {
value = FinalValue();
Reset();
}
return value;
}
void BrowserControlsOffsetManager::Animation::SetBounds(float min, float max) {
min_value_ = min;
max_value_ = max;
}
std::optional<float> BrowserControlsOffsetManager::Animation::Reset() {
auto ret =
jump_to_end_on_reset_
? std::make_optional(std::clamp(stop_value_, min_value_, max_value_))
: std::nullopt;
started_ = false;
initialized_ = false;
start_time_ = base::TimeTicks();
start_value_ = 0.f;
stop_time_ = base::TimeTicks();
stop_value_ = 0.f;
direction_ = AnimationDirection::kNoAnimation;
duration_ = base::TimeDelta();
min_value_ = 0.f;
max_value_ = 1.f;
jump_to_end_on_reset_ = false;
return ret;
}
bool BrowserControlsOffsetManager::Animation::IsComplete(float value) {
return (direction_ == AnimationDirection::kShowingControls &&
(value >= stop_value_ || value >= max_value_)) ||
(direction_ == AnimationDirection::kHidingControls &&
(value <= stop_value_ || value <= min_value_));
}
float BrowserControlsOffsetManager::Animation::FinalValue() {
return std::clamp(stop_value_, min_value_, max_value_);
}
} // namespace cc
|