1 // Copyright (c) 2012 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 5 #ifndef UI_VIEWS_CONTROLS_BUTTON_LABEL_BUTTON_H_ 6 #define UI_VIEWS_CONTROLS_BUTTON_LABEL_BUTTON_H_ 7 8 #include <array> 9 #include <memory> 10 11 #include "base/bind.h" 12 #include "base/compiler_specific.h" 13 #include "base/gtest_prod_util.h" 14 #include "base/macros.h" 15 #include "base/optional.h" 16 #include "third_party/skia/include/core/SkColor.h" 17 #include "ui/gfx/image/image_skia.h" 18 #include "ui/views/controls/button/button.h" 19 #include "ui/views/controls/button/label_button_label.h" 20 #include "ui/views/controls/image_view.h" 21 #include "ui/views/controls/label.h" 22 #include "ui/views/layout/layout_provider.h" 23 #include "ui/views/metadata/view_factory.h" 24 #include "ui/views/native_theme_delegate.h" 25 #include "ui/views/style/typography.h" 26 #include "ui/views/widget/widget.h" 27 28 namespace views { 29 30 class InkDropContainerView; 31 class LabelButtonBorder; 32 33 // LabelButton is a button with text and an icon. 34 class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate { 35 public: 36 METADATA_HEADER(LabelButton); 37 38 // Creates a LabelButton with pressed events sent to |callback| and label 39 // |text|. |button_context| is a value from views::style::TextContext and 40 // determines the appearance of |text|. 41 explicit LabelButton(PressedCallback callback = PressedCallback(), 42 const base::string16& text = base::string16(), 43 int button_context = style::CONTEXT_BUTTON); 44 LabelButton(const LabelButton&) = delete; 45 LabelButton& operator=(const LabelButton&) = delete; 46 ~LabelButton() override; 47 48 // Gets or sets the image shown for the specified button state. 49 // GetImage returns the image for STATE_NORMAL if the state's image is empty. 50 virtual gfx::ImageSkia GetImage(ButtonState for_state) const; 51 // TODO(http://crbug.com/1100034) prefer SetImageModel over SetImage(). 52 void SetImage(ButtonState for_state, const gfx::ImageSkia& image); 53 void SetImageModel(ButtonState for_state, const ui::ImageModel& image_model); 54 55 // Gets or sets the text shown on the button. 56 const base::string16& GetText() const; 57 virtual void SetText(const base::string16& text); 58 59 // Makes the button report its preferred size without the label. This lets 60 // AnimatingLayoutManager gradually shrink the button until the text is 61 // invisible, at which point the text gets cleared. Think of this as 62 // transitioning from the current text to SetText(""). 63 // Note that the layout manager (or manual SetBounds calls) need to be 64 // configured to eventually hit the the button's preferred size (or smaller) 65 // or the text will never be cleared. 66 void ShrinkDownThenClearText(); 67 68 // Sets the text color shown for the specified button |for_state| to |color|. 69 void SetTextColor(ButtonState for_state, SkColor color); 70 71 // Sets the text colors shown for the non-disabled states to |color|. 72 virtual void SetEnabledTextColors(base::Optional<SkColor> color); 73 74 // Gets the current state text color. 75 SkColor GetCurrentTextColor() const; 76 77 // Sets drop shadows underneath the text. 78 void SetTextShadows(const gfx::ShadowValues& shadows); 79 80 // Sets whether subpixel rendering is used on the label. 81 void SetTextSubpixelRenderingEnabled(bool enabled); 82 83 // Sets the elide behavior of this button. 84 void SetElideBehavior(gfx::ElideBehavior elide_behavior); 85 86 // Sets the horizontal alignment used for the button; reversed in RTL. The 87 // optional image will lead the text, unless the button is right-aligned. 88 void SetHorizontalAlignment(gfx::HorizontalAlignment alignment); 89 gfx::HorizontalAlignment GetHorizontalAlignment() const; 90 91 gfx::Size GetMinSize() const; 92 void SetMinSize(const gfx::Size& min_size); 93 94 gfx::Size GetMaxSize() const; 95 void SetMaxSize(const gfx::Size& max_size); 96 97 // Gets or sets the option to handle the return key; false by default. 98 bool GetIsDefault() const; 99 void SetIsDefault(bool is_default); 100 101 // Sets the spacing between the image and the text. 102 int GetImageLabelSpacing() const; 103 void SetImageLabelSpacing(int spacing); 104 105 // Gets or sets the option to place the image aligned with the center of the 106 // the label. The image is not centered for CheckBox and RadioButton only. 107 bool GetImageCentered() const; 108 void SetImageCentered(bool image_centered); 109 110 // Creates the default border for this button. This can be overridden by 111 // subclasses. 112 virtual std::unique_ptr<LabelButtonBorder> CreateDefaultBorder() const; 113 114 // Button: 115 void SetBorder(std::unique_ptr<Border> border) override; 116 void OnBoundsChanged(const gfx::Rect& previous_bounds) override; 117 gfx::Size CalculatePreferredSize() const override; 118 gfx::Size GetMinimumSize() const override; 119 int GetHeightForWidth(int w) const override; 120 void Layout() override; 121 void GetAccessibleNodeData(ui::AXNodeData* node_data) override; 122 void AddLayerBeneathView(ui::Layer* new_layer) override; 123 void RemoveLayerBeneathView(ui::Layer* old_layer) override; 124 125 // NativeThemeDelegate: 126 ui::NativeTheme::Part GetThemePart() const override; 127 gfx::Rect GetThemePaintRect() const override; 128 ui::NativeTheme::State GetThemeState( 129 ui::NativeTheme::ExtraParams* params) const override; 130 const gfx::Animation* GetThemeAnimation() const override; 131 ui::NativeTheme::State GetBackgroundThemeState( 132 ui::NativeTheme::ExtraParams* params) const override; 133 ui::NativeTheme::State GetForegroundThemeState( 134 ui::NativeTheme::ExtraParams* params) const override; 135 136 protected: image()137 ImageView* image() const { return image_; } label()138 Label* label() const { return label_; } ink_drop_container()139 InkDropContainerView* ink_drop_container() const { 140 return ink_drop_container_; 141 } 142 explicitly_set_normal_color()143 bool explicitly_set_normal_color() const { 144 return explicitly_set_colors_[STATE_NORMAL]; 145 } 146 explicitly_set_colors()147 const std::array<bool, STATE_COUNT>& explicitly_set_colors() const { 148 return explicitly_set_colors_; 149 } set_explicitly_set_colors(const std::array<bool,STATE_COUNT> & colors)150 void set_explicitly_set_colors(const std::array<bool, STATE_COUNT>& colors) { 151 explicitly_set_colors_ = colors; 152 } 153 154 // Updates the image view to contain the appropriate button state image. 155 void UpdateImage(); 156 157 // Updates the background color, if the background color is state-sensitive. UpdateBackgroundColor()158 virtual void UpdateBackgroundColor() {} 159 160 // Returns the current visual appearance of the button. This takes into 161 // account both the button's underlying state, the state of the containing 162 // widget, and the parent of the containing widget. 163 ButtonState GetVisualState() const; 164 165 // Fills |params| with information about the button. 166 virtual void GetExtraParams(ui::NativeTheme::ExtraParams* params) const; 167 168 // Changes the visual styling to match changes in the default state. Returns 169 // the PropertyEffects triggered as a result. 170 virtual PropertyEffects UpdateStyleToIndicateDefaultStatus(); 171 172 // Button: 173 void ChildPreferredSizeChanged(View* child) override; 174 void AddedToWidget() override; 175 void RemovedFromWidget() override; 176 void OnFocus() override; 177 void OnBlur() override; 178 void OnThemeChanged() override; 179 void StateChanged(ButtonState old_state) override; 180 181 private: 182 void SetTextInternal(const base::string16& text); 183 184 void ClearTextIfShrunkDown(); 185 186 // Gets the preferred size (without respecting min_size_ or max_size_), but 187 // does not account for the label. This is shared between GetHeightForWidth 188 // and CalculatePreferredSize. GetHeightForWidth will subtract the width 189 // returned from this method to get width available for the label while 190 // CalculatePreferredSize will just add the preferred width from the label. 191 // Both methods will then use the max of inset height + label height and this 192 // height as total height, and clamp to min/max sizes as appropriate. 193 gfx::Size GetUnclampedSizeWithoutLabel() const; 194 195 // Updates the portions of the object that might change in response to a 196 // change in the value returned by GetVisualState(). 197 void VisualStateChanged(); 198 199 // Resets colors from the NativeTheme, explicitly set colors are unchanged. 200 void ResetColorsFromNativeTheme(); 201 202 // Updates additional state related to focus or default status, rather than 203 // merely the Button::state(). E.g. ensures the label text color is 204 // correct for the current background. 205 void ResetLabelEnabledColor(); 206 207 // Returns the state whose image is shown for |for_state|, by falling back to 208 // STATE_NORMAL when |for_state|'s image is empty. 209 ButtonState ImageStateForState(ButtonState for_state) const; 210 211 void FlipCanvasOnPaintForRTLUIChanged(); 212 213 // The image and label shown in the button. 214 ImageView* image_; 215 internal::LabelButtonLabel* label_; 216 217 // A separate view is necessary to hold the ink drop layer so that it can 218 // be stacked below |image_| and on top of |label_|, without resorting to 219 // drawing |label_| on a layer (which can mess with subpixel anti-aliasing). 220 InkDropContainerView* ink_drop_container_; 221 222 // The cached font lists in the normal and default button style. The latter 223 // may be bold. 224 gfx::FontList cached_normal_font_list_; 225 gfx::FontList cached_default_button_font_list_; 226 227 // The image models and colors for each button state. 228 ui::ImageModel button_state_image_models_[STATE_COUNT] = {}; 229 SkColor button_state_colors_[STATE_COUNT] = {}; 230 231 // Used to track whether SetTextColor() has been invoked. 232 std::array<bool, STATE_COUNT> explicitly_set_colors_ = {}; 233 234 // |min_size_| and |max_size_| may be set to clamp the preferred size. 235 gfx::Size min_size_; 236 gfx::Size max_size_; 237 238 // A flag indicating that this button should not include the label in its 239 // desired size. Furthermore, once the bounds of the button adapt to this 240 // desired size, the text in the label should get cleared. 241 bool shrinking_down_label_ = false; 242 243 // Flag indicating default handling of the return key via an accelerator. 244 // Whether or not the button appears or behaves as the default button in its 245 // current context; 246 bool is_default_ = false; 247 248 // True if current border was set by UpdateThemedBorder. 249 bool border_is_themed_border_ = true; 250 251 // A flag indicating that this button's image should be aligned with the 252 // center of the label when multiline is enabled. This shouldn't be the case 253 // for a CheckBox or a RadioButton. 254 bool image_centered_ = true; 255 256 // Spacing between the image and the text. 257 int image_label_spacing_ = LayoutProvider::Get()->GetDistanceMetric( 258 DISTANCE_RELATED_LABEL_HORIZONTAL); 259 260 // Alignment of the button. This can be different from the alignment of the 261 // text; for example, the label may be set to ALIGN_TO_HEAD (alignment matches 262 // text direction) while |this| is laid out as ALIGN_LEFT (alignment matches 263 // UI direction). 264 gfx::HorizontalAlignment horizontal_alignment_ = gfx::ALIGN_LEFT; 265 266 std::unique_ptr<Widget::PaintAsActiveCallbackList::Subscription> 267 paint_as_active_subscription_; 268 269 PropertyChangedSubscription flip_canvas_on_paint_subscription_ = 270 AddFlipCanvasOnPaintForRTLUIChangedCallback( 271 base::BindRepeating(&LabelButton::FlipCanvasOnPaintForRTLUIChanged, 272 base::Unretained(this))); 273 }; 274 275 BEGIN_VIEW_BUILDER(VIEWS_EXPORT, LabelButton, Button) 276 VIEW_BUILDER_PROPERTY(base::string16, Text) 277 VIEW_BUILDER_PROPERTY(gfx::HorizontalAlignment, HorizontalAlignment) 278 VIEW_BUILDER_PROPERTY(gfx::Size, MinSize) 279 VIEW_BUILDER_PROPERTY(gfx::Size, MaxSize) 280 VIEW_BUILDER_PROPERTY(bool, IsDefault) 281 VIEW_BUILDER_PROPERTY(int, ImageLabelSpacing) 282 VIEW_BUILDER_PROPERTY(bool, ImageCentered) 283 END_VIEW_BUILDER 284 285 } // namespace views 286 287 DEFINE_VIEW_BUILDER(VIEWS_EXPORT, LabelButton) 288 289 #endif // UI_VIEWS_CONTROLS_BUTTON_LABEL_BUTTON_H_ 290