summaryrefslogtreecommitdiff
path: root/chromium/ui/views/controls/label.h
blob: 0a6bac23a2f67446b91383d4909012747f5db342 (plain)
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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_VIEWS_CONTROLS_LABEL_H_
#define UI_VIEWS_CONTROLS_LABEL_H_

#include <memory>
#include <vector>

#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/optional.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/render_text.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/metadata/metadata_header_macros.h"
#include "ui/views/metadata/view_factory.h"
#include "ui/views/selection_controller_delegate.h"
#include "ui/views/style/typography.h"
#include "ui/views/view.h"
#include "ui/views/word_lookup_client.h"

namespace views {
class LabelSelectionTest;
class MenuRunner;
class SelectionController;

// A view subclass that can display a string.
class VIEWS_EXPORT Label : public View,
                           public ContextMenuController,
                           public WordLookupClient,
                           public SelectionControllerDelegate,
                           public ui::SimpleMenuModel::Delegate {
 public:
  METADATA_HEADER(Label);

  enum MenuCommands {
    kCopy = 1,
    kSelectAll,
    kLastCommandId = kSelectAll,
  };

  // Helper to construct a Label that doesn't use the views typography spec.
  // Using this causes Label to obtain colors from ui::NativeTheme and line
  // spacing from gfx::FontList::GetHeight().
  // TODO(tapted): Audit users of this class when MD is default. Then add
  // foreground/background colors, line spacing and everything else that
  // views::TextContext abstracts away so the separate setters can be removed.
  struct CustomFont {
    // TODO(tapted): Change this to a size delta and font weight since that's
    // typically all the callers really care about, and would allow Label to
    // guarantee caching of the FontList in ResourceBundle.
    const gfx::FontList& font_list;
  };

  // Create Labels with style::CONTEXT_CONTROL_LABEL and style::STYLE_PRIMARY.
  // TODO(tapted): Remove these. Callers must specify a context or use the
  // constructor taking a CustomFont.
  Label();
  explicit Label(const base::string16& text);

  // Construct a Label in the given |text_context|. The |text_style| can change
  // later, so provide a default. The |text_context| is fixed.
  // By default text directionality will be derived from the label text, however
  // it can be overriden with |directionality_mode|.
  Label(const base::string16& text,
        int text_context,
        int text_style = style::STYLE_PRIMARY,
        gfx::DirectionalityMode directionality_mode =
            gfx::DirectionalityMode::DIRECTIONALITY_FROM_TEXT);

  // Construct a Label with the given |font| description.
  Label(const base::string16& text, const CustomFont& font);

  ~Label() override;

  static const gfx::FontList& GetDefaultFontList();

  // Gets or sets the fonts used by this label.
  const gfx::FontList& font_list() const { return full_text_->font_list(); }

  // TODO(tapted): Replace this with a private method, e.g., OnFontChanged().
  virtual void SetFontList(const gfx::FontList& font_list);

  // Get or set the label text.
  const base::string16& GetText() const;
  virtual void SetText(const base::string16& text);

  // Where the label appears in the UI. Passed in from the constructor. This is
  // a value from views::style::TextContext or an enum that extends it.
  int GetTextContext() const;
  void SetTextContext(int text_context);

  // The style of the label.  This is a value from views::style::TextStyle or an
  // enum that extends it.
  int GetTextStyle() const;
  void SetTextStyle(int style);

  // Enables or disables auto-color-readability (enabled by default).  If this
  // is enabled, then calls to set any foreground or background color will
  // trigger an automatic mapper that uses color_utils::BlendForMinContrast()
  // to ensure that the foreground colors are readable over the background
  // color.
  bool GetAutoColorReadabilityEnabled() const;
  void SetAutoColorReadabilityEnabled(bool enabled);

  // Gets/Sets the color.  This will automatically force the color to be
  // readable over the current background color, if auto color readability is
  // enabled.
  SkColor GetEnabledColor() const;
  virtual void SetEnabledColor(SkColor color);

  // Gets/Sets the background color. This won't be explicitly drawn, but the
  // label will force the text color to be readable over it.
  SkColor GetBackgroundColor() const;
  void SetBackgroundColor(SkColor color);

  // Gets/Sets the selection text color. This will automatically force the color
  // to be readable over the selection background color, if auto color
  // readability is enabled. Initialized with system default.
  SkColor GetSelectionTextColor() const;
  void SetSelectionTextColor(SkColor color);

  // Gets/Sets the selection background color. Initialized with system default.
  SkColor GetSelectionBackgroundColor() const;
  void SetSelectionBackgroundColor(SkColor color);

  // Get/Set drop shadows underneath the text.
  const gfx::ShadowValues& GetShadows() const;
  void SetShadows(const gfx::ShadowValues& shadows);

  // Gets/Sets whether subpixel rendering is used; the default is true, but this
  // feature also requires an opaque background color.
  // TODO(mukai): rename this as SetSubpixelRenderingSuppressed() to keep the
  // consistency with RenderText field name.
  bool GetSubpixelRenderingEnabled() const;
  void SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled);

  // Gets/Sets whether the DCHECK() checking that subpixel-rendered text is
  // only drawn onto opaque layers is skipped. Use this to suppress false
  // positives - for example, if the label is drawn onto an opaque region of a
  // non-opaque layer. If possible, prefer making the layer opaque or painting
  // onto an opaque views::Background, as those cases are detected and
  // excluded by this DCHECK automatically.
  bool GetSkipSubpixelRenderingOpacityCheck() const;
  void SetSkipSubpixelRenderingOpacityCheck(
      bool skip_subpixel_rendering_opacity_check);

  // Gets/Sets the horizontal alignment; the argument value is mirrored in RTL
  // UI.
  gfx::HorizontalAlignment GetHorizontalAlignment() const;
  void SetHorizontalAlignment(gfx::HorizontalAlignment alignment);

  // Gets/Sets the vertical alignment. Affects how whitespace is distributed
  // vertically around the label text, or if the label is not tall enough to
  // render all of the text, what gets cut off. ALIGN_MIDDLE is default and is
  // strongly suggested for single-line labels because it produces a consistent
  // baseline even when rendering with mixed fonts.
  gfx::VerticalAlignment GetVerticalAlignment() const;
  void SetVerticalAlignment(gfx::VerticalAlignment alignment);

  // Get or set the distance in pixels between baselines of multi-line text.
  // Default is the height of the default font.
  int GetLineHeight() const;
  void SetLineHeight(int line_height);

  // Get or set if the label text can wrap on multiple lines; default is false.
  bool GetMultiLine() const;
  void SetMultiLine(bool multi_line);

  // If multi-line, a non-zero value will cap the number of lines rendered, and
  // elide the rest (currently only ELIDE_TAIL supported). See gfx::RenderText.
  int GetMaxLines() const;
  void SetMaxLines(int max_lines);

  // Returns the number of lines required to render all text. The actual number
  // of rendered lines might be limited by |max_lines_| which elides the rest.
  size_t GetRequiredLines() const;

  // Get or set if the label text should be obscured before rendering (e.g.
  // should "Password!" display as "*********"); default is false.
  bool GetObscured() const;
  void SetObscured(bool obscured);

  // Returns true if some portion of the text is not displayed, either because
  // of eliding or clipping.
  bool IsDisplayTextTruncated() const;

  // Gets/Sets whether multi-line text can wrap mid-word; the default is false.
  // TODO(mukai): allow specifying WordWrapBehavior.
  bool GetAllowCharacterBreak() const;
  void SetAllowCharacterBreak(bool allow_character_break);

  // For the provided line index, gets the corresponding rendered line and
  // returns the text position of the first character of that line.
  size_t GetTextIndexOfLine(size_t line) const;

  // Set the truncate length of the |full_text_|.
  // NOTE: This does not affect the |display_text_|, since right now the only
  // consumer does not need that; if you need this function, you may need to
  // implement this.
  void SetTruncateLength(size_t truncate_length);

  // Gets/Sets the eliding or fading behavior, applied as necessary. The default
  // is to elide at the end. Eliding is not well-supported for multi-line
  // labels.
  gfx::ElideBehavior GetElideBehavior() const;
  void SetElideBehavior(gfx::ElideBehavior elide_behavior);

  // Gets/Sets the tooltip text.  Default behavior for a label (single-line) is
  // to show the full text if it is wider than its bounds.  Calling this
  // overrides the default behavior and lets you set a custom tooltip.  To
  // revert to default behavior, call this with an empty string.
  base::string16 GetTooltipText() const;
  void SetTooltipText(const base::string16& tooltip_text);

  // Get or set whether this label can act as a tooltip handler; the default is
  // true.  Set to false whenever an ancestor view should handle tooltips
  // instead.
  bool GetHandlesTooltips() const;
  void SetHandlesTooltips(bool enabled);

  // 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);

  // Like SizeToFit, but uses a smaller width if possible.
  int GetMaximumWidth() const;
  void SetMaximumWidth(int max_width);

  // Gets/Sets whether the preferred size is empty when the label is not
  // visible.
  bool GetCollapseWhenHidden() const;
  void SetCollapseWhenHidden(bool value);

  // Get the text as displayed to the user, respecting the obscured flag.
  base::string16 GetDisplayTextForTesting();

  // Get the text direction, as displayed to the user.
  base::i18n::TextDirection GetTextDirectionForTesting();

  // Returns true if the label can be made selectable. For example, links do not
  // support text selection.
  // Subclasses should override this function in case they want to selectively
  // support text selection. If a subclass stops supporting text selection, it
  // should call SetSelectable(false).
  virtual bool IsSelectionSupported() const;

  // Returns true if the label is selectable. Default is false.
  bool GetSelectable() const;

  // Sets whether the label is selectable. False is returned if the call fails,
  // i.e. when selection is not supported but |selectable| is true. For example,
  // obscured labels do not support text selection.
  bool SetSelectable(bool selectable);

  // Returns true if the label has a selection.
  bool HasSelection() const;

  // Selects the entire text. NO-OP if the label is not selectable.
  void SelectAll();

  // Clears any active selection.
  void ClearSelection();

  // Selects the given text range. NO-OP if the label is not selectable or the
  // |range| endpoints don't lie on grapheme boundaries.
  void SelectRange(const gfx::Range& range);

  // Get the visual bounds containing the logical substring of the full text
  // within the |range|. See gfx::RenderText.
  std::vector<gfx::Rect> GetSubstringBounds(const gfx::Range& range);

  views::PropertyChangedSubscription AddTextChangedCallback(
      views::PropertyChangedCallback callback) WARN_UNUSED_RESULT;

  // View:
  int GetBaseline() const override;
  gfx::Size CalculatePreferredSize() const override;
  gfx::Size GetMinimumSize() const override;
  int GetHeightForWidth(int w) const override;
  View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
  bool GetCanProcessEventsWithinSubtree() const override;
  WordLookupClient* GetWordLookupClient() override;
  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
  base::string16 GetTooltipText(const gfx::Point& p) const override;

 protected:
  // Create a single RenderText instance to actually be painted.
  virtual std::unique_ptr<gfx::RenderText> CreateRenderText() const;

  // Returns the preferred size and position of the text in local coordinates,
  // which may exceed the local bounds of the label.
  gfx::Rect GetTextBounds() const;

  void PaintText(gfx::Canvas* canvas);

  // View:
  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
  void VisibilityChanged(View* starting_from, bool is_visible) override;
  void OnPaint(gfx::Canvas* canvas) override;
  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
                                  float new_device_scale_factor) override;
  void OnThemeChanged() override;
  gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;
  void OnFocus() override;
  void OnBlur() override;
  bool OnMousePressed(const ui::MouseEvent& event) override;
  bool OnMouseDragged(const ui::MouseEvent& event) override;
  void OnMouseReleased(const ui::MouseEvent& event) override;
  void OnMouseCaptureLost() override;
  bool OnKeyPressed(const ui::KeyEvent& event) override;
  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
  bool CanHandleAccelerators() const override;

 private:
  FRIEND_TEST_ALL_PREFIXES(LabelTest, ResetRenderTextData);
  FRIEND_TEST_ALL_PREFIXES(LabelTest, MultilineSupportedRenderText);
  FRIEND_TEST_ALL_PREFIXES(LabelTest, TextChangeWithoutLayout);
  FRIEND_TEST_ALL_PREFIXES(LabelTest, EmptyLabel);
  FRIEND_TEST_ALL_PREFIXES(LabelTest, FocusBounds);
  FRIEND_TEST_ALL_PREFIXES(LabelTest, MultiLineSizingWithElide);
  FRIEND_TEST_ALL_PREFIXES(LabelTest, IsDisplayTextTruncated);
  FRIEND_TEST_ALL_PREFIXES(LabelTest, ChecksSubpixelRenderingOntoOpaqueSurface);
  friend class LabelSelectionTest;

  // ContextMenuController overrides:
  void ShowContextMenuForViewImpl(View* source,
                                  const gfx::Point& point,
                                  ui::MenuSourceType source_type) override;

  // WordLookupClient overrides:
  bool GetWordLookupDataAtPoint(const gfx::Point& point,
                                gfx::DecoratedText* decorated_word,
                                gfx::Point* baseline_point) override;

  bool GetWordLookupDataFromSelection(gfx::DecoratedText* decorated_text,
                                      gfx::Point* baseline_point) override;

  // SelectionControllerDelegate overrides:
  gfx::RenderText* GetRenderTextForSelectionController() override;
  bool IsReadOnly() const override;
  bool SupportsDrag() const override;
  bool HasTextBeingDragged() const override;
  void SetTextBeingDragged(bool value) override;
  int GetViewHeight() const override;
  int GetViewWidth() const override;
  int GetDragSelectionDelay() const override;
  void OnBeforePointerAction() override;
  void OnAfterPointerAction(bool text_changed, bool selection_changed) override;
  bool PasteSelectionClipboard() override;
  void UpdateSelectionClipboard() override;

  // ui::SimpleMenuModel::Delegate overrides:
  bool IsCommandIdChecked(int command_id) const override;
  bool IsCommandIdEnabled(int command_id) const override;
  void ExecuteCommand(int command_id, int event_flags) override;
  bool GetAcceleratorForCommandId(int command_id,
                                  ui::Accelerator* accelerator) const override;

  const gfx::RenderText* GetRenderTextForSelectionController() const;

  void Init(const base::string16& text,
            const gfx::FontList& font_list,
            gfx::DirectionalityMode directionality_mode);

  // Set up |display_text_| to actually be painted.
  void MaybeBuildDisplayText() const;

  // Get the text size for the current layout.
  gfx::Size GetTextSize() const;

  // Returns the appropriate foreground color to use given the proposed
  // |foreground| and |background| colors.
  SkColor GetForegroundColor(SkColor foreground, SkColor background) const;

  // Updates text and selection colors from requested colors.
  void RecalculateColors();

  // Applies the foreground color to |display_text_|.
  void ApplyTextColors() const;

  // Updates any colors that have not been explicitly set from the theme.
  void UpdateColorsFromTheme();

  bool ShouldShowDefaultTooltip() const;

  // Clears |display_text_| and updates |stored_selection_range_|.
  // TODO(crbug.com/1103804) Most uses of this function are inefficient; either
  // replace with setting attributes on both RenderTexts or collapse them to one
  // RenderText.
  void ClearDisplayText();

  // Returns the currently selected text.
  base::string16 GetSelectedText() const;

  // Updates the clipboard with the currently selected text.
  void CopyToClipboard();

  // Builds |context_menu_contents_|.
  void BuildContextMenuContents();

  int text_context_;
  int text_style_;
  base::Optional<int> line_height_;

  // An un-elided and single-line RenderText object used for preferred sizing.
  std::unique_ptr<gfx::RenderText> full_text_;

  // The RenderText instance used for drawing.
  mutable std::unique_ptr<gfx::RenderText> display_text_;

  // Persists the current selection range between the calls to
  // ClearDisplayText() and MaybeBuildDisplayText(). Holds an InvalidRange when
  // not in use.
  mutable gfx::Range stored_selection_range_ = gfx::Range::InvalidRange();

  SkColor requested_enabled_color_ = gfx::kPlaceholderColor;
  SkColor actual_enabled_color_ = gfx::kPlaceholderColor;
  SkColor background_color_ = gfx::kPlaceholderColor;
  SkColor requested_selection_text_color_ = gfx::kPlaceholderColor;
  SkColor actual_selection_text_color_ = gfx::kPlaceholderColor;
  SkColor selection_background_color_ = gfx::kPlaceholderColor;

  // Set to true once the corresponding setter is invoked.
  bool enabled_color_set_ = false;
  bool background_color_set_ = false;
  bool selection_text_color_set_ = false;
  bool selection_background_color_set_ = false;

  gfx::ElideBehavior elide_behavior_ = gfx::ELIDE_TAIL;

  bool subpixel_rendering_enabled_ = true;
  bool skip_subpixel_rendering_opacity_check_ = false;
  bool auto_color_readability_enabled_ = true;
  // TODO(mukai): remove |multi_line_| when all RenderText can render multiline.
  bool multi_line_ = false;
  int max_lines_ = 0;
  base::string16 tooltip_text_;
  bool handles_tooltips_ = true;
  // Whether to collapse the label when it's not visible.
  bool collapse_when_hidden_ = false;
  int fixed_width_ = 0;
  int max_width_ = 0;

  std::unique_ptr<SelectionController> selection_controller_;

  // Context menu related members.
  ui::SimpleMenuModel context_menu_contents_;
  std::unique_ptr<views::MenuRunner> context_menu_runner_;

  DISALLOW_COPY_AND_ASSIGN(Label);
};

BEGIN_VIEW_BUILDER(VIEWS_EXPORT, Label, View)
VIEW_BUILDER_PROPERTY(const gfx::FontList&, FontList)
VIEW_BUILDER_PROPERTY(const base::string16&, Text)
VIEW_BUILDER_PROPERTY(int, TextStyle)
VIEW_BUILDER_PROPERTY(int, TextContext)
VIEW_BUILDER_PROPERTY(bool, AutoColorReadabilityEnabled)
VIEW_BUILDER_PROPERTY(SkColor, EnabledColor)
VIEW_BUILDER_PROPERTY(SkColor, BackgroundColor)
VIEW_BUILDER_PROPERTY(SkColor, SelectionTextColor)
VIEW_BUILDER_PROPERTY(SkColor, SelectionBackgroundColor)
VIEW_BUILDER_PROPERTY(const gfx::ShadowValues&, Shadows)
VIEW_BUILDER_PROPERTY(bool, SubpixelRenderingEnabled)
VIEW_BUILDER_PROPERTY(bool, SkipSubpixelRenderingOpacityCheck)
VIEW_BUILDER_PROPERTY(gfx::HorizontalAlignment, HorizontalAlignment)
VIEW_BUILDER_PROPERTY(gfx::VerticalAlignment, VerticalAlignment)
VIEW_BUILDER_PROPERTY(int, LineHeight)
VIEW_BUILDER_PROPERTY(bool, MultiLine)
VIEW_BUILDER_PROPERTY(int, MaxLines)
VIEW_BUILDER_PROPERTY(bool, Obscured)
VIEW_BUILDER_PROPERTY(bool, AllowCharacterBreak)
VIEW_BUILDER_PROPERTY(size_t, TruncateLength)
VIEW_BUILDER_PROPERTY(gfx::ElideBehavior, ElideBehavior)
VIEW_BUILDER_PROPERTY(const base::string16&, TooltipText)
VIEW_BUILDER_PROPERTY(bool, HandlesTooltips)
VIEW_BUILDER_PROPERTY(int, MaximumWidth)
VIEW_BUILDER_PROPERTY(bool, CollapseWhenHidden)
VIEW_BUILDER_PROPERTY(bool, Selectable)
END_VIEW_BUILDER

}  // namespace views

DEFINE_VIEW_BUILDER(VIEWS_EXPORT, Label)

#endif  // UI_VIEWS_CONTROLS_LABEL_H_