123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef ASH_SHELF_SCROLLABLE_SHELF_VIEW_H_
- #define ASH_SHELF_SCROLLABLE_SHELF_VIEW_H_
- #include <memory>
- #include "ash/app_list/views/app_list_drag_and_drop_host.h"
- #include "ash/ash_export.h"
- #include "ash/controls/gradient_layer_delegate.h"
- #include "ash/drag_drop/drag_image_view.h"
- #include "ash/public/cpp/shelf_config.h"
- #include "ash/public/cpp/shelf_model.h"
- #include "ash/shelf/scroll_arrow_view.h"
- #include "ash/shelf/shelf.h"
- #include "ash/shelf/shelf_button_delegate.h"
- #include "ash/shelf/shelf_container_view.h"
- #include "ash/shelf/shelf_tooltip_delegate.h"
- #include "ash/shelf/shelf_view.h"
- #include "base/callback_helpers.h"
- #include "base/cancelable_callback.h"
- #include "base/time/time.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #include "ui/compositor/layer_animation_observer.h"
- #include "ui/views/animation/ink_drop_host_view.h"
- #include "ui/views/context_menu_controller.h"
- #include "ui/views/controls/button/button.h"
- #include "ui/views/widget/unique_widget_ptr.h"
- #include "ui/views/widget/widget.h"
- namespace views {
- class FocusSearch;
- }
- namespace ui {
- class PresentationTimeRecorder;
- }
- namespace ash {
- class ASH_EXPORT ScrollableShelfView : public views::AccessiblePaneView,
- public ShelfView::Delegate,
- public ShellObserver,
- public ShelfConfig::Observer,
- public ShelfButtonDelegate,
- public ShelfTooltipDelegate,
- public views::ContextMenuController,
- public ui::ImplicitAnimationObserver {
- public:
- class TestObserver {
- public:
- virtual ~TestObserver() = default;
- virtual void OnPageFlipTimerFired() = 0;
- };
- enum LayoutStrategy {
- // The arrow buttons are not shown. It means that there is enough space to
- // accommodate all of shelf icons.
- kNotShowArrowButtons,
- // Only the left arrow button is shown.
- kShowLeftArrowButton,
- // Only the right arrow button is shown.
- kShowRightArrowButton,
- // Both buttons are shown.
- kShowButtons
- };
- ScrollableShelfView(ShelfModel* model, Shelf* shelf);
- ScrollableShelfView(const ScrollableShelfView&) = delete;
- ScrollableShelfView& operator=(const ScrollableShelfView&) = delete;
- ~ScrollableShelfView() override;
- void Init();
- // Called when the focus ring for ScrollableShelfView is enabled/disabled.
- // |activated| is true when enabling the focus ring.
- void OnFocusRingActivationChanged(bool activated);
- // Scrolls to a new page of shelf icons. |forward| indicates whether the next
- // page or previous page is shown.
- void ScrollToNewPage(bool forward);
- // AccessiblePaneView:
- views::FocusSearch* GetFocusSearch() override;
- views::FocusTraversable* GetFocusTraversableParent() override;
- views::View* GetFocusTraversableParentView() override;
- views::View* GetDefaultFocusableChild() override;
- // Returns the |available_space_|.
- gfx::Rect GetHotseatBackgroundBounds() const;
- // Returns whether the view should adapt to RTL.
- bool ShouldAdaptToRTL() const;
- // Returns whether the scrollable shelf's current size is equal to the target
- // size.
- bool NeedUpdateToTargetBounds() const;
- // Returns the icon's target bounds in screen. The returned bounds are
- // calculated with the hotseat's target bounds instead of the actual bounds.
- // It helps to get the icon's final location before the bounds animation on
- // hotseat ends.
- gfx::Rect GetTargetScreenBoundsOfItemIcon(const ShelfID& id) const;
- // Returns whether scrollable shelf should show app buttons with scrolling
- // when the view size is |target_size| and app button size is |button_size|.
- bool RequiresScrollingForItemSize(const gfx::Size& target_size,
- int button_size) const;
- // Sets padding insets. `padding_insets` should adapt to RTL for the
- // horizontal shelf.
- void SetEdgePaddingInsets(const gfx::Insets& padding_insets);
- // Returns the edge padding insets based on the scrollable shelf view's
- // target bounds or the current bounds, indicated by |use_target_bounds|. Note
- // that the returned value is mirrored for the horizontal shelf under RTL.
- gfx::Insets CalculateMirroredEdgePadding(bool use_target_bounds) const;
- views::View* GetShelfContainerViewForTest();
- bool ShouldAdjustForTest() const;
- void SetTestObserver(TestObserver* test_observer);
- // Returns true if any shelf corner button has ripple ring activated.
- bool IsAnyCornerButtonInkDropActivatedForTest() const;
- // Returns the maximum scroll distance for the current layout.
- float GetScrollUpperBoundForTest() const;
- // Returns whether `page_flip_timer_` is running.
- bool IsPageFlipTimerBusyForTest() const;
- ShelfView* shelf_view() { return shelf_view_; }
- ShelfContainerView* shelf_container_view() { return shelf_container_view_; }
- const ShelfContainerView* shelf_container_view() const {
- return shelf_container_view_;
- }
- ScrollArrowView* left_arrow() { return left_arrow_; }
- const ScrollArrowView* left_arrow() const { return left_arrow_; }
- ScrollArrowView* right_arrow() { return right_arrow_; }
- const ScrollArrowView* right_arrow() const { return right_arrow_; }
- LayoutStrategy layout_strategy_for_test() const { return layout_strategy_; }
- gfx::Vector2dF scroll_offset_for_test() const { return scroll_offset_; }
- absl::optional<size_t> first_tappable_app_index() const {
- return first_tappable_app_index_;
- }
- absl::optional<size_t> last_tappable_app_index() const {
- return last_tappable_app_index_;
- }
- void set_default_last_focusable_child(bool default_last_focusable_child) {
- default_last_focusable_child_ = default_last_focusable_child;
- }
- void set_page_flip_time_threshold(base::TimeDelta page_flip_time_threshold) {
- page_flip_time_threshold_ = page_flip_time_threshold;
- }
- const gfx::Rect& visible_space() const { return visible_space_; }
- const gfx::Insets& edge_padding_insets() const {
- return edge_padding_insets_;
- }
- void set_is_padding_configured_externally(
- bool is_padding_configured_externally) {
- is_padding_configured_externally_ = is_padding_configured_externally;
- }
- private:
- friend class ShelfTestApi;
- class ScrollableShelfArrowView;
- class ScopedActiveInkDropCountImpl;
- enum ScrollStatus {
- // Indicates whether the gesture scrolling is across the main axis.
- // That is, whether it is scrolling vertically for bottom shelf, or
- // whether it is scrolling horizontally for left/right shelf.
- kAcrossMainAxisScroll,
- // Indicates whether the gesture scrolling is along the main axis.
- // That is, whether it is scrolling horizontally for bottom shelf, or
- // whether it is scrolling vertically for left/right shelf.
- kAlongMainAxisScroll,
- // Not in scrolling.
- kNotInScroll
- };
- // Sum of the shelf button size and the gap between shelf buttons.
- int GetSumOfButtonSizeAndSpacing() const;
- // Decides whether the current first visible shelf icon of the scrollable
- // shelf should be hidden or fully shown when gesture scroll ends.
- int GetGestureDragThreshold() const;
- // Returns the maximum scroll distance based on the given space for icons.
- float CalculateScrollUpperBound(int available_space_for_icons) const;
- // Returns the clamped scroll offset.
- float CalculateClampedScrollOffset(float scroll,
- int available_space_for_icons) const;
- // Creates the animation for scrolling shelf by |scroll_distance|.
- void StartShelfScrollAnimation(float scroll_distance);
- // Calculates the layout strategy based on:
- // (1) scroll offset on the main axis.
- // (2) length of the available space to accommodate shelf icons.
- LayoutStrategy CalculateLayoutStrategy(float scroll_distance_on_main_axis,
- int available_length) const;
- Shelf* GetShelf();
- const Shelf* GetShelf() const;
- // views::View:
- gfx::Size CalculatePreferredSize() const override;
- void Layout() override;
- void ChildPreferredSizeChanged(views::View* child) override;
- const char* GetClassName() const override;
- void OnScrollEvent(ui::ScrollEvent* event) override;
- void OnMouseEvent(ui::MouseEvent* event) override;
- void OnGestureEvent(ui::GestureEvent* event) override;
- void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
- void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
- void ViewHierarchyChanged(
- const views::ViewHierarchyChangedDetails& details) override;
- void ScrollRectToVisible(const gfx::Rect& rect) override;
- std::unique_ptr<ui::Layer> RecreateLayer() override;
- // ShelfView::Delegate:
- void ScheduleScrollForItemDragIfNeeded(
- const gfx::Rect& location_in_screen) override;
- void CancelScrollForItemDrag() override;
- bool AreBoundsWithinVisibleSpace(
- const gfx::Rect& bounds_in_screem) const override;
- // ShelfButtonDelegate:
- void OnShelfButtonAboutToRequestFocusFromTabTraversal(ShelfButton* button,
- bool reverse) override;
- void ButtonPressed(views::Button* sender,
- const ui::Event& event,
- views::InkDrop* ink_drop) override;
- void HandleAccessibleActionScrollToMakeVisible(ShelfButton* button) override;
- std::unique_ptr<ScopedActiveInkDropCount> CreateScopedActiveInkDropCount(
- const ShelfButton* sender) override;
- void OnButtonWillBeRemoved() override;
- void OnAppButtonActivated(const ShelfButton* button) override;
- // ContextMenuController:
- void ShowContextMenuForViewImpl(views::View* source,
- const gfx::Point& point,
- ui::MenuSourceType source_type) override;
- // ShellObserver:
- void OnShelfAlignmentChanged(aura::Window* root_window,
- ShelfAlignment old_alignment) override;
- // ShelfConfig::Observer:
- void OnShelfConfigUpdated() override;
- // ShelfTooltipDelegate:
- bool ShouldShowTooltipForView(const views::View* view) const override;
- bool ShouldHideTooltip(const gfx::Point& cursor_location) const override;
- const std::vector<aura::Window*> GetOpenWindowsForView(
- views::View* view) override;
- std::u16string GetTitleForView(const views::View* view) const override;
- views::View* GetViewForEvent(const ui::Event& event) override;
- // ui::ImplicitAnimationObserver:
- void OnImplicitAnimationsCompleted() override;
- // Returns whether the left/right arrow button should show based on the
- // current layout strategy. Because Layout(), where the visibility of arrow
- // buttons is updated, may be called in asynchronous way, we should not use
- // arrow buttons' visibility directly.
- bool ShouldShowLeftArrow() const;
- bool ShouldShowRightArrow() const;
- // Returns the local bounds depending on which view bounds are used: actual
- // view bounds or target view bounds.
- gfx::Rect GetAvailableLocalBounds(bool use_target_bounds) const;
- // Calculates padding for display centering alignment depending on which view
- // bounds are used: actual view bounds or target view bounds. The returned
- // value is mirrored for the horizontal shelf under RTL.
- gfx::Insets CalculateMirroredPaddingForDisplayCentering(
- bool use_target_bounds) const;
- // Returns whether the received gesture event should be handled here.
- bool ShouldHandleGestures(const ui::GestureEvent& event);
- // Resets the attributes related with gesture scroll to their default values.
- void ResetScrollStatus();
- // Handles events for scrolling the shelf. Returns whether the event has been
- // consumed.
- bool ProcessGestureEvent(const ui::GestureEvent& event);
- void HandleMouseWheelEvent(ui::MouseWheelEvent* event);
- // Scrolls the view by distance of |x_offset| or |y_offset|. |animating|
- // indicates whether the animation displays. |x_offset| or |y_offset| has to
- // be float. Otherwise the slow gesture drag is neglected.
- void ScrollByXOffset(float x_offset, bool animating);
- void ScrollByYOffset(float y_offset, bool animating);
- // Scrolls the view to the target offset. |animating| indicates whether the
- // scroll animation is needed. Note that |target_offset| may be an illegal
- // value and may get adjusted in CalculateClampedScrollOffset function.
- void ScrollToMainOffset(float target_offset, bool animating);
- // Calculates the scroll distance to show a new page of shelf icons for
- // the given layout strategy. |forward| indicates whether the next page or
- // previous page is shown.
- float CalculatePageScrollingOffset(bool forward,
- LayoutStrategy layout_strategy) const;
- // Calculates the absolute value of page scroll distance.
- float CalculatePageScrollingOffsetInAbs(LayoutStrategy layout_strategy) const;
- // Calculates the target offset on the main axis after scrolling by
- // |scroll_distance| while the offset before scroll is |start_offset|.
- float CalculateTargetOffsetAfterScroll(float start_offset,
- float scroll_distance) const;
- // Calculates the bounds of the gradient zone before/after the shelf
- // container.
- GradientLayerDelegate::FadeZone CalculateStartGradientZone() const;
- GradientLayerDelegate::FadeZone CalculateEndGradientZone() const;
- // Updates the visibility of gradient zones.
- void UpdateGradientZoneState();
- // Updates the gradient zone if the gradient zone's target bounds are
- // different from the actual values.
- void MaybeUpdateGradientZone();
- void PaintGradientZone(
- const GradientLayerDelegate::FadeZone& start_gradient_zone,
- const GradientLayerDelegate::FadeZone& end_gradient_zone);
- bool ShouldApplyMaskLayerGradientZone() const;
- // Returns the actual scroll offset for the given scroll distance along the
- // main axis under the specific layout strategy. When the left arrow button
- // shows, |shelf_view_| is translated due to the change in
- // |shelf_container_view_|'s bounds. That translation offset is not included
- // in |scroll_offset_|.
- float GetActualScrollOffset(float main_axis_scroll_distance,
- LayoutStrategy layout_strategy) const;
- // Updates |first_tappable_app_index_| and |last_tappable_app_index_|.
- void UpdateTappableIconIndices();
- // Calculates the indices of the first/last tappable app under the given
- // layout strategy and offset along the main axis (that is the x-axis when
- // shelf is horizontally aligned or the y-axis if the shelf is vertically
- // aligned).
- std::pair<absl::optional<size_t>, absl::optional<size_t>>
- CalculateTappableIconIndices(LayoutStrategy layout_strategy,
- int scroll_distance_on_main_axis) const;
- views::View* FindFirstFocusableChild();
- views::View* FindLastFocusableChild();
- // Returns the available space on the main axis for shelf icons.
- int GetSpaceForIcons() const;
- // Returns whether |available_size| is able to accommodate all shelf icons
- // without scrolling. |icons_preferred_size| is the space required by shelf
- // icons.
- bool CanFitAllAppsWithoutScrolling(
- const gfx::Size& available_size,
- const gfx::Size& icons_preferred_size) const;
- // Returns whether scrolling should be handled. |is_gesture_fling| is true
- // when the scrolling is triggered by gesture fling event; when it is false,
- // the scrolling is triggered by touch pad or mouse wheel event.
- bool ShouldHandleScroll(const gfx::Vector2dF& offset,
- bool is_gesture_fling) const;
- // May initiate the scroll animation to ensure that the app icons are shown
- // correctly. Returns whether the animation is created.
- bool AdjustOffset();
- // Returns the offset by which the scroll distance along the main axis should
- // be adjusted to ensure the correct UI under the specific layout strategy.
- // Three parameters are needed: (1) scroll offset on the main axis (2) layout
- // strategy (3) available space for shelf icons.
- float CalculateAdjustmentOffset(int main_axis_scroll_distance,
- LayoutStrategy layout_strategy,
- int available_space_for_icons) const;
- int CalculateScrollDistanceAfterAdjustment(
- int main_axis_scroll_distance,
- LayoutStrategy layout_strategy) const;
- // Updates the available space for child views (such as the arrow button,
- // shelf view) which is smaller than the view's bounds due to paddings.
- void UpdateAvailableSpace();
- // Returns the clip rectangle of |shelf_container_view_| for the given layout
- // strategy. Note that |shelf_container_view_|'s bounds are the same with
- // ScrollableShelfView's. It is why we can use |visible_space_| directly
- // without coordinate transformation.
- gfx::Rect CalculateVisibleSpace(LayoutStrategy layout_strategy) const;
- // Calculates the padding insets which help to show the edging app icon's
- // ripple ring correctly.
- gfx::Insets CalculateRipplePaddingInsets() const;
- // Calculates the rounded corners for |shelf_container_view_|. It contributes
- // to cut off the child view out of the scrollable shelf's bounds, such as
- // ripple ring.
- gfx::RoundedCornersF CalculateShelfContainerRoundedCorners() const;
- // Scrolls to a new page if |drag_icon_| is dragged out of |visible_space_|
- // for enough time. The function is called when |page_flip_timer_| is fired.
- void OnPageFlipTimer();
- // Returns whether a scroll event should be handled by this view or delegated
- // to the shelf.
- bool ShouldDelegateScrollToShelf(const ui::ScrollEvent& event) const;
- // Calculates the scroll distance along the main axis.
- float CalculateMainAxisScrollDistance() const;
- // Updates |scroll_offset_| from |target_offset_| using shelf alignment.
- // |scroll_offset_| may need to update in following cases: (1) View bounds are
- // changed. (2) View is scrolled. (3) A shelf icon is added/removed.
- void UpdateScrollOffset(float target_offset);
- // Updates the available space, which may also trigger the change in scroll
- // offset and layout strategy.
- void UpdateAvailableSpaceAndScroll();
- // Returns the scroll offset assuming view bounds being the target bounds.
- int CalculateScrollOffsetForTargetAvailableSpace(
- const gfx::Rect& target_space) const;
- // Returns whether |sender|'s activated ink drop should be counted.
- bool ShouldCountActivatedInkDrop(const views::View* sender) const;
- // Enable/disable the rounded corners of the shelf container.
- void EnableShelfRoundedCorners(bool enable);
- // Update the number of corner buttons with ripple ring activated. |increase|
- // indicates whether the number increases or decreases.
- void OnActiveInkDropChange(bool increase);
- // Returns whether layer clip should be enabled.
- bool ShouldEnableLayerClip() const;
- // Enable/disable the layer clip on |shelf_container_view_|.
- void EnableLayerClipOnShelfContainerView(bool enable);
- // Calculates the length of space required by shelf icons to show without
- // scroll. Note that the return value includes the padding space between the
- // app icon and the end of scrollable shelf.
- int CalculateShelfIconsPreferredLength() const;
- LayoutStrategy layout_strategy_ = kNotShowArrowButtons;
- // Child views Owned by views hierarchy.
- ScrollArrowView* left_arrow_ = nullptr;
- ScrollArrowView* right_arrow_ = nullptr;
- ShelfContainerView* shelf_container_view_ = nullptr;
- // Available space to accommodate child views. It is mirrored for the
- // horizontal shelf under RTL.
- gfx::Rect available_space_;
- ShelfView* shelf_view_ = nullptr;
- // Defines the padding space inside the scrollable shelf. It is decided by the
- // current padding strategy. Note that `edge_padding_insets_` is mirrored
- // for the horizontal shelf under RTL.
- gfx::Insets edge_padding_insets_;
- // Indicates whether |edge_padding_insets_| is configured externally.
- // Usually |edge_padding_insets_| is calculated by ScrollableShelfView's
- // member function. However, in some animations, |edge_padding_insets_|
- // is set by animation progress to ensure the smooth bounds transition.
- bool is_padding_configured_externally_ = false;
- // Visible space of |shelf_container_view| in ScrollableShelfView's local
- // coordinates. Different from |available_space_|, |visible_space_| only
- // contains app icons and is mirrored for horizontal shelf under RTL.
- gfx::Rect visible_space_;
- gfx::Vector2dF scroll_offset_;
- ScrollStatus scroll_status_ = kNotInScroll;
- // Gesture states are preserved when the gesture scrolling along the main axis
- // (that is, whether it is scrolling horizontally for bottom shelf, or whether
- // it is scrolling horizontally for left/right shelf) gets started. They help
- // to handle the gesture fling event.
- gfx::Vector2dF scroll_offset_before_main_axis_scrolling_;
- LayoutStrategy layout_strategy_before_main_axis_scrolling_ =
- kNotShowArrowButtons;
- std::unique_ptr<GradientLayerDelegate> gradient_layer_delegate_;
- std::unique_ptr<views::FocusSearch> focus_search_;
- // The index of the first/last tappable app index.
- absl::optional<size_t> first_tappable_app_index_ = absl::nullopt;
- absl::optional<size_t> last_tappable_app_index_ = absl::nullopt;
- // The number of corner buttons whose ink drop is activated.
- int activated_corner_buttons_ = 0;
- // Whether this view should focus its last focusable child (instead of its
- // first) when focused.
- bool default_last_focusable_child_ = false;
- // Indicates whether the focus ring on shelf items contained by
- // ScrollableShelfView is enabled.
- bool focus_ring_activated_ = false;
- // Indicates that the view is during the scroll animation.
- bool during_scroll_animation_ = false;
- // Indicates whether the gradient zone before/after the shelf container view
- // should show.
- bool should_show_start_gradient_zone_ = false;
- bool should_show_end_gradient_zone_ = false;
- // Waiting time before flipping the page.
- base::TimeDelta page_flip_time_threshold_;
- TestObserver* test_observer_ = nullptr;
- // If page flip timer is active for shelf item drag, the last known drag item
- // bounds in screen coordinates.
- absl::optional<gfx::Rect> drag_item_bounds_in_screen_;
- base::OneShotTimer page_flip_timer_;
- // Indicates whether the layer clip should be applied to
- // |shelf_container_view_| in non-overflow mode.
- bool layer_clip_in_non_overflow_ = false;
- // Records the presentation time for the scrollable shelf dragging.
- std::unique_ptr<ui::PresentationTimeRecorder> presentation_time_recorder_;
- base::ScopedClosureRunner force_show_hotseat_resetter_;
- };
- } // namespace ash
- #endif // ASH_SHELF_SCROLLABLE_SHELF_VIEW_H_
|