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 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
|
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_CONTROLS_STYLED_LABEL_H_
#define UI_VIEWS_CONTROLS_STYLED_LABEL_H_
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "base/containers/lru_cache.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/class_property.h"
#include "ui/color/color_id.h"
#include "ui/color/color_variant.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/range/range.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/controls/link.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/style/typography.h"
#include "ui/views/view.h"
namespace views {
class Label;
class Link;
class LinkFragment;
// A class which can apply mixed styles to a block of text. Currently, text is
// always multiline. Trailing whitespace in the styled label text is not
// supported and will be trimmed on StyledLabel construction. Leading
// whitespace is respected, provided not only whitespace fits in the first line.
// In this case, leading whitespace is ignored.
class VIEWS_EXPORT StyledLabel : public View {
METADATA_HEADER(StyledLabel, View)
public:
// Parameters that define label style for a styled label's text range.
struct VIEWS_EXPORT RangeStyleInfo {
RangeStyleInfo();
RangeStyleInfo(const RangeStyleInfo&);
RangeStyleInfo& operator=(const RangeStyleInfo&);
~RangeStyleInfo();
// Creates a range style info with default values for link.
static RangeStyleInfo CreateForLink(Link::ClickedCallback callback);
static RangeStyleInfo CreateForLink(base::RepeatingClosure callback);
// Allows full customization of the font used in the range. Ignores the
// StyledLabel's default text context and |text_style|.
std::optional<gfx::FontList> custom_font;
// The style::TextStyle for this range.
std::optional<int> text_style;
// Overrides the text color given by |text_style| for this range.
// DEPRECATED: Use TextStyle.
std::optional<SkColor> override_color;
// Overrides the text color given by |text_style| for this range.
std::optional<ui::ColorId> override_color_id;
// A callback to be called when this link is clicked. Only used if
// |text_style| is style::STYLE_LINK.
Link::ClickedCallback callback;
// Tooltip for the range.
std::u16string tooltip;
// Accessible name for the range.
std::u16string accessible_name;
// A custom view shown instead of the underlying text. Ownership of custom
// views must be passed to StyledLabel via AddCustomView().
raw_ptr<View> custom_view = nullptr;
};
// Sizing information for laying out the label based on a particular width.
struct VIEWS_EXPORT LayoutSizeInfo {
explicit LayoutSizeInfo(int max_valid_width);
LayoutSizeInfo(const LayoutSizeInfo&);
LayoutSizeInfo& operator=(const LayoutSizeInfo&);
~LayoutSizeInfo();
// The maximum width for which this info is guaranteed to be valid.
// Requesting a larger width than this will force a recomputation.
int max_valid_width = 0;
// The actual size needed to lay out the label for a requested width of
// |max_valid_width|. total_size.width() is at most |max_valid_width| but
// may be smaller depending on how line wrapping is computed. Requesting a
// smaller width than this will force a recomputation.
gfx::Size total_size;
// The sizes of each line of child views. |total_size| can be computed
// directly from these values and is kept separately just for convenience.
std::vector<gfx::Size> line_sizes;
};
StyledLabel();
StyledLabel(const StyledLabel&) = delete;
StyledLabel& operator=(const StyledLabel&) = delete;
~StyledLabel() override;
// Sets the text to be displayed, and clears any previous styling. Trailing
// whitespace is trimmed from the text.
const std::u16string& GetText() const;
void SetText(std::u16string text);
// Returns the FontList that should be used. |style_info| is an optional
// argument that takes precedence over the default values.
gfx::FontList GetFontList(
const RangeStyleInfo& style_info = RangeStyleInfo()) const;
// Marks the given range within |text_| with style defined by |style_info|.
// |range| must be contained in |text_|.
void AddStyleRange(const gfx::Range& range, const RangeStyleInfo& style_info);
// Passes ownership of a custom view for use by RangeStyleInfo structs.
void AddCustomView(std::unique_ptr<View> custom_view);
// Get/Set the context of this text. All ranges have the same context.
// |text_context| must be a value from views::style::TextContext.
int GetTextContext() const;
void SetTextContext(int text_context);
// Set the default text style.
// |text_style| must be a value from views::style::TextStyle.
int GetDefaultTextStyle() const;
void SetDefaultTextStyle(int text_style);
// Set the default enabled color id.
std::optional<ui::ColorId> GetDefaultEnabledColorId() const;
void SetDefaultEnabledColorId(std::optional<ui::ColorId> enabled_color_id);
// Get or set the distance in pixels between baselines of multi-line text.
// Default is 0, indicating the distance between lines should be the standard
// one for the label's text, font list, and platform.
int GetLineHeight() const;
void SetLineHeight(int height);
// Gets/Sets the color or color id of the background on which the label is
// drawn. This won't be explicitly drawn, but the label will force the text
// color to be readable over it.
std::optional<ui::ColorVariant> GetDisplayedOnBackgroundColor() const;
void SetDisplayedOnBackgroundColor(ui::ColorVariant color);
bool GetAutoColorReadabilityEnabled() const;
void SetAutoColorReadabilityEnabled(bool auto_color_readability);
bool GetSubpixelRenderingEnabled() const;
void SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled);
// Returns the layout size information that would be used to layout the label
// at width |w|. This can be used by callers who need more detail than what's
// provided by GetHeightForWidth().
const LayoutSizeInfo& GetLayoutSizeInfoForWidth(int w) const;
// Resizes the label so its width is set to the fixed width and its height
// deduced accordingly. Even if all widths of the lines are shorter than
// |fixed_width|, the given value is applied to the element's width.
// This is only intended for multi-line labels and is useful when the label's
// text contains several lines separated with \n.
// |fixed_width| is the fixed width that will be used (longer lines will be
// wrapped). If 0, no fixed width is enforced.
void SizeToFit(int fixed_width);
[[nodiscard]] base::CallbackListSubscription AddTextChangedCallback(
views::PropertyChangedCallback callback);
// View:
gfx::Size GetMinimumSize() const override;
gfx::Size CalculatePreferredSize(
const SizeBounds& available_size) const override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
void Layout(PassKey) override;
void PreferredSizeChanged() override;
// Sets the horizontal alignment; the argument value is mirrored in RTL UI.
void SetHorizontalAlignment(gfx::HorizontalAlignment alignment);
// Clears all the styles applied to the label.
void ClearStyleRanges();
// Sends a space keypress to the first child that is a link. Assumes at least
// one such child exists.
void ClickFirstLinkForTesting();
// Gets the first child that is a link. Returns nullptr if there isn't any.
views::Link* GetFirstLinkForTesting();
private:
struct StyleRange {
StyleRange(const gfx::Range& range, const RangeStyleInfo& style_info)
: range(range), style_info(style_info) {}
~StyleRange() = default;
auto operator<=>(const StyleRange& other) const;
gfx::Range range;
RangeStyleInfo style_info;
};
using StyleRanges = std::list<StyleRange>;
// Child view-related information for layout.
struct LayoutViews;
// Returns the starting X coordinate for the views in a line, based on the
// current |horizontal_alignment_| and insets and given the amount of excess
// space available on that line.
int StartX(int excess_space) const;
// Sets |layout_size_info_| and |layout_views_| for the given |width|. No-op
// if current_width <= width <= max_width, where:
// current_width = layout_size_info_.total_size.width()
// width = max(width, GetInsets().width())
// max_width = layout_size_info_.max_valid_width
void CalculateLayout(int width) const;
// Creates a Label for a given |text|, |style_info|, and |range|.
std::unique_ptr<Label> CreateLabel(
const std::u16string& text,
const RangeStyleInfo& style_info,
const gfx::Range& range,
LinkFragment** previous_link_component) const;
// Update the label background color from the theme or
// |displayed_on_background_color_| if set.
void UpdateLabelBackgroundColor();
// Remove all child views. Place all custom views back into custom_views_ and
// delete the rest.
void RemoveOrDeleteAllChildViews();
void RecreateChildViews();
// The text to display.
std::u16string text_;
int text_context_ = style::CONTEXT_LABEL;
int default_text_style_ = style::STYLE_PRIMARY;
std::optional<ui::ColorId> default_enabled_color_id_;
std::optional<int> line_height_;
int fixed_width_ = 0;
// Temporarily owns the custom views until they've been been placed into the
// StyledLabel's child list. This list also holds the custom views during
// layout.
std::list<std::unique_ptr<View>> custom_views_;
// Temporarily owns the views to be deleted during layout. These views might
// still be referenced on the stack. If we delete them immediately, UaFs
// could happen when the stack unwinds.
std::vector<std::unique_ptr<View>> pending_delete_views_;
// The ranges that should be linkified, sorted by start position.
StyleRanges style_ranges_;
// Saves the effects of the last CalculateLayout() call to avoid repeated
// calculation. |layout_size_info_| can then be cached until the next
// recalculation, while |layout_views_| only exists until the next Layout().
mutable LayoutSizeInfo layout_size_info_{0};
mutable std::unique_ptr<LayoutViews> layout_views_;
// Saves the LayoutSizeInfo for additional CalculateLayout() calls. Layout
// managers sometimes repeatedly ask for size information for the same (small)
// number of widths. Caching multiple LayoutSideInfos helps avoid doing many
// unnecessary calculations.
mutable base::LRUCache<int, LayoutSizeInfo> layout_size_info_cache_{16};
// Background color on which the label is drawn, for auto color readability.
std::optional<ui::ColorVariant> displayed_on_background_color_;
// Controls whether the text is automatically re-colored to be readable on the
// background.
bool auto_color_readability_enabled_ = true;
// Controls whether subpixel rendering is enabled.
bool subpixel_rendering_enabled_ = true;
// Controls whether subviews need to be recreated. Recreating subviews can
// cause some functionality to break under certain circumstances.
// eg: If re-creating the subview occurs after OnMousePressed() and before
// OnMouseRelease(), the link will not be clickable.
bool need_recreate_child_ = true;
// The horizontal alignment. This value is flipped for RTL. The default
// behavior is to align left in LTR UI and right in RTL UI.
gfx::HorizontalAlignment horizontal_alignment_ =
base::i18n::IsRTL() ? gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
};
BEGIN_VIEW_BUILDER(VIEWS_EXPORT, StyledLabel, View)
VIEW_BUILDER_PROPERTY(const std::u16string&, Text)
VIEW_BUILDER_PROPERTY(int, TextContext)
VIEW_BUILDER_PROPERTY(int, DefaultTextStyle)
VIEW_BUILDER_PROPERTY(int, LineHeight)
VIEW_BUILDER_PROPERTY(ui::ColorVariant, DisplayedOnBackgroundColor)
VIEW_BUILDER_PROPERTY(bool, AutoColorReadabilityEnabled)
VIEW_BUILDER_PROPERTY(gfx::HorizontalAlignment, HorizontalAlignment)
VIEW_BUILDER_PROPERTY(std::optional<ui::ColorId>, DefaultEnabledColorId)
VIEW_BUILDER_METHOD(SizeToFit, int)
VIEW_BUILDER_METHOD(AddStyleRange, gfx::Range, StyledLabel::RangeStyleInfo)
END_VIEW_BUILDER
} // namespace views
DEFINE_VIEW_BUILDER(VIEWS_EXPORT, views::StyledLabel)
#endif // UI_VIEWS_CONTROLS_STYLED_LABEL_H_
|