// Copyright 2019 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CC_INPUT_SCROLLBAR_CONTROLLER_H_ #define CC_INPUT_SCROLLBAR_CONTROLLER_H_ #include <memory> #include <optional> #include "base/cancelable_callback.h" #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" #include "base/time/time.h" #include "cc/cc_export.h" #include "cc/input/input_handler.h" #include "cc/input/scrollbar.h" #include "cc/layers/layer_impl.h" #include "cc/layers/painted_scrollbar_layer_impl.h" // High level documentation: // https://source.chromium.org/chromium/chromium/src/+/main:cc/input/README.md // Click scrolling. // - A click is considered as a kMouseDown and a kMouseUp in quick succession. // Every click on a composited non-custom arrow leads to 3 GestureEvents in // total. // - GSB and GSU on get queued in the CTEQ on mousedown and a GSE on mouseup. // - The delta scrolled is constant at 40px (scaled by the device_scale_factor) // for scrollbar arrows and a function of the viewport length in the case of // track autoscroll. // Thumb dragging. // - The sequence of events in the CTEQ would be something like GSB, GSU, GSU, // GSU..., GSE // - On every pointermove, the scroll delta is determined is as current pointer // position - the point at which we got the initial mousedown. // - The delta is then scaled by the scroller to scrollbar ratio so that // dragging the thumb moves the scroller proportionately. // - This ratio is calculated as: // (scroll_layer_length - viewport_length) / // (scrollbar_track_length - scrollbar_thumb_length) // - On pointerup, the GSE clears state as mentioned above. // VSync aligned autoscroll. // - Autoscroll is implemented as a "scroll animation" which has a linear timing // function (see cc::LinearTimingFunction) and a curve with a constant velocity. // - The main thread does autoscrolling by pumping events at 50ms interval. To // have a similar kind of behaviour on the compositor thread, the autoscroll // velocity is set to 800px per second for scrollbar arrows. // - For track autoscrolling however, the velocity is a function of the viewport // length. // - Based on this velocity, an autoscroll curve is created. // - An autoscroll animation is set up. (via // LayerTreeHostImpl::ScrollAnimationCreateInternal) on the the known // scroll_node and the scroller starts animation when the pointer is held. // Nuances: // Thumb snapping. // - During a thumb drag, if a pointer moves too far away from the scrollbar // track, the thumb is supposed to snap back to it original place (i.e to the // point before the thumb drag started). // - This is done by having an imaginary no_snap_rect around the scrollbar // track. This extends about 8 times the width of the track on either side. When // a manipulation is in progress, the mouse is expected to stay within the // bounds of this rect. Assuming a standard scrollbar, 17px wide, this is how // it'd look like. // https://github.com/rahul8805/CompositorThreadedScrollbarDocs/blob/master/snap.PNG?raw=true // - When a pointerdown is received, record the original offset of the thumb. // - On every pointermove, check if the pointer is within the bounds of the // no_snap_rect. If false, snap to the initial_scroll_offset and stop processing // pointermove(s) until the pointer reenters the bounds of the rect. // - The moment the mouse re-enters the bounds of the no_snap_rect, we snap to // the initial_scroll_offset + event.PositionInWidget. // Thumb anchoring. // - During a thumb drag, if the pointer runs off the track, there should be no // additional scrolling until the pointer reenters the track and crosses the // original mousedown point. // - This is done by sending "clamped" deltas. The amount of scrollable delta is // computed using LayerTreeHostImpl::ComputeScrollDelta. // - Since the deltas are clamped, overscroll doesn't occur if it can't be // consumed by the CurrentlyScrollingNode. // Autoscroll play/pause. // - When the pointer moves in and out of bounds of a scrollbar part that can // initiate autoscrolls (like arrows or track), the autoscroll animation is // expected to play or pause accordingly. // - On every ScrollbarController::WillBeginMainFrame, the pointer location is // constantly checked and if it is outside the bounds of the scrollbar part that // initiated the autoscroll, the autoscroll is stopped. // - Similarly, when the pointer reenters the bounds, autoscroll is restarted // again. All the vital information during autoscrolling such the velocity, // direction, scroll layer length etc is held in // cc::ScrollbarController::AutoscrollState. // Shift + click. // - Doing a shift click on any part of a scrollbar track is supposed to do an // instant scroll to that location (such that the thumb is still centered on the // pointer). // - When the MouseEvent reaches the // InputHandlerProxy::RouteToTypeSpecificHandler, if the event is found to have // a "Shift" modifier, the ScrollbarController calculates the offset based on // the pointers current location on the track. // - Once the offset is determined, the InputHandlerProxy creates a GSU with // state that tells the LayerTreeHostImpl to perform a non-animated scroll to // the offset. // Continuous autoscrolling. // - This builds on top of the autoscolling implementation. "Continuous" // autoscrolling is when an autoscroll is in progress and the size of the // content keeps increasing. For eg: When you keep the down arrow pressed on // websites like Facebook, the autoscrolling is expected to keep on going until // the mouse is released. // - This is implemented by monitoring the length of the scroller layer at every // frame and if the length increases (and if autoscroll in the forward direction // is already in progress), the old animation is aborted and a new autoscroll // animation with the new scroller length is kicked off. namespace cc { class LayerTreeHostImpl; // This class is responsible for hit testing composited scrollbars, event // handling and creating gesture scroll deltas. class CC_EXPORT ScrollbarController { … }; } // namespace cc #endif // CC_INPUT_SCROLLBAR_CONTROLLER_H_