browser_controls_offset_manager.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. // Copyright 2016 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.
  4. #include "cc/input/browser_controls_offset_manager.h"
  5. #include <stdint.h>
  6. #include <algorithm>
  7. #include <utility>
  8. #include "base/check_op.h"
  9. #include "base/cxx17_backports.h"
  10. #include "base/memory/ptr_util.h"
  11. #include "cc/input/browser_controls_offset_manager_client.h"
  12. #include "cc/trees/layer_tree_impl.h"
  13. #include "components/viz/common/frame_sinks/begin_frame_args.h"
  14. #include "ui/gfx/animation/tween.h"
  15. #include "ui/gfx/geometry/transform.h"
  16. #include "ui/gfx/geometry/vector2d_f.h"
  17. namespace cc {
  18. namespace {
  19. // These constants were chosen empirically for their visually pleasant behavior.
  20. // Contact tedchoc@chromium.org for questions about changing these values.
  21. const int64_t kShowHideMaxDurationMs = 200;
  22. // TODO(sinansahin): Temporary value, pending UX guidance probably.
  23. const int64_t kHeightChangeDurationMs = 200;
  24. }
  25. // static
  26. std::unique_ptr<BrowserControlsOffsetManager>
  27. BrowserControlsOffsetManager::Create(BrowserControlsOffsetManagerClient* client,
  28. float controls_show_threshold,
  29. float controls_hide_threshold) {
  30. return base::WrapUnique(new BrowserControlsOffsetManager(
  31. client, controls_show_threshold, controls_hide_threshold));
  32. }
  33. BrowserControlsOffsetManager::BrowserControlsOffsetManager(
  34. BrowserControlsOffsetManagerClient* client,
  35. float controls_show_threshold,
  36. float controls_hide_threshold)
  37. : client_(client),
  38. permitted_state_(BrowserControlsState::kBoth),
  39. accumulated_scroll_delta_(0.f),
  40. baseline_top_content_offset_(0.f),
  41. baseline_bottom_content_offset_(0.f),
  42. controls_show_threshold_(controls_hide_threshold),
  43. controls_hide_threshold_(controls_show_threshold),
  44. pinch_gesture_active_(false),
  45. constraint_changed_since_commit_(false),
  46. top_min_height_change_in_progress_(false),
  47. bottom_min_height_change_in_progress_(false),
  48. top_controls_min_height_offset_(0.f),
  49. bottom_controls_min_height_offset_(0.f) {
  50. CHECK(client_);
  51. UpdateOldBrowserControlsParams();
  52. }
  53. BrowserControlsOffsetManager::~BrowserControlsOffsetManager() = default;
  54. float BrowserControlsOffsetManager::ControlsTopOffset() const {
  55. return ContentTopOffset() - TopControlsHeight();
  56. }
  57. float BrowserControlsOffsetManager::ContentTopOffset() const {
  58. return TopControlsHeight() > 0
  59. ? TopControlsShownRatio() * TopControlsHeight() : 0.0f;
  60. }
  61. float BrowserControlsOffsetManager::TopControlsShownRatio() const {
  62. return client_->CurrentTopControlsShownRatio();
  63. }
  64. float BrowserControlsOffsetManager::TopControlsHeight() const {
  65. return client_->TopControlsHeight();
  66. }
  67. float BrowserControlsOffsetManager::TopControlsMinHeight() const {
  68. return client_->TopControlsMinHeight();
  69. }
  70. float BrowserControlsOffsetManager::TopControlsMinShownRatio() const {
  71. return TopControlsHeight() ? TopControlsMinHeight() / TopControlsHeight()
  72. : 0.f;
  73. }
  74. float BrowserControlsOffsetManager::BottomControlsHeight() const {
  75. return client_->BottomControlsHeight();
  76. }
  77. float BrowserControlsOffsetManager::BottomControlsMinHeight() const {
  78. return client_->BottomControlsMinHeight();
  79. }
  80. float BrowserControlsOffsetManager::BottomControlsMinShownRatio() const {
  81. return BottomControlsHeight()
  82. ? BottomControlsMinHeight() / BottomControlsHeight()
  83. : 0.f;
  84. }
  85. float BrowserControlsOffsetManager::ContentBottomOffset() const {
  86. return BottomControlsHeight() > 0
  87. ? BottomControlsShownRatio() * BottomControlsHeight() : 0.0f;
  88. }
  89. float BrowserControlsOffsetManager::BottomControlsShownRatio() const {
  90. return client_->CurrentBottomControlsShownRatio();
  91. }
  92. float BrowserControlsOffsetManager::TopControlsMinHeightOffset() const {
  93. return top_controls_min_height_offset_;
  94. }
  95. float BrowserControlsOffsetManager::BottomControlsMinHeightOffset() const {
  96. return bottom_controls_min_height_offset_;
  97. }
  98. std::pair<float, float>
  99. BrowserControlsOffsetManager::TopControlsShownRatioRange() {
  100. if (top_controls_animation_.IsInitialized())
  101. return std::make_pair(top_controls_animation_.min_value(),
  102. top_controls_animation_.max_value());
  103. return std::make_pair(0.f, 1.f);
  104. }
  105. std::pair<float, float>
  106. BrowserControlsOffsetManager::BottomControlsShownRatioRange() {
  107. if (bottom_controls_animation_.IsInitialized())
  108. return std::make_pair(bottom_controls_animation_.min_value(),
  109. bottom_controls_animation_.max_value());
  110. return std::make_pair(0.f, 1.f);
  111. }
  112. void BrowserControlsOffsetManager::UpdateBrowserControlsState(
  113. BrowserControlsState constraints,
  114. BrowserControlsState current,
  115. bool animate) {
  116. DCHECK(!(constraints == BrowserControlsState::kShown &&
  117. current == BrowserControlsState::kHidden));
  118. DCHECK(!(constraints == BrowserControlsState::kHidden &&
  119. current == BrowserControlsState::kShown));
  120. TRACE_EVENT2("cc", "BrowserControlsOffsetManager::UpdateBrowserControlsState",
  121. "constraints", static_cast<int>(constraints), "current",
  122. static_cast<int>(current));
  123. // If the constraints have changed we need to inform Blink about it since
  124. // that'll affect main thread scrolling as well as layout.
  125. if (permitted_state_ != constraints) {
  126. constraint_changed_since_commit_ = true;
  127. client_->SetNeedsCommit();
  128. }
  129. permitted_state_ = constraints;
  130. // Don't do anything if it doesn't matter which state the controls are in.
  131. if (constraints == BrowserControlsState::kBoth &&
  132. current == BrowserControlsState::kBoth)
  133. return;
  134. // Don't do anything if there is no change in offset.
  135. float final_top_shown_ratio = 1.f;
  136. float final_bottom_shown_ratio = 1.f;
  137. AnimationDirection direction = AnimationDirection::SHOWING_CONTROLS;
  138. if (constraints == BrowserControlsState::kHidden ||
  139. current == BrowserControlsState::kHidden) {
  140. final_top_shown_ratio = TopControlsMinShownRatio();
  141. final_bottom_shown_ratio = BottomControlsMinShownRatio();
  142. direction = AnimationDirection::HIDING_CONTROLS;
  143. }
  144. if (final_top_shown_ratio == TopControlsShownRatio() &&
  145. final_bottom_shown_ratio == BottomControlsShownRatio()) {
  146. TRACE_EVENT_INSTANT0("cc", "Ratios Unchanged", TRACE_EVENT_SCOPE_THREAD);
  147. ResetAnimations();
  148. return;
  149. }
  150. // Don't do anything if the currently running animations end in our desired
  151. // state.
  152. float animated_top_shown_ratio = top_controls_animation_.IsInitialized()
  153. ? top_controls_animation_.FinalValue()
  154. : TopControlsShownRatio();
  155. float animated_bottom_shown_ratio =
  156. bottom_controls_animation_.IsInitialized()
  157. ? bottom_controls_animation_.FinalValue()
  158. : BottomControlsShownRatio();
  159. if (animated_top_shown_ratio == final_top_shown_ratio &&
  160. animated_bottom_shown_ratio == final_bottom_shown_ratio) {
  161. return;
  162. }
  163. ResetAnimations();
  164. if (animate)
  165. SetupAnimation(direction);
  166. else
  167. client_->SetCurrentBrowserControlsShownRatio(final_top_shown_ratio,
  168. final_bottom_shown_ratio);
  169. }
  170. BrowserControlsState BrowserControlsOffsetManager::PullConstraintForMainThread(
  171. bool* out_changed_since_commit) {
  172. DCHECK(out_changed_since_commit);
  173. *out_changed_since_commit = constraint_changed_since_commit_;
  174. return permitted_state_;
  175. }
  176. void BrowserControlsOffsetManager::NotifyConstraintSyncedToMainThread() {
  177. constraint_changed_since_commit_ = false;
  178. }
  179. void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
  180. bool animate_changes) {
  181. if (old_browser_controls_params_.top_controls_height == TopControlsHeight() &&
  182. old_browser_controls_params_.top_controls_min_height ==
  183. TopControlsMinHeight() &&
  184. old_browser_controls_params_.bottom_controls_height ==
  185. BottomControlsHeight() &&
  186. old_browser_controls_params_.bottom_controls_min_height ==
  187. BottomControlsMinHeight()) {
  188. return;
  189. }
  190. // We continue to update both top and bottom controls even if one has a height
  191. // of 0 so that animations work properly. So here, we should preserve the
  192. // ratios even if the controls height is 0.
  193. float old_top_height = old_browser_controls_params_.top_controls_height;
  194. float new_top_ratio =
  195. TopControlsHeight()
  196. ? TopControlsShownRatio() * old_top_height / TopControlsHeight()
  197. : TopControlsShownRatio();
  198. float old_bottom_height = old_browser_controls_params_.bottom_controls_height;
  199. float new_bottom_ratio = BottomControlsHeight()
  200. ? BottomControlsShownRatio() *
  201. old_bottom_height / BottomControlsHeight()
  202. : BottomControlsShownRatio();
  203. if (!animate_changes) {
  204. // If the min-heights changed when the controls were at the min-height, the
  205. // shown ratios need to be snapped to the new min-shown-ratio to keep the
  206. // controls at the min height. If the controls were fully shown, we want to
  207. // keep them fully shown even after the heights changed. For any other
  208. // cases, we should update the shown ratio so the visible height remains the
  209. // same.
  210. if (TopControlsShownRatio() == OldTopControlsMinShownRatio())
  211. new_top_ratio = TopControlsMinShownRatio();
  212. else if (TopControlsShownRatio() == 1.f)
  213. new_top_ratio = 1.f;
  214. if (BottomControlsShownRatio() == OldBottomControlsMinShownRatio())
  215. new_bottom_ratio = BottomControlsMinShownRatio();
  216. else if (BottomControlsShownRatio() == 1.f)
  217. new_bottom_ratio = 1.f;
  218. }
  219. // Browser controls height change animations
  220. // If the browser controls heights (and/or min-heights) changed and need to be
  221. // animated, the setup is done here. All the animations done in this class
  222. // involve changing the shown ratios smoothly.
  223. //
  224. // There are several cases to handle:
  225. // 1- The controls shown ratio was at the minimum ratio
  226. // - If the min-height changed, we will run an animation from
  227. // old-min-height / new-total-height to new-min-height / new-total-height
  228. // - If the min-height didn't change, we should update the shown ratio to
  229. // min-height / new-total-height so that the controls keep the same
  230. // visible height and don't jump. No animation needed in this case.
  231. // 2- The controls shown ratio was at the highest ratio (should be 1 here)
  232. // - If the total height changed, we will run an animation from
  233. // old-total-height / new-total-height to 1.
  234. // - If the total height didn't change, we don't need to do anything.
  235. // 3- The controls shown ratio is between the minimum and the maximum.
  236. // - If an animation is running to the old min-height, start a new
  237. // animation to min-height / total-height.
  238. // - Otherwise don't start an animation. We're either animating the
  239. // controls to their expanded state, in which case we can let that
  240. // animation continue, or we're scrolling and no animation is needed.
  241. // Update the shown ratio so the visible height remains the same (see
  242. // new_{top,bottom}_ratio above).
  243. //
  244. // When this method is called as a result of a height change,
  245. // TopControlsHeight(), TopControlsMinHeight(), BottomControlsHeight(), and
  246. // BottomControlsMinHeight() will already be returning the new values.
  247. // However, the shown ratios aren't updated.
  248. bool top_controls_need_animation = animate_changes;
  249. bool bottom_controls_need_animation = animate_changes;
  250. // To handle the case where the min-height changes while we're animating to
  251. // the previous min-height, base our "are we at the minimum shown ratio"
  252. // check on the post-animation ratio if an animation is running, rather than
  253. // its current value.
  254. float effective_top_shown_ratio = TopControlsShownRatio();
  255. if (top_controls_animation_.IsInitialized()) {
  256. effective_top_shown_ratio = top_controls_animation_.FinalValue();
  257. }
  258. float effective_bottom_shown_ratio = BottomControlsShownRatio();
  259. if (bottom_controls_animation_.IsInitialized()) {
  260. effective_bottom_shown_ratio = bottom_controls_animation_.FinalValue();
  261. }
  262. float top_target_ratio;
  263. // We can't animate if we don't have top controls.
  264. if (!TopControlsHeight()) {
  265. top_controls_need_animation = false;
  266. // If the top controls height changed when they were fully shown.
  267. } else if (TopControlsShownRatio() == 1.f &&
  268. TopControlsHeight() != old_top_height) {
  269. top_target_ratio = 1.f; // i.e. new_height / new_height
  270. // If the top controls min-height changed when they were at the minimum
  271. // shown ratio. For example, the min height changed from 0 to a positive
  272. // value while the top controls were completely hidden.
  273. } else if (effective_top_shown_ratio == OldTopControlsMinShownRatio() &&
  274. TopControlsMinHeight() !=
  275. old_browser_controls_params_.top_controls_min_height) {
  276. top_target_ratio = TopControlsMinShownRatio();
  277. } else {
  278. top_controls_need_animation = false;
  279. }
  280. float bottom_target_ratio;
  281. // We can't animate if we don't have bottom controls.
  282. if (!BottomControlsHeight()) {
  283. bottom_controls_need_animation = false;
  284. // If the bottom controls height changed when they were fully shown.
  285. } else if (BottomControlsShownRatio() == 1.f &&
  286. BottomControlsHeight() != old_bottom_height) {
  287. bottom_target_ratio = 1.f; // i.e. new_height / new_height
  288. // If the bottom controls min-height changed when they were at the minimum
  289. // shown ratio.
  290. } else if (effective_bottom_shown_ratio == OldBottomControlsMinShownRatio() &&
  291. BottomControlsMinHeight() !=
  292. old_browser_controls_params_.bottom_controls_min_height) {
  293. bottom_target_ratio = BottomControlsMinShownRatio();
  294. } else {
  295. bottom_controls_need_animation = false;
  296. }
  297. if (top_controls_need_animation) {
  298. InitAnimationForHeightChange(&top_controls_animation_, new_top_ratio,
  299. top_target_ratio);
  300. if (old_browser_controls_params_.top_controls_min_height !=
  301. TopControlsMinHeight()) {
  302. top_controls_min_height_offset_ =
  303. old_browser_controls_params_.top_controls_min_height;
  304. top_min_height_change_in_progress_ = true;
  305. SetTopMinHeightOffsetAnimationRange(top_controls_min_height_offset_,
  306. TopControlsMinHeight());
  307. }
  308. } else {
  309. top_controls_min_height_offset_ = TopControlsMinHeight();
  310. }
  311. if (bottom_controls_need_animation) {
  312. InitAnimationForHeightChange(&bottom_controls_animation_, new_bottom_ratio,
  313. bottom_target_ratio);
  314. if (old_browser_controls_params_.bottom_controls_min_height !=
  315. BottomControlsMinHeight()) {
  316. bottom_controls_min_height_offset_ =
  317. old_browser_controls_params_.bottom_controls_min_height;
  318. bottom_min_height_change_in_progress_ = true;
  319. SetBottomMinHeightOffsetAnimationRange(bottom_controls_min_height_offset_,
  320. BottomControlsMinHeight());
  321. }
  322. } else {
  323. bottom_controls_min_height_offset_ = BottomControlsMinHeight();
  324. }
  325. // We won't run any animations if the controls are in an in-between state.
  326. // Examples: a show/hide animation is running, shown ratio is some value
  327. // between min-shown-ratio and 1 because of a scroll event.
  328. UpdateOldBrowserControlsParams();
  329. client_->SetCurrentBrowserControlsShownRatio(new_top_ratio, new_bottom_ratio);
  330. }
  331. void BrowserControlsOffsetManager::ScrollBegin() {
  332. if (pinch_gesture_active_)
  333. return;
  334. ResetAnimations();
  335. ResetBaseline();
  336. }
  337. gfx::Vector2dF BrowserControlsOffsetManager::ScrollBy(
  338. const gfx::Vector2dF& pending_delta) {
  339. // If one or both of the top/bottom controls are showing, the shown ratio
  340. // needs to be computed.
  341. if (!TopControlsHeight() && !BottomControlsHeight())
  342. return pending_delta;
  343. if (pinch_gesture_active_)
  344. return pending_delta;
  345. if ((permitted_state_ == BrowserControlsState::kShown &&
  346. pending_delta.y() > 0) ||
  347. (permitted_state_ == BrowserControlsState::kHidden &&
  348. pending_delta.y() < 0))
  349. return pending_delta;
  350. // Scroll the page up before expanding the browser controls if
  351. // OnlyExpandTopControlsAtPageTop() returns true.
  352. float viewport_offset_y = client_->ViewportScrollOffset().y();
  353. if (client_->OnlyExpandTopControlsAtPageTop() && pending_delta.y() < 0 &&
  354. viewport_offset_y > 0) {
  355. // Reset the baseline so the controls will immediately begin to scroll
  356. // once we're at the top.
  357. ResetBaseline();
  358. // Only scroll the controls by the amount remaining after the page contents
  359. // have been scrolled to the top.
  360. accumulated_scroll_delta_ =
  361. std::min(0.f, pending_delta.y() + viewport_offset_y);
  362. } else {
  363. accumulated_scroll_delta_ += pending_delta.y();
  364. }
  365. // We want to base our calculations on top or bottom controls. After consuming
  366. // the scroll delta, we will calculate a shown ratio for the controls. The
  367. // top controls have the priority because they need to visually be in sync
  368. // with the web contents.
  369. bool base_on_top_controls = TopControlsHeight();
  370. float old_top_offset = ContentTopOffset();
  371. float baseline_content_offset = base_on_top_controls
  372. ? baseline_top_content_offset_
  373. : baseline_bottom_content_offset_;
  374. // The top and bottom controls ratios can be calculated independently.
  375. // However, we want the (normalized) ratios to be equal when scrolling.
  376. // Having normalized ratios in this context means the top and bottom controls
  377. // reach the min and max ratios at the same time when scrolling or during
  378. // the usual show/hide animations, but they can have different shown ratios at
  379. // any time.
  380. float shown_ratio =
  381. (baseline_content_offset - accumulated_scroll_delta_) /
  382. (base_on_top_controls ? TopControlsHeight() : BottomControlsHeight());
  383. float min_ratio = base_on_top_controls ? TopControlsMinShownRatio()
  384. : BottomControlsMinShownRatio();
  385. float normalized_shown_ratio =
  386. (base::clamp(shown_ratio, min_ratio, 1.f) - min_ratio) /
  387. (1.f - min_ratio);
  388. // Even though the real shown ratios (shown height / total height) of the top
  389. // and bottom controls can be different, they share the same
  390. // relative/normalized ratio to keep them in sync.
  391. client_->SetCurrentBrowserControlsShownRatio(
  392. TopControlsMinShownRatio() +
  393. normalized_shown_ratio * (1.f - TopControlsMinShownRatio()),
  394. BottomControlsMinShownRatio() +
  395. normalized_shown_ratio * (1.f - BottomControlsMinShownRatio()));
  396. // If the controls are fully visible, treat the current position as the
  397. // new baseline even if the gesture didn't end.
  398. if (TopControlsShownRatio() == 1.f && BottomControlsShownRatio() == 1.f)
  399. ResetBaseline();
  400. ResetAnimations();
  401. // applied_delta will negate any scroll on the content if the top browser
  402. // controls are showing in favor of hiding the controls and resizing the
  403. // content. If the top controls have no height, the content should scroll
  404. // immediately.
  405. gfx::Vector2dF applied_delta(0.f, old_top_offset - ContentTopOffset());
  406. return pending_delta - applied_delta;
  407. }
  408. void BrowserControlsOffsetManager::ScrollEnd() {
  409. if (pinch_gesture_active_)
  410. return;
  411. StartAnimationIfNecessary();
  412. }
  413. void BrowserControlsOffsetManager::PinchBegin() {
  414. DCHECK(!pinch_gesture_active_);
  415. pinch_gesture_active_ = true;
  416. StartAnimationIfNecessary();
  417. }
  418. void BrowserControlsOffsetManager::PinchEnd() {
  419. DCHECK(pinch_gesture_active_);
  420. // Pinch{Begin,End} will always occur within the scope of Scroll{Begin,End},
  421. // so return to a state expected by the remaining scroll sequence.
  422. pinch_gesture_active_ = false;
  423. ScrollBegin();
  424. }
  425. gfx::Vector2dF BrowserControlsOffsetManager::Animate(
  426. base::TimeTicks monotonic_time) {
  427. if (!HasAnimation() || !client_->HaveRootScrollNode())
  428. return gfx::Vector2dF();
  429. float old_top_offset = ContentTopOffset();
  430. float old_bottom_offset = ContentBottomOffset();
  431. absl::optional<float> new_top_ratio =
  432. top_controls_animation_.Tick(monotonic_time);
  433. if (!new_top_ratio.has_value())
  434. new_top_ratio = TopControlsShownRatio();
  435. absl::optional<float> new_bottom_ratio =
  436. bottom_controls_animation_.Tick(monotonic_time);
  437. if (!new_bottom_ratio.has_value())
  438. new_bottom_ratio = BottomControlsShownRatio();
  439. client_->SetCurrentBrowserControlsShownRatio(new_top_ratio.value(),
  440. new_bottom_ratio.value());
  441. float top_offset_delta = ContentTopOffset() - old_top_offset;
  442. float bottom_offset_delta = ContentBottomOffset() - old_bottom_offset;
  443. if (top_min_height_change_in_progress_) {
  444. // The change in top offset may be larger than the min-height, resulting in
  445. // too low or too high |top_controls_min_height_offset_| values. So, we
  446. // should clamp it to a valid range.
  447. top_controls_min_height_offset_ =
  448. base::clamp(top_controls_min_height_offset_ + top_offset_delta,
  449. top_min_height_offset_animation_range_->first,
  450. top_min_height_offset_animation_range_->second);
  451. // Ticking the animation might reset it if it's at the final value.
  452. top_min_height_change_in_progress_ =
  453. top_controls_animation_.IsInitialized();
  454. }
  455. if (bottom_min_height_change_in_progress_) {
  456. // The change in bottom offset may be larger than the min-height, resulting
  457. // in too low or too high |bottom_controls_min_height_offset_| values. So,
  458. // we should clamp it to a valid range.
  459. bottom_controls_min_height_offset_ =
  460. base::clamp(bottom_controls_min_height_offset_ + bottom_offset_delta,
  461. bottom_min_height_offset_animation_range_->first,
  462. bottom_min_height_offset_animation_range_->second);
  463. // Ticking the animation might reset it if it's at the final value.
  464. bottom_min_height_change_in_progress_ =
  465. bottom_controls_animation_.IsInitialized();
  466. }
  467. gfx::Vector2dF scroll_delta(0.f, top_offset_delta);
  468. return scroll_delta;
  469. }
  470. bool BrowserControlsOffsetManager::HasAnimation() {
  471. return top_controls_animation_.IsInitialized() ||
  472. bottom_controls_animation_.IsInitialized();
  473. }
  474. void BrowserControlsOffsetManager::ResetAnimations() {
  475. // If the animation doesn't need to jump to the end, Animation::Reset() will
  476. // return |absl::nullopt|.
  477. absl::optional<float> top_ratio = top_controls_animation_.Reset();
  478. absl::optional<float> bottom_ratio = bottom_controls_animation_.Reset();
  479. if (top_ratio.has_value() || bottom_ratio.has_value()) {
  480. client_->SetCurrentBrowserControlsShownRatio(
  481. top_ratio.has_value() ? top_ratio.value() : TopControlsShownRatio(),
  482. bottom_ratio.has_value() ? bottom_ratio.value()
  483. : BottomControlsShownRatio());
  484. if (top_min_height_change_in_progress_) {
  485. DCHECK(top_ratio.has_value());
  486. top_controls_min_height_offset_ = TopControlsMinHeight();
  487. }
  488. if (bottom_min_height_change_in_progress_) {
  489. DCHECK(bottom_ratio.has_value());
  490. bottom_controls_min_height_offset_ = BottomControlsMinHeight();
  491. }
  492. }
  493. top_min_height_change_in_progress_ = false;
  494. bottom_min_height_change_in_progress_ = false;
  495. top_min_height_offset_animation_range_.reset();
  496. bottom_min_height_offset_animation_range_.reset();
  497. }
  498. void BrowserControlsOffsetManager::SetupAnimation(
  499. AnimationDirection direction) {
  500. DCHECK_NE(AnimationDirection::NO_ANIMATION, direction);
  501. DCHECK(direction != AnimationDirection::HIDING_CONTROLS ||
  502. TopControlsShownRatio() > 0.f);
  503. DCHECK(direction != AnimationDirection::SHOWING_CONTROLS ||
  504. TopControlsShownRatio() < 1.f);
  505. if (top_controls_animation_.IsInitialized() &&
  506. top_controls_animation_.Direction() == direction &&
  507. bottom_controls_animation_.IsInitialized() &&
  508. bottom_controls_animation_.Direction() == direction) {
  509. return;
  510. }
  511. if (!TopControlsHeight() && !BottomControlsHeight()) {
  512. float ratio = direction == AnimationDirection::HIDING_CONTROLS ? 0.f : 1.f;
  513. client_->SetCurrentBrowserControlsShownRatio(ratio, ratio);
  514. return;
  515. }
  516. // Providing artificially larger/smaller stop ratios to make the animation
  517. // faster if the start ratio is closer to stop ratio.
  518. const float max_stop_ratio =
  519. direction == AnimationDirection::SHOWING_CONTROLS ? 1 : -1;
  520. float top_start_ratio = TopControlsShownRatio();
  521. float top_stop_ratio = top_start_ratio + max_stop_ratio;
  522. top_controls_animation_.Initialize(direction, top_start_ratio, top_stop_ratio,
  523. kShowHideMaxDurationMs,
  524. /*jump_to_end_on_reset=*/false);
  525. top_controls_animation_.SetBounds(TopControlsMinShownRatio(), 1.f);
  526. float bottom_start_ratio = BottomControlsShownRatio();
  527. float bottom_stop_ratio = bottom_start_ratio + max_stop_ratio;
  528. bottom_controls_animation_.Initialize(
  529. direction, bottom_start_ratio, bottom_stop_ratio, kShowHideMaxDurationMs,
  530. /*jump_to_end_on_reset=*/false);
  531. bottom_controls_animation_.SetBounds(BottomControlsMinShownRatio(), 1.f);
  532. client_->DidChangeBrowserControlsPosition();
  533. }
  534. void BrowserControlsOffsetManager::StartAnimationIfNecessary() {
  535. if ((TopControlsShownRatio() == TopControlsMinShownRatio() ||
  536. TopControlsShownRatio() == 1.f) &&
  537. (BottomControlsShownRatio() == BottomControlsMinShownRatio() ||
  538. BottomControlsShownRatio() == 1.f))
  539. return;
  540. float normalized_top_ratio =
  541. (TopControlsShownRatio() - TopControlsMinShownRatio()) /
  542. (1.f - TopControlsMinShownRatio());
  543. if (normalized_top_ratio >= 1.f - controls_hide_threshold_) {
  544. // If we're showing so much that the hide threshold won't trigger, show.
  545. SetupAnimation(AnimationDirection::SHOWING_CONTROLS);
  546. } else if (normalized_top_ratio <= controls_show_threshold_) {
  547. // If we're showing so little that the show threshold won't trigger, hide.
  548. SetupAnimation(AnimationDirection::HIDING_CONTROLS);
  549. } else {
  550. // If we could be either showing or hiding, we determine which one to
  551. // do based on whether or not the total scroll delta was moving up or
  552. // down.
  553. SetupAnimation(accumulated_scroll_delta_ <= 0.f
  554. ? AnimationDirection::SHOWING_CONTROLS
  555. : AnimationDirection::HIDING_CONTROLS);
  556. }
  557. }
  558. void BrowserControlsOffsetManager::ResetBaseline() {
  559. accumulated_scroll_delta_ = 0.f;
  560. baseline_top_content_offset_ = ContentTopOffset();
  561. baseline_bottom_content_offset_ = ContentBottomOffset();
  562. }
  563. void BrowserControlsOffsetManager::InitAnimationForHeightChange(
  564. Animation* animation,
  565. float start_ratio,
  566. float stop_ratio) {
  567. AnimationDirection direction = start_ratio < stop_ratio
  568. ? AnimationDirection::SHOWING_CONTROLS
  569. : AnimationDirection::HIDING_CONTROLS;
  570. animation->Initialize(direction, start_ratio, stop_ratio,
  571. kHeightChangeDurationMs, /*jump_to_end_on_reset=*/true);
  572. }
  573. float BrowserControlsOffsetManager::OldTopControlsMinShownRatio() {
  574. return old_browser_controls_params_.top_controls_height
  575. ? old_browser_controls_params_.top_controls_min_height /
  576. old_browser_controls_params_.top_controls_height
  577. : 0.f;
  578. }
  579. float BrowserControlsOffsetManager::OldBottomControlsMinShownRatio() {
  580. return old_browser_controls_params_.bottom_controls_height
  581. ? old_browser_controls_params_.bottom_controls_min_height /
  582. old_browser_controls_params_.bottom_controls_height
  583. : 0.f;
  584. }
  585. void BrowserControlsOffsetManager::UpdateOldBrowserControlsParams() {
  586. // No need to update the other two bool members as they aren't useful for this
  587. // class.
  588. old_browser_controls_params_.top_controls_height = TopControlsHeight();
  589. old_browser_controls_params_.top_controls_min_height = TopControlsMinHeight();
  590. old_browser_controls_params_.bottom_controls_height = BottomControlsHeight();
  591. old_browser_controls_params_.bottom_controls_min_height =
  592. BottomControlsMinHeight();
  593. }
  594. void BrowserControlsOffsetManager::SetTopMinHeightOffsetAnimationRange(
  595. float from,
  596. float to) {
  597. top_min_height_offset_animation_range_ =
  598. std::make_pair(std::min(from, to), std::max(from, to));
  599. }
  600. void BrowserControlsOffsetManager::SetBottomMinHeightOffsetAnimationRange(
  601. float from,
  602. float to) {
  603. bottom_min_height_offset_animation_range_ =
  604. std::make_pair(std::min(from, to), std::max(from, to));
  605. }
  606. // class Animation
  607. BrowserControlsOffsetManager::Animation::Animation() {}
  608. void BrowserControlsOffsetManager::Animation::Initialize(
  609. AnimationDirection direction,
  610. float start_value,
  611. float stop_value,
  612. int64_t duration,
  613. bool jump_to_end_on_reset) {
  614. direction_ = direction;
  615. start_value_ = start_value;
  616. stop_value_ = stop_value;
  617. duration_ = base::Milliseconds(duration);
  618. initialized_ = true;
  619. jump_to_end_on_reset_ = jump_to_end_on_reset;
  620. SetBounds(std::min(start_value_, stop_value_),
  621. std::max(start_value_, stop_value_));
  622. }
  623. absl::optional<float> BrowserControlsOffsetManager::Animation::Tick(
  624. base::TimeTicks monotonic_time) {
  625. if (!IsInitialized())
  626. return absl::nullopt;
  627. if (!started_) {
  628. start_time_ = monotonic_time;
  629. stop_time_ = start_time_ + duration_;
  630. started_ = true;
  631. }
  632. float value = gfx::Tween::ClampedFloatValueBetween(
  633. monotonic_time, start_time_, start_value_, stop_time_, stop_value_);
  634. if (IsComplete(value)) {
  635. value = FinalValue();
  636. Reset();
  637. }
  638. return value;
  639. }
  640. void BrowserControlsOffsetManager::Animation::SetBounds(float min, float max) {
  641. min_value_ = min;
  642. max_value_ = max;
  643. }
  644. absl::optional<float> BrowserControlsOffsetManager::Animation::Reset() {
  645. auto ret = jump_to_end_on_reset_ ? absl::make_optional(base::clamp(
  646. stop_value_, min_value_, max_value_))
  647. : absl::nullopt;
  648. started_ = false;
  649. initialized_ = false;
  650. start_time_ = base::TimeTicks();
  651. start_value_ = 0.f;
  652. stop_time_ = base::TimeTicks();
  653. stop_value_ = 0.f;
  654. direction_ = AnimationDirection::NO_ANIMATION;
  655. duration_ = base::TimeDelta();
  656. min_value_ = 0.f;
  657. max_value_ = 1.f;
  658. jump_to_end_on_reset_ = false;
  659. return ret;
  660. }
  661. bool BrowserControlsOffsetManager::Animation::IsComplete(float value) {
  662. return (direction_ == AnimationDirection::SHOWING_CONTROLS &&
  663. (value >= stop_value_ || value >= max_value_)) ||
  664. (direction_ == AnimationDirection::HIDING_CONTROLS &&
  665. (value <= stop_value_ || value <= min_value_));
  666. }
  667. float BrowserControlsOffsetManager::Animation::FinalValue() {
  668. return base::clamp(stop_value_, min_value_, max_value_);
  669. }
  670. } // namespace cc