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

ash / style / pill_button.h [blame]

// Copyright 2021 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_STYLE_PILL_BUTTON_H_
#define ASH_STYLE_PILL_BUTTON_H_

#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/color/color_id.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/metadata/view_factory.h"

namespace ash {

class BlurredBackgroundShield;

// A label button with a rounded rectangle background. It can have an icon
// inside as well, and its text and background colors will be different based on
// the type of the button.
class ASH_EXPORT PillButton : public views::LabelButton {
  METADATA_HEADER(PillButton, views::LabelButton)

 public:
  using ColorVariant = absl::variant<SkColor, ui::ColorId>;

  static constexpr int kPillButtonHorizontalSpacing = 16;
  static constexpr int kPaddingReductionForIcon = 4;

  // The PillButton style is defined with 4 features:
  // 1. Color variant defines which background, text, and icon color scheme to
  // be used, for example Default, Floating, Alert, etc.
  // 2. Button size indicates whether we should use the default size 32 or a
  // large size 36.
  // 3. With/without an icon.
  // 4. Icon position: leading or following.
  // For ease of extracting features from a button type, each feature is
  // represented by a different bit mask.
  using TypeFlag = int;

  static constexpr TypeFlag kDefault = 1;
  static constexpr TypeFlag kDefaultElevated = 1 << 1;
  static constexpr TypeFlag kPrimary = 1 << 2;
  static constexpr TypeFlag kSecondary = 1 << 3;
  static constexpr TypeFlag kFloating = 1 << 4;
  static constexpr TypeFlag kAlert = 1 << 5;
  // TODO(crbug.com/1355517): Get rid of `kAccent` after CrosNext is fully
  // launched.
  static constexpr TypeFlag kAccent = 1 << 6;
  static constexpr TypeFlag kLarge = 1 << 7;
  static constexpr TypeFlag kIconLeading = 1 << 8;
  static constexpr TypeFlag kIconFollowing = 1 << 9;

  // Types of the PillButton. Each type is represented as the bitwise OR
  // operation of the feature bit masks. The naming rule of the button type is
  // k{Color Variant}{Button Size}{Icon}{Icon Position}.
  enum Type {
    // PillButton with default text and background colors, a leading icon.
    kDefaultWithIconLeading = kDefault | kIconLeading,
    // PillButton with default text and background colors, a following icon.
    kDefaultWithIconFollowing = kDefault | kIconFollowing,
    // PillButton with default text and background colors, a large button size,
    // a leading icon.
    kDefaultLargeWithIconLeading = kDefault | kLarge | kIconLeading,
    // PillButton with default text and background colors, a large button size,
    // a following icon.
    kDefaultLargeWithIconFollowing = kDefault | kLarge | kIconFollowing,
    // PillButton with default text and background colors, no icon.
    kDefaultWithoutIcon = kDefault,
    // PillButton with default text and background colors, a large button size,
    // no icon.
    kDefaultLargeWithoutIcon = kDefault | kLarge,

    // PillButton with default-elevated text and background colors, a leading
    // icon.
    kDefaultElevatedWithIconLeading = kDefaultElevated | kIconLeading,
    // PillButton with default-elevated text and background colors, a following
    // icon.
    kDefaultElevatedWithIconFollowing = kDefaultElevated | kIconFollowing,
    // PillButton with default-elevated text and background colors, a large
    // button size, a leading icon.
    kDefaultElevatedLargeWithIconLeading =
        kDefaultElevated | kLarge | kIconLeading,
    // PillButton with default-elevated text and background colors, a large
    // button size, a following icon.
    kDefaultElevatedLargeWithIconFollowing =
        kDefaultElevated | kLarge | kIconFollowing,
    // PillButton with default-elevated text and background colors, no icon.
    kDefaultElevatedWithoutIcon = kDefaultElevated,
    // PillButton with default-elevated text and background colors, a large
    // button size,
    // no icon.
    kDefaultElevatedLargeWithoutIcon = kDefaultElevated | kLarge,

    // PillButton with primary text and background colors, a leading icon.
    kPrimaryWithIconLeading = kPrimary | kIconLeading,
    // PillButton with primary text and background colors, a following icon.
    kPrimaryWithIconFollowing = kPrimary | kIconFollowing,
    // PillButton with primary text and background colors, a large button size,
    // a leading icon.
    kPrimaryLargeWithIconLeading = kPrimary | kLarge | kIconLeading,
    // PillButton with primary text and background colors, a large button size,
    // a following icon.
    kPrimaryLargeWithIconFollowing = kPrimary | kLarge | kIconFollowing,
    // PillButton with primary text and background colors, no icon.
    kPrimaryWithoutIcon = kPrimary,
    // PillButton with primary text and background colors, a large button size,
    // no icon.
    kPrimaryLargeWithoutIcon = kPrimary | kLarge,

    // PillButton with secondary text and background colors, a leading icon.
    kSecondaryWithIconLeading = kSecondary | kIconLeading,
    // PillButton with secondary text and background colors, a following icon.
    kSecondaryWithIconFollowing = kSecondary | kIconFollowing,
    // PillButton with secondary text and background colors, a large button
    // size, a leading icon.
    kSecondaryLargeWithIconLeading = kSecondary | kLarge | kIconLeading,
    // PillButton with secondary text and background colors, a large button
    // size, a following icon.
    kSecondaryLargeWithIconFollowing = kSecondary | kLarge | kIconFollowing,
    // PillButton with secondary text and background colors, no icon.
    kSecondaryWithoutIcon = kSecondary,
    // PillButton with secondary text and background colors, a large button
    // size, no icon.
    kSecondaryLargeWithoutIcon = kSecondary | kLarge,

    // PillButton with floating text colors, no background, a leading icon.
    kFloatingWithIconLeading = kFloating | kIconLeading,
    // PillButton with floating text colors, no background, a following icon.
    kFloatingWithIconFollowing = kFloating | kIconFollowing,
    // PillButton with floating text colors, no background, a large button size,
    // a leading icon.
    kFloatingLargeWithIconLeading = kFloating | kLarge | kIconLeading,
    // PillButton with floating text colors, no background, a large button size,
    // a following icon.
    kFloatingLargeWithIconFollowing = kFloating | kLarge | kIconFollowing,
    // PillButton with floating text colors, no background, no icon.
    kFloatingWithoutIcon = kFloating,
    // PillButton with floating text colors, no background, a large button size,
    // no icon.
    kFloatingLargeWithoutIcon = kFloating | kLarge,

    // PillButton with alert text and background colors, a leading icon.
    kAlertWithIconLeading = kAlert | kIconLeading,
    // PillButton with alert text and background colors, a following icon.
    kAlertWithIconFollowing = kAlert | kIconFollowing,
    // PillButton with alert text and background colors, a large button size, a
    // leading icon.
    kAlertLargeWithIconLeading = kAlert | kLarge | kIconLeading,
    // PillButton with alert text and background colors, a large button size, a
    // following icon.
    kAlertLargeWithIconFollowing = kAlert | kLarge | kIconFollowing,
    // PillButton with alert text and background colors, no icon.
    kAlertWithoutIcon = kAlert,
    // PillButton with alert text and background colors, a large button size, no
    // icon.
    kAlertLargeWithoutIcon = kAlert | kLarge,

    // Old button types.
    // TODO(crbug.com/1355517): Get rid of these types after CrosNext is fully
    // launched.
    // PillButton with accent text and background colors, no icon.
    kAccentWithoutIcon = kAccent,
    // PillButton with accent text, no background, no icon.
    kAccentFloatingWithoutIcon = kAccent | kFloating,
  };

  explicit PillButton(
      PressedCallback callback = PressedCallback(),
      const std::u16string& text = std::u16string(),
      Type type = Type::kDefaultWithoutIcon,
      const gfx::VectorIcon* icon = nullptr,
      int horizontal_spacing = kPillButtonHorizontalSpacing,
      int padding_reduction_for_icon = kPaddingReductionForIcon);
  PillButton(const PillButton&) = delete;
  PillButton& operator=(const PillButton&) = delete;
  ~PillButton() override;

  // views::LabelButton:
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;
  gfx::Insets GetInsets() const override;
  void UpdateBackgroundColor() override;
  views::PropertyEffects UpdateStyleToIndicateDefaultStatus() override;
  void SetText(const std::u16string& text) override;
  void OnSetTooltipText(const std::u16string& tooltip_text) override;

  // Sets the button's background color, text's color or icon's color. Note, do
  // this only when the button wants to have different colors from the default
  // ones.
  void SetBackgroundColor(const SkColor background_color);
  void SetBackgroundColorId(ui::ColorId background_color_id);
  void SetButtonTextColor(const SkColor text_color);
  void SetButtonTextColorId(ui::ColorId text_color_id);
  void SetIconColor(const SkColor icon_color);
  void SetIconColorId(ui::ColorId icon_color_id);
  // TODO(b/290639214): This method is deprecating. Try not to change button
  // type afterward. If a new button type is needed, please create a new
  // instance.
  void SetPillButtonType(Type type);

  // Sets the button's label to use the default label font, which is smaller
  // and less heavily weighted.
  void SetUseDefaultLabelFont();

  // Sets if the button should enable the background blur. Once the button
  // enables the background blur, it will use `BlurredBackgroundShield` as the
  // background which is performance consuming so only use it as needed.
  void SetEnableBackgroundBlur(bool enable);

  void SetTextWithStringId(int message_id);
  void SetUseLabelAsDefaultTooltip(bool use_label_as_default_tooltip);

 private:
  // Initializes the button layout, focus ring and background according to the
  // button type.
  void Init();

  void UpdateTextColor();
  void UpdateIconColor();

  // Returns the spacing on the side where the icon locates. The value is set
  // smaller to make the spacing on two sides visually look the same.
  int GetHorizontalSpacingWithIcon() const;

  void UpdateTooltipText();

  Type type_;
  const raw_ptr<const gfx::VectorIcon> icon_;

  // Horizontal spacing of this button. `kPillButtonHorizontalSpacing` will be
  // set as the default value.
  int horizontal_spacing_;

  // The padding reduced by icon.
  int padding_reduction_for_icon_;

  // Custom colors and color IDs.
  ColorVariant background_color_ = gfx::kPlaceholderColor;
  ColorVariant text_color_ = gfx::kPlaceholderColor;
  ColorVariant icon_color_ = gfx::kPlaceholderColor;

  bool enable_background_blur_ = false;
  std::unique_ptr<BlurredBackgroundShield> blurred_background_;

  // Indicates if we are going to use the label contents for tooltip as default.
  bool use_label_as_default_tooltip_ = true;

  // When the tooltip becomes equal to Label's Text, this variable holds the
  // original value of the tooltip text if the Label's Text was not used.
  std::u16string original_tooltip_text_;

  // Called to update background color when the button is enabled/disabled.
  base::CallbackListSubscription enabled_changed_subscription_;
};

BEGIN_VIEW_BUILDER(ASH_EXPORT, PillButton, views::LabelButton)
VIEW_BUILDER_PROPERTY(const SkColor, BackgroundColor)
VIEW_BUILDER_PROPERTY(ui::ColorId, BackgroundColorId)
VIEW_BUILDER_PROPERTY(const SkColor, ButtonTextColor)
VIEW_BUILDER_PROPERTY(ui::ColorId, ButtonTextColorId)
VIEW_BUILDER_PROPERTY(const SkColor, IconColor)
VIEW_BUILDER_PROPERTY(ui::ColorId, IconColorId)
VIEW_BUILDER_PROPERTY(PillButton::Type, PillButtonType)
VIEW_BUILDER_PROPERTY(bool, EnableBackgroundBlur)
VIEW_BUILDER_PROPERTY(int, TextWithStringId)
VIEW_BUILDER_PROPERTY(bool, UseLabelAsDefaultTooltip)
END_VIEW_BUILDER

}  // namespace ash

DEFINE_VIEW_BUILDER(ASH_EXPORT, ash::PillButton)

#endif  // ASH_STYLE_PILL_BUTTON_H_