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

ash / shelf / home_button.h [blame]

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SHELF_HOME_BUTTON_H_
#define ASH_SHELF_HOME_BUTTON_H_

#include <memory>

#include "ash/app_list/app_list_metrics.h"
#include "ash/app_list/app_list_model_provider.h"
#include "ash/app_list/quick_app_access_model.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/app_list/app_list_controller_observer.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/shelf/home_button_controller.h"
#include "ash/shelf/shelf_button_delegate.h"
#include "ash/shelf/shelf_control_button.h"
#include "ash/shell_observer.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/events/devices/input_device_event_observer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/views/view_targeter_delegate.h"

namespace views {
class AnimationBuilder;
class CircleLayerDelegate;
class ImageButton;
class Label;
}  // namespace views

namespace ui {
class LayerOwner;
}

namespace ash {

class Shelf;
class ShelfButtonDelegate;
class ShelfNavigationWidget;
class Shell;

// Button used for the AppList icon on the shelf. It opens the app list (in
// clamshell mode) or home screen (in tablet mode). Because the clamshell-mode
// app list appears like a dismissable overlay, the button is highlighted while
// the app list is open in clamshell mode.
//
// If Assistant is enabled, the button is filled in; long-pressing it will
// launch Assistant.
class ASH_EXPORT HomeButton : public ShelfControlButton,
                              public ShelfButtonDelegate,
                              public views::ViewTargeterDelegate,
                              public ShellObserver,
                              public ShelfConfig::Observer,
                              public AppListModelProvider::Observer,
                              public QuickAppAccessModel::Observer,
                              public ui::InputDeviceEventObserver {
  METADATA_HEADER(HomeButton, ShelfControlButton)

 public:
  class ScopedNoClipRect {
   public:
    explicit ScopedNoClipRect(ShelfNavigationWidget* shelf_navigation_widget);
    ScopedNoClipRect(const ScopedNoClipRect&) = delete;
    ScopedNoClipRect& operator=(const ScopedNoClipRect&) = delete;
    ~ScopedNoClipRect();

   private:
    const raw_ptr<ShelfNavigationWidget> shelf_navigation_widget_;
    const gfx::Rect clip_rect_;
  };

  // An observer that can be used to track the nudge animation state. Currently
  // used in testing.
  class NudgeAnimationObserver : public base::CheckedObserver {
   public:
    NudgeAnimationObserver() = default;
    NudgeAnimationObserver(const NudgeAnimationObserver&) = delete;
    NudgeAnimationObserver& operator=(const NudgeAnimationObserver&) = delete;
    ~NudgeAnimationObserver() override = default;

    // Called when the nudge animation is started/ended.
    virtual void NudgeAnimationStarted(HomeButton* home_button) = 0;
    virtual void NudgeAnimationEnded(HomeButton* home_button) = 0;

    // Called when the nudge label is animated to fully shown.
    virtual void NudgeLabelShown(HomeButton* home_button) = 0;
  };

  explicit HomeButton(Shelf* shelf);

  HomeButton(const HomeButton&) = delete;
  HomeButton& operator=(const HomeButton&) = delete;

  ~HomeButton() override;

  // views::View:
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;
  void Layout(PassKey) override;
  void AddedToWidget() override;

  // views::Button:
  void OnGestureEvent(ui::GestureEvent* event) override;
  std::u16string GetTooltipText(const gfx::Point& p) const override;

  // ShelfButtonDelegate:
  void OnShelfButtonAboutToRequestFocusFromTabTraversal(ShelfButton* button,
                                                        bool reverse) override;
  void ButtonPressed(views::Button* sender,
                     const ui::Event& event,
                     views::InkDrop* ink_drop) override;

  // ShelfConfig::Observer:
  void OnShelfConfigUpdated() override;

  // ui::InputDeviceEventObserver:
  void OnInputDeviceConfigurationChanged(uint8_t input_device_types) override;
  void OnDeviceListsComplete() override;

  // Called when the availability of a long-press gesture may have changed, e.g.
  // when Assistant becomes enabled.
  void OnAssistantAvailabilityChanged();

  // True if the app list is shown for the display containing this button.
  bool IsShowingAppList() const;

  // Called when a locale change is detected. Updates the button tooltip and
  // accessible name.
  void HandleLocaleChange();

  // Returns the display which contains this view.
  int64_t GetDisplayId() const;

  // Clip rect of this view's widget will be removed during the life time of the
  // returned ScopedNoClipRect.
  [[nodiscard]] std::unique_ptr<ScopedNoClipRect> CreateScopedNoClipRect();

  // Checks if the `nudge_label_` can be shown for the launcher nudge.
  // NOTE: This must be called after `CreateNudgeLabel()`, where the
  // `nudge_label_` is created. This is because whether the nudge can be shown
  // depends on nudge_label_'s preferred size.
  bool CanShowNudgeLabel() const;

  // Starts the launcher nudge animation.
  void StartNudgeAnimation();

  // Sets the button's "toggled" state - the button is toggled when the bubble
  // launcher is shown.
  void SetToggled(bool toggled);

  void AddNudgeAnimationObserverForTest(NudgeAnimationObserver* observer);
  void RemoveNudgeAnimationObserverForTest(NudgeAnimationObserver* observer);

  views::View* expandable_container_for_test() const {
    return expandable_container_;
  }

  views::Label* nudge_label_for_test() const { return nudge_label_; }

  views::ImageButton* quick_app_button_for_test() const {
    return quick_app_button_;
  }

  void UpdateTooltipText();

 protected:
  // views::Button:
  void OnThemeChanged() override;

 private:
  class ButtonImageView;

  // Creates `nudge_label_` for launcher nudge.
  void CreateNudgeLabel();

  // Creates the `expandable_container_` which holds either the `nudge_label_`
  // or the `quick_app_button_`.
  void CreateExpandableContainer();

  // Creates the `quick_app_button_` to be shown next to the home button.
  void CreateQuickAppButton();

  // Called when the quick app button is pressed.
  void QuickAppButtonPressed();

  // Animation functions for launcher nudge.
  void AnimateNudgeRipple(views::AnimationBuilder& builder);
  void AnimateNudgeBounce(views::AnimationBuilder& builder);
  void AnimateNudgeLabelSlideIn(views::AnimationBuilder& builder);
  void AnimateNudgeLabelSlideOut();
  void AnimateNudgeLabelFadeOut();

  // Callbacks for the nudge animation.
  void OnNudgeAnimationStarted();
  void OnNudgeAnimationEnded();
  void OnLabelSlideInAnimationEnded();
  void OnLabelFadeOutAnimationEnded();

  // Removes the nudge label from the view hierarchy.
  void RemoveNudgeLabel();

  // Removes the quick app button from the view hierarchy.
  void RemoveQuickAppButton();

  // views::ViewTargeterDelegate:
  bool DoesIntersectRect(const views::View* target,
                         const gfx::Rect& rect) const override;

  // ShellObserver:
  void OnShellDestroying() override;

  // AppListModelProvider::Observer:
  void OnActiveAppListModelsChanged(AppListModel* model,
                                    SearchModel* search_model) override;

  // QuickAppAccessModel::Observer:
  void OnQuickAppShouldShowChanged(bool quick_app_shown) override;
  void OnQuickAppIconChanged() override;

  // Create and animate in the quick app button from behind the home button.
  void AnimateQuickAppButtonIn();

  // Animate out the quick app button, deleting the quick app button when
  // completed.
  void AnimateQuickAppButtonOut();

  // Callback for the quick app button slide out animation.
  void OnQuickAppButtonSlideOutDone();

  // Returns a transform which will translate the child of the
  // `expandable_container` to be placed behind the home button.
  gfx::Transform GetTransformForContainerChildBehindHomeButton();

  // Returns a clip rect which will clip the `expandable_container` to the
  // bounds of the home button.
  gfx::Rect GetExpandableContainerClipRectToHomeButton();

  base::ScopedObservation<QuickAppAccessModel, QuickAppAccessModel::Observer>
      quick_app_model_observation_{this};

  base::ScopedObservation<Shell, ShellObserver> shell_observation_{this};

  base::ScopedObservation<AppListModelProvider, AppListModelProvider::Observer>
      app_list_model_observation_{this};

  const raw_ptr<Shelf> shelf_;

  // The view that paints the home button content. In its own view to ensure
  // the background is stacked above `expandable_container_`.
  raw_ptr<ButtonImageView> button_image_view_ = nullptr;

  // The container of `nudge_label_` or `quick_app_button_`. This is also
  // responsible for painting the background of the contents. This container can
  // expand visually by animation.
  raw_ptr<views::View> expandable_container_ = nullptr;

  // The app button which is shown next to the home button. Only shown when
  // set by SetQuickApp().
  raw_ptr<views::ImageButton> quick_app_button_ = nullptr;

  // The controller used to determine the button's behavior.
  HomeButtonController controller_;

  // The delegate used by |nudge_ripple_layer_|. Only exists during the
  // nudge animation.
  std::unique_ptr<views::CircleLayerDelegate> ripple_layer_delegate_;

  // The ripple layer in the launcher nudge animation. Only exists during the
  // nudge animation.
  ui::LayerOwner nudge_ripple_layer_;

  // The label view and for launcher nudge animation.
  raw_ptr<views::Label> nudge_label_ = nullptr;

  // The timer that counts down to hide the nudge_label_ from showing state.
  base::OneShotTimer label_nudge_timer_;

  std::unique_ptr<ScopedNoClipRect> scoped_no_clip_rect_;

  base::ObserverList<NudgeAnimationObserver> observers_;

  base::WeakPtrFactory<HomeButton> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_SHELF_HOME_BUTTON_H_