pill_button.cc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // Copyright 2021 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 "ash/style/pill_button.h"
  5. #include "ash/constants/ash_features.h"
  6. #include "ash/public/cpp/style/scoped_light_mode_as_default.h"
  7. #include "ash/style/ash_color_provider.h"
  8. #include "ash/style/style_util.h"
  9. #include "ui/base/metadata/metadata_impl_macros.h"
  10. #include "ui/compositor/layer.h"
  11. #include "ui/gfx/paint_vector_icon.h"
  12. #include "ui/views/background.h"
  13. #include "ui/views/controls/focus_ring.h"
  14. #include "ui/views/controls/highlight_path_generator.h"
  15. namespace ash {
  16. namespace {
  17. // The height of default size button, mainly used for button types other than
  18. // kIconLarge.
  19. constexpr int kPillButtonHeight = 32;
  20. // The height of large size button, used for button type kIconLarge.
  21. constexpr int kPillButtonLargeHeight = 36;
  22. constexpr int kPillButtonMinimumWidth = 56;
  23. constexpr int kIconSize = 20;
  24. constexpr int kIconPillButtonImageLabelSpacingDp = 8;
  25. constexpr int kPaddingReductionForIcon = 4;
  26. // Including the thickness and inset of the focus ring in order to keep 2px
  27. // padding between the focus ring and content of the button.
  28. constexpr int kFocusRingPadding = 2 + views::FocusRing::kDefaultHaloThickness +
  29. views::FocusRing::kDefaultHaloInset;
  30. // Returns true it is a floating type of PillButton, which is a type of
  31. // PillButton without a background.
  32. bool IsFloatingPillButton(PillButton::Type type) {
  33. return type == PillButton::Type::kIconlessFloating ||
  34. type == PillButton::Type::kIconlessAccentFloating;
  35. }
  36. // Returns true if the button has an icon.
  37. bool IsIconPillButton(PillButton::Type type) {
  38. return type == PillButton::Type::kIcon ||
  39. type == PillButton::Type::kIconLarge;
  40. }
  41. // Returns the button height according to the given type.
  42. int GetButtonHeight(PillButton::Type type) {
  43. return type == PillButton::Type::kIconLarge ? kPillButtonLargeHeight
  44. : kPillButtonHeight;
  45. }
  46. SkColor GetDefaultBackgroundColor(PillButton::Type type) {
  47. AshColorProvider::ControlsLayerType color_id =
  48. AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive;
  49. switch (type) {
  50. case PillButton::Type::kIcon:
  51. case PillButton::Type::kIconLarge:
  52. case PillButton::Type::kIconless:
  53. case PillButton::Type::kIconlessAccent:
  54. break;
  55. case PillButton::Type::kIconlessAlert:
  56. color_id =
  57. AshColorProvider::ControlsLayerType::kControlBackgroundColorAlert;
  58. break;
  59. case PillButton::Type::kIconlessProminent:
  60. color_id =
  61. AshColorProvider::ControlsLayerType::kControlBackgroundColorActive;
  62. break;
  63. case PillButton::Type::kIconlessFloating:
  64. case PillButton::Type::kIconlessAccentFloating:
  65. return SK_ColorTRANSPARENT;
  66. }
  67. return AshColorProvider::Get()->GetControlsLayerColor(color_id);
  68. }
  69. SkColor GetDefaultButtonTextColor(PillButton::Type type) {
  70. AshColorProvider::ContentLayerType color_id =
  71. AshColorProvider::ContentLayerType::kButtonLabelColor;
  72. switch (type) {
  73. case PillButton::Type::kIcon:
  74. case PillButton::Type::kIconLarge:
  75. case PillButton::Type::kIconless:
  76. case PillButton::Type::kIconlessFloating:
  77. break;
  78. case PillButton::Type::kIconlessAlert:
  79. case PillButton::Type::kIconlessProminent:
  80. color_id = AshColorProvider::ContentLayerType::kButtonLabelColorPrimary;
  81. break;
  82. case PillButton::Type::kIconlessAccent:
  83. case PillButton::Type::kIconlessAccentFloating:
  84. color_id = AshColorProvider::ContentLayerType::kButtonLabelColorBlue;
  85. break;
  86. }
  87. return AshColorProvider::Get()->GetContentLayerColor(color_id);
  88. }
  89. } // namespace
  90. PillButton::PillButton(PressedCallback callback,
  91. const std::u16string& text,
  92. PillButton::Type type,
  93. const gfx::VectorIcon* icon,
  94. int horizontal_spacing,
  95. bool use_light_colors,
  96. bool rounded_highlight_path)
  97. : views::LabelButton(std::move(callback), text),
  98. type_(type),
  99. icon_(icon),
  100. use_light_colors_(use_light_colors),
  101. horizontal_spacing_(horizontal_spacing),
  102. rounded_highlight_path_(rounded_highlight_path) {
  103. SetPaintToLayer();
  104. layer()->SetFillsBoundsOpaquely(false);
  105. SetHorizontalAlignment(gfx::ALIGN_CENTER);
  106. InitializeButtonLayout();
  107. label()->SetSubpixelRenderingEnabled(false);
  108. // TODO: Unify the font size, weight under ash/style as well.
  109. label()->SetFontList(views::Label::GetDefaultFontList().Derive(
  110. 1, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
  111. StyleUtil::SetUpInkDropForButton(
  112. this, gfx::Insets(),
  113. /*highlight_on_hover=*/false,
  114. /*highlight_on_focus=*/false,
  115. /*background_color=*/
  116. use_light_colors ? SK_ColorWHITE : gfx::kPlaceholderColor);
  117. views::FocusRing::Get(this)->SetColorId(
  118. (use_light_colors_ && !features::IsDarkLightModeEnabled())
  119. ? ui::kColorAshLightFocusRing
  120. : ui::kColorAshFocusRing);
  121. SetTooltipText(text);
  122. }
  123. PillButton::~PillButton() = default;
  124. gfx::Size PillButton::CalculatePreferredSize() const {
  125. int button_width = label()->GetPreferredSize().width();
  126. if (IsIconPillButton(type_)) {
  127. // Add the padding on two sides.
  128. button_width += horizontal_spacing_ + GetHorizontalSpacingWithIcon();
  129. // Add the icon width and the spacing between the icon and the text.
  130. button_width += kIconSize + kIconPillButtonImageLabelSpacingDp;
  131. } else {
  132. button_width += 2 * horizontal_spacing_;
  133. }
  134. const int height = GetButtonHeight(type_);
  135. gfx::Size size(button_width, height);
  136. size.SetToMax(gfx::Size(kPillButtonMinimumWidth, height));
  137. return size;
  138. }
  139. int PillButton::GetHeightForWidth(int width) const {
  140. return GetButtonHeight(type_);
  141. }
  142. void PillButton::OnThemeChanged() {
  143. views::LabelButton::OnThemeChanged();
  144. auto* color_provider = AshColorProvider::Get();
  145. SkColor enabled_icon_color =
  146. icon_color_.value_or(color_provider->GetContentLayerColor(
  147. AshColorProvider::ContentLayerType::kButtonIconColor));
  148. SkColor enabled_text_color =
  149. text_color_.value_or(GetDefaultButtonTextColor(type_));
  150. SkColor background_color =
  151. background_color_.value_or(GetDefaultBackgroundColor(type_));
  152. if (background())
  153. background()->SetNativeControlColor(background_color);
  154. // Override the colors to light mode if `use_light_colors_` is true when D/L
  155. // is not enabled.
  156. if (use_light_colors_ && !features::IsDarkLightModeEnabled()) {
  157. ScopedLightModeAsDefault scoped_light_mode_as_default;
  158. enabled_icon_color =
  159. icon_color_.value_or(color_provider->GetContentLayerColor(
  160. AshColorProvider::ContentLayerType::kButtonIconColor));
  161. enabled_text_color = text_color_.value_or(GetDefaultButtonTextColor(type_));
  162. background_color =
  163. background_color_.value_or(GetDefaultBackgroundColor(type_));
  164. if (background())
  165. background()->SetNativeControlColor(background_color);
  166. }
  167. if (IsIconPillButton(type_)) {
  168. DCHECK(icon_);
  169. SetImage(views::Button::STATE_NORMAL,
  170. gfx::CreateVectorIcon(*icon_, kIconSize, enabled_icon_color));
  171. SetImage(views::Button::STATE_DISABLED,
  172. gfx::CreateVectorIcon(
  173. *icon_, kIconSize,
  174. AshColorProvider::GetDisabledColor(enabled_icon_color)));
  175. SetImageLabelSpacing(kIconPillButtonImageLabelSpacingDp);
  176. }
  177. SetEnabledTextColors(enabled_text_color);
  178. SetTextColor(views::Button::STATE_DISABLED,
  179. AshColorProvider::GetDisabledColor(enabled_text_color));
  180. }
  181. void PillButton::SetBackgroundColor(const SkColor background_color) {
  182. if (background_color_ == background_color)
  183. return;
  184. background_color_ = background_color;
  185. DCHECK(background());
  186. background()->SetNativeControlColor(background_color_.value());
  187. }
  188. void PillButton::SetButtonTextColor(const SkColor text_color) {
  189. if (text_color_ == text_color)
  190. return;
  191. text_color_ = text_color;
  192. OnThemeChanged();
  193. }
  194. void PillButton::SetIconColor(const SkColor icon_color) {
  195. if (icon_color_ == icon_color)
  196. return;
  197. icon_color_ = icon_color;
  198. OnThemeChanged();
  199. }
  200. void PillButton::SetPillButtonType(Type type) {
  201. if (type_ == type)
  202. return;
  203. type_ = type;
  204. OnThemeChanged();
  205. }
  206. void PillButton::SetUseDefaultLabelFont() {
  207. label()->SetFontList(views::Label::GetDefaultFontList());
  208. }
  209. void PillButton::InitializeButtonLayout() {
  210. const int height = GetButtonHeight(type_);
  211. const int vertical_spacing =
  212. std::max((height - GetPreferredSize().height()) / 2, 0);
  213. const int left_padding = IsIconPillButton(type_)
  214. ? GetHorizontalSpacingWithIcon()
  215. : horizontal_spacing_;
  216. SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
  217. vertical_spacing, left_padding, vertical_spacing, horizontal_spacing_)));
  218. if (rounded_highlight_path_) {
  219. if (type_ == Type::kIconlessProminent) {
  220. views::InstallRoundRectHighlightPathGenerator(
  221. this, gfx::Insets(-kFocusRingPadding),
  222. height / 2.f + kFocusRingPadding);
  223. } else {
  224. views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
  225. height / 2.f);
  226. }
  227. }
  228. if (!IsFloatingPillButton(type_)) {
  229. SetBackground(views::CreateRoundedRectBackground(
  230. GetDefaultBackgroundColor(type_), height / 2.f));
  231. }
  232. PreferredSizeChanged();
  233. }
  234. int PillButton::GetHorizontalSpacingWithIcon() const {
  235. return std::max(horizontal_spacing_ - kPaddingReductionForIcon, 0);
  236. }
  237. BEGIN_METADATA(PillButton, views::LabelButton)
  238. END_METADATA
  239. } // namespace ash