Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(113)

Side by Side Diff: ui/views/controls/label.cc

Issue 2413223003: Views:: Make Labels support text selection. (Closed)
Patch Set: -- Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/controls/label.h" 5 #include "ui/views/controls/label.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <cmath> 10 #include <cmath>
11 #include <limits> 11 #include <limits>
12 #include <utility> 12 #include <utility>
13 #include <vector> 13 #include <vector>
14 14
15 #include "base/i18n/rtl.h" 15 #include "base/i18n/rtl.h"
16 #include "base/logging.h" 16 #include "base/logging.h"
17 #include "base/profiler/scoped_tracker.h" 17 #include "base/profiler/scoped_tracker.h"
18 #include "base/strings/string_split.h" 18 #include "base/strings/string_split.h"
19 #include "base/strings/utf_string_conversions.h" 19 #include "base/strings/utf_string_conversions.h"
20 #include "ui/accessibility/ax_view_state.h" 20 #include "ui/accessibility/ax_view_state.h"
21 #include "ui/base/clipboard/scoped_clipboard_writer.h"
22 #include "ui/base/cursor/cursor.h"
21 #include "ui/base/default_style.h" 23 #include "ui/base/default_style.h"
22 #include "ui/base/material_design/material_design_controller.h" 24 #include "ui/base/material_design/material_design_controller.h"
23 #include "ui/base/resource/resource_bundle.h" 25 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/canvas.h" 26 #include "ui/gfx/canvas.h"
25 #include "ui/gfx/color_utils.h" 27 #include "ui/gfx/color_utils.h"
26 #include "ui/gfx/geometry/insets.h" 28 #include "ui/gfx/geometry/insets.h"
27 #include "ui/gfx/text_elider.h" 29 #include "ui/gfx/text_elider.h"
28 #include "ui/native_theme/native_theme.h" 30 #include "ui/native_theme/native_theme.h"
31 #include "ui/views/focus/focus_manager.h"
32 #include "ui/views/native_cursor.h"
33 #include "ui/views/selection_controller.h"
29 34
30 namespace views { 35 namespace views {
31 // static 36 // static
32 const char Label::kViewClassName[] = "Label"; 37 const char Label::kViewClassName[] = "Label";
33 const int Label::kFocusBorderPadding = 1; 38 const int Label::kFocusBorderPadding = 1;
34 39
35 Label::Label() : Label(base::string16()) { 40 Label::Label() : Label(base::string16()) {
36 } 41 }
37 42
38 Label::Label(const base::string16& text) : Label(text, GetDefaultFontList()) { 43 Label::Label(const base::string16& text) : Label(text, GetDefaultFontList()) {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 98
94 void Label::SetBackgroundColor(SkColor color) { 99 void Label::SetBackgroundColor(SkColor color) {
95 if (background_color_set_ && background_color_ == color) 100 if (background_color_set_ && background_color_ == color)
96 return; 101 return;
97 is_first_paint_text_ = true; 102 is_first_paint_text_ = true;
98 background_color_ = color; 103 background_color_ = color;
99 background_color_set_ = true; 104 background_color_set_ = true;
100 RecalculateColors(); 105 RecalculateColors();
101 } 106 }
102 107
108 void Label::SetSelectionTextColor(SkColor color) {
109 if (selection_text_color_set_ && requested_selection_text_color_ == color)
110 return;
111 is_first_paint_text_ = true;
112 requested_selection_text_color_ = color;
113 selection_text_color_set_ = true;
114 RecalculateColors();
115 }
116
117 void Label::SetSelectionBackgroundColor(SkColor color) {
118 if (selection_background_color_set_ && selection_background_color_ == color)
119 return;
120 is_first_paint_text_ = true;
121 selection_background_color_ = color;
122 selection_background_color_set_ = true;
123 RecalculateColors();
124 }
125
103 void Label::SetShadows(const gfx::ShadowValues& shadows) { 126 void Label::SetShadows(const gfx::ShadowValues& shadows) {
104 // TODO(mukai): early exit if the specified shadows are same. 127 // TODO(mukai): early exit if the specified shadows are same.
105 is_first_paint_text_ = true; 128 is_first_paint_text_ = true;
106 render_text_->set_shadows(shadows); 129 render_text_->set_shadows(shadows);
107 ResetLayout(); 130 ResetLayout();
108 } 131 }
109 132
110 void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) { 133 void Label::SetSubpixelRenderingEnabled(bool subpixel_rendering_enabled) {
111 if (subpixel_rendering_enabled_ == subpixel_rendering_enabled) 134 if (subpixel_rendering_enabled_ == subpixel_rendering_enabled)
112 return; 135 return;
(...skipping 27 matching lines...) Expand all
140 void Label::SetMultiLine(bool multi_line) { 163 void Label::SetMultiLine(bool multi_line) {
141 DCHECK(!multi_line || (elide_behavior_ == gfx::ELIDE_TAIL || 164 DCHECK(!multi_line || (elide_behavior_ == gfx::ELIDE_TAIL ||
142 elide_behavior_ == gfx::NO_ELIDE)); 165 elide_behavior_ == gfx::NO_ELIDE));
143 if (this->multi_line() == multi_line) 166 if (this->multi_line() == multi_line)
144 return; 167 return;
145 is_first_paint_text_ = true; 168 is_first_paint_text_ = true;
146 multi_line_ = multi_line; 169 multi_line_ = multi_line;
147 if (render_text_->MultilineSupported()) 170 if (render_text_->MultilineSupported())
148 render_text_->SetMultiline(multi_line); 171 render_text_->SetMultiline(multi_line);
149 render_text_->SetReplaceNewlineCharsWithSymbols(!multi_line); 172 render_text_->SetReplaceNewlineCharsWithSymbols(!multi_line);
173 if (multi_line)
174 SetSelectable(false);
150 ResetLayout(); 175 ResetLayout();
151 } 176 }
152 177
153 void Label::SetObscured(bool obscured) { 178 void Label::SetObscured(bool obscured) {
154 if (this->obscured() == obscured) 179 if (this->obscured() == obscured)
155 return; 180 return;
156 is_first_paint_text_ = true; 181 is_first_paint_text_ = true;
157 render_text_->SetObscured(obscured); 182 render_text_->SetObscured(obscured);
158 ResetLayout(); 183 ResetLayout();
159 } 184 }
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 } 222 }
198 223
199 void Label::SetMaximumWidth(int max_width) { 224 void Label::SetMaximumWidth(int max_width) {
200 DCHECK(multi_line()); 225 DCHECK(multi_line());
201 DCHECK_EQ(0, fixed_width_); 226 DCHECK_EQ(0, fixed_width_);
202 max_width_ = max_width; 227 max_width_ = max_width;
203 SizeToPreferredSize(); 228 SizeToPreferredSize();
204 } 229 }
205 230
206 base::string16 Label::GetDisplayTextForTesting() { 231 base::string16 Label::GetDisplayTextForTesting() {
207 lines_.clear(); 232 ClearRenderTextLines();
208 MaybeBuildRenderTextLines(); 233 MaybeBuildRenderTextLines();
209 base::string16 result; 234 base::string16 result;
210 if (lines_.empty()) 235 if (lines_.empty())
211 return result; 236 return result;
212 result.append(lines_[0]->GetDisplayText()); 237 result.append(lines_[0]->GetDisplayText());
213 for (size_t i = 1; i < lines_.size(); ++i) { 238 for (size_t i = 1; i < lines_.size(); ++i) {
214 result.append(1, '\n'); 239 result.append(1, '\n');
215 result.append(lines_[i]->GetDisplayText()); 240 result.append(lines_[i]->GetDisplayText());
216 } 241 }
217 return result; 242 return result;
218 } 243 }
219 244
245 bool Label::IsSelectionSupported() const {
246 return !multi_line() && render_text_->IsSelectionSupported();
247 }
248
249 bool Label::IsSelectable() const {
250 if (selection_controller_) {
251 DCHECK(IsSelectionSupported());
252 return true;
253 }
254 return false;
255 }
256
257 bool Label::SetSelectable(bool value) {
258 if (value == IsSelectable())
259 return true;
260
261 if (!value) {
262 ClearSelection();
263 selection_controller_.reset();
264 return true;
265 }
266
267 if (!IsSelectionSupported())
268 return false;
269
270 selection_controller_.reset(new SelectionController(this));
271
272 // On Linux, update the selection clipboard on a text selection.
273 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
274 selection_controller_->set_handles_selection_clipboard(true);
275 #endif
276
277 return true;
278 }
279
280 bool Label::HasSelection() const {
281 const gfx::RenderText* render_text = GetRenderTextForSelectionController();
282 return render_text ? !render_text->selection().is_empty() : false;
283 }
284
285 void Label::SelectAll() {
286 gfx::RenderText* render_text = GetRenderTextForSelectionController();
287 if (!render_text)
288 return;
289 render_text->SelectAll(false);
290 SchedulePaint();
291 }
292
293 void Label::ClearSelection() {
294 gfx::RenderText* render_text = GetRenderTextForSelectionController();
295 if (!render_text)
296 return;
297 render_text->ClearSelection();
298 SchedulePaint();
299 }
300
301 void Label::SelectRange(const gfx::Range& range) {
302 gfx::RenderText* render_text = GetRenderTextForSelectionController();
303 if (render_text && render_text->SelectRange(range))
304 SchedulePaint();
305 }
306
220 gfx::Insets Label::GetInsets() const { 307 gfx::Insets Label::GetInsets() const {
221 gfx::Insets insets = View::GetInsets(); 308 gfx::Insets insets = View::GetInsets();
222 if (focus_behavior() != FocusBehavior::NEVER) { 309 if (focus_behavior() != FocusBehavior::NEVER) {
223 insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding, 310 insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
224 kFocusBorderPadding, kFocusBorderPadding); 311 kFocusBorderPadding, kFocusBorderPadding);
225 } 312 }
226 return insets; 313 return insets;
227 } 314 }
228 315
229 int Label::GetBaseline() const { 316 int Label::GetBaseline() const {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
289 height = render_text_->GetStringSize().height(); 376 height = render_text_->GetStringSize().height();
290 } else { 377 } else {
291 std::vector<base::string16> lines = GetLinesForWidth(w); 378 std::vector<base::string16> lines = GetLinesForWidth(w);
292 height = lines.size() * std::max(line_height(), font_list().GetHeight()); 379 height = lines.size() * std::max(line_height(), font_list().GetHeight());
293 } 380 }
294 height -= gfx::ShadowValue::GetMargin(render_text_->shadows()).height(); 381 height -= gfx::ShadowValue::GetMargin(render_text_->shadows()).height();
295 return height + GetInsets().height(); 382 return height + GetInsets().height();
296 } 383 }
297 384
298 void Label::Layout() { 385 void Label::Layout() {
299 lines_.clear(); 386 ClearRenderTextLines();
300 } 387 }
301 388
302 const char* Label::GetClassName() const { 389 const char* Label::GetClassName() const {
303 return kViewClassName; 390 return kViewClassName;
304 } 391 }
305 392
306 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) { 393 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) {
307 if (!handles_tooltips_ || 394 if (!handles_tooltips_ ||
308 (tooltip_text_.empty() && !ShouldShowDefaultTooltip())) 395 (tooltip_text_.empty() && !ShouldShowDefaultTooltip()))
309 return NULL; 396 return NULL;
310 397
311 return HitTestPoint(point) ? this : NULL; 398 return HitTestPoint(point) ? this : NULL;
312 } 399 }
313 400
314 bool Label::CanProcessEventsWithinSubtree() const { 401 bool Label::CanProcessEventsWithinSubtree() const {
315 // Send events to the parent view for handling. 402 return !!GetRenderTextForSelectionController();
316 return false;
317 } 403 }
318 404
319 void Label::GetAccessibleState(ui::AXViewState* state) { 405 void Label::GetAccessibleState(ui::AXViewState* state) {
320 state->role = ui::AX_ROLE_STATIC_TEXT; 406 state->role = ui::AX_ROLE_STATIC_TEXT;
321 state->AddStateFlag(ui::AX_STATE_READ_ONLY); 407 state->AddStateFlag(ui::AX_STATE_READ_ONLY);
322 // Note that |render_text_| is never elided (see the comment in Init() too). 408 // Note that |render_text_| is never elided (see the comment in Init() too).
323 state->name = render_text_->GetDisplayText(); 409 state->name = render_text_->GetDisplayText();
324 } 410 }
325 411
326 bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { 412 bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const {
(...skipping 16 matching lines...) Expand all
343 429
344 void Label::OnEnabledChanged() { 430 void Label::OnEnabledChanged() {
345 ApplyTextColors(); 431 ApplyTextColors();
346 View::OnEnabledChanged(); 432 View::OnEnabledChanged();
347 } 433 }
348 434
349 std::unique_ptr<gfx::RenderText> Label::CreateRenderText( 435 std::unique_ptr<gfx::RenderText> Label::CreateRenderText(
350 const base::string16& text, 436 const base::string16& text,
351 gfx::HorizontalAlignment alignment, 437 gfx::HorizontalAlignment alignment,
352 gfx::DirectionalityMode directionality, 438 gfx::DirectionalityMode directionality,
353 gfx::ElideBehavior elide_behavior) { 439 gfx::ElideBehavior elide_behavior) const {
354 std::unique_ptr<gfx::RenderText> render_text( 440 std::unique_ptr<gfx::RenderText> render_text(
355 render_text_->CreateInstanceOfSameType()); 441 render_text_->CreateInstanceOfSameType());
356 render_text->SetHorizontalAlignment(alignment); 442 render_text->SetHorizontalAlignment(alignment);
357 render_text->SetDirectionalityMode(directionality); 443 render_text->SetDirectionalityMode(directionality);
358 render_text->SetElideBehavior(elide_behavior); 444 render_text->SetElideBehavior(elide_behavior);
359 render_text->SetObscured(obscured()); 445 render_text->SetObscured(obscured());
360 render_text->SetMinLineHeight(line_height()); 446 render_text->SetMinLineHeight(line_height());
361 render_text->SetFontList(font_list()); 447 render_text->SetFontList(font_list());
362 render_text->set_shadows(shadows()); 448 render_text->set_shadows(shadows());
363 render_text->SetCursorEnabled(false); 449 render_text->SetCursorEnabled(false);
(...skipping 18 matching lines...) Expand all
382 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is 468 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is
383 // fixed. 469 // fixed.
384 tracked_objects::ScopedTracker tracking_profile( 470 tracked_objects::ScopedTracker tracking_profile(
385 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()")); 471 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()"));
386 472
387 is_first_paint_text_ = false; 473 is_first_paint_text_ = false;
388 PaintText(canvas); 474 PaintText(canvas);
389 } else { 475 } else {
390 PaintText(canvas); 476 PaintText(canvas);
391 } 477 }
392 if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial()) 478
479 // Check for IsAccessibilityFocusable() to prevent drawing a focus rect for
480 // non-focusable labels with selection, which are given focus explicitly in
481 // OnMousePressed.
482 if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial() &&
483 IsAccessibilityFocusable()) {
393 canvas->DrawFocusRect(GetFocusBounds()); 484 canvas->DrawFocusRect(GetFocusBounds());
485 }
394 } 486 }
395 487
396 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { 488 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) {
397 UpdateColorsFromTheme(theme); 489 UpdateColorsFromTheme(theme);
398 } 490 }
399 491
492 gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) {
493 return GetRenderTextForSelectionController() ? GetNativeIBeamCursor()
msw 2016/10/28 06:30:26 nit: should this just check IsSelectable?
karandeepb 2016/11/01 11:06:25 So GetRenderTextForSelectionController() may still
494 : gfx::kNullCursor;
495 }
496
497 void Label::OnFocus() {
498 gfx::RenderText* render_text = GetRenderTextForSelectionController();
499 if (render_text) {
500 render_text->set_focused(true);
501 SchedulePaint();
502 }
503 View::OnFocus();
504 }
505
506 void Label::OnBlur() {
507 gfx::RenderText* render_text = GetRenderTextForSelectionController();
508 if (render_text) {
509 render_text->set_focused(false);
510 SchedulePaint();
511 }
512 View::OnBlur();
513 }
514
515 bool Label::OnMousePressed(const ui::MouseEvent& event) {
516 if(!GetRenderTextForSelectionController())
msw 2016/10/28 06:30:26 nit: space after if; ditto elsewhere below, run 'g
karandeepb 2016/11/01 11:06:25 Done.
517 return false;
518
519 // RequestFocus() won't work when the label has FocusBehavior::NEVER. Hence
520 // explicitly set the focused view.
521 // TODO(karandeepb): If a widget with a label having FocusBehavior::NEVER as
karandeepb 2016/10/27 07:31:54 Still need to find a workaround for this. Also, no
msw 2016/10/28 06:30:26 I think this is probably fine as a temporary solut
karandeepb 2016/11/01 11:06:25 Done. Will use the label text selection bug to tra
522 // the currently focused view (due to selection) was to lose focus, focus
523 // won't be restored to the label (and hence a text selection won't be drawn)
524 // when the widget gets focus again. Fix this.
525 if ((event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) &&
526 GetFocusManager()) {
527 GetFocusManager()->SetFocusedView(this);
528 }
529
530 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
531 if (event.IsOnlyMiddleMouseButton() && GetFocusManager())
532 GetFocusManager()->SetFocusedView(this);
533 #endif
534
535 return selection_controller_->OnMousePressed(event, false);
536 }
537
538 bool Label::OnMouseDragged(const ui::MouseEvent& event) {
msw 2016/10/28 06:30:26 aside: Making SelectionController a pre-target eve
karandeepb 2016/11/01 11:06:25 As pointed out in the prior CL, making SelectionCo
msw 2016/11/01 23:42:21 Yeah, these would be good tasks to pursue, I'm in
539 if(!GetRenderTextForSelectionController())
540 return false;
541
542 return selection_controller_->OnMouseDragged(event);
543 }
544
545 void Label::OnMouseReleased(const ui::MouseEvent& event) {
546 if(!GetRenderTextForSelectionController())
547 return;
548
549 selection_controller_->OnMouseReleased(event);
550 }
551
552 void Label::OnMouseCaptureLost() {
553 if(!GetRenderTextForSelectionController())
554 return;
555
556 selection_controller_->OnMouseCaptureLost();
557 }
558
400 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) { 559 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) {
401 View::OnDeviceScaleFactorChanged(device_scale_factor); 560 View::OnDeviceScaleFactorChanged(device_scale_factor);
402 // When the device scale factor is changed, some font rendering parameters is 561 // When the device scale factor is changed, some font rendering parameters is
403 // changed (especially, hinting). The bounding box of the text has to be 562 // changed (especially, hinting). The bounding box of the text has to be
404 // re-computed based on the new parameters. See crbug.com/441439 563 // re-computed based on the new parameters. See crbug.com/441439
405 ResetLayout(); 564 ResetLayout();
406 } 565 }
407 566
408 void Label::VisibilityChanged(View* starting_from, bool is_visible) { 567 void Label::VisibilityChanged(View* starting_from, bool is_visible) {
409 if (!is_visible) 568 if (!is_visible)
410 lines_.clear(); 569 ClearRenderTextLines();
570 }
571
572 gfx::RenderText* Label::GetRenderTextForSelectionController() {
573 return const_cast<gfx::RenderText*>(
574 static_cast<const Label*>(this)->GetRenderTextForSelectionController());
575 }
576
577 bool Label::IsReadOnly() const {
578 return true;
579 }
580
581 bool Label::SupportsDrag() const {
582 return false;
583 }
584
585 bool Label::HasTextBeingDragged() const {
586 return false;
587 }
588
589 void Label::SetTextBeingDragged(bool value) {
590 NOTREACHED();
591 }
592
593 int Label::GetViewHeight() const {
594 return height();
595 }
596
597 int Label::GetViewWidth() const {
598 return width();
599 }
600
601 int Label::GetDragSelectionDelay() const {
602 // Labels don't need to use a repeating timer to update the drag selection.
603 // Since the cursor is disabled for labels, a selection outside the display
604 // area won't change the text in the display area. It is expected that all the
605 // text will fit in the display area for labels anyway.
606 return 0;
607 }
608
609 void Label::OnBeforePointerAction() {}
610
611 void Label::OnAfterPointerAction(bool text_changed, bool selection_changed) {
612 DCHECK(!text_changed);
613 if (selection_changed)
614 SchedulePaint();
615 }
616
617 bool Label::PasteSelectionClipboard() {
618 NOTREACHED();
619 return false;
620 }
621
622 void Label::UpdateSelectionClipboard() {
623 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
624 gfx::RenderText* render_text = GetRenderTextForSelectionController();
625 DCHECK(render_text);
626 if (!obscured()) {
msw 2016/10/28 06:30:26 nit: do this before getting |render_text|
karandeepb 2016/11/01 11:06:25 Obsolete.
627 const base::string16 selected_text =
628 render_text->GetTextFromRange(render_text->selection());
629 ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION)
630 .WriteText(selected_text);
631 }
632 #endif
633 }
634
635 const gfx::RenderText* Label::GetRenderTextForSelectionController() const {
636 if (!IsSelectable())
637 return nullptr;
638 MaybeBuildRenderTextLines();
639
640 // This may happen when the content bounds of the view are empty.
641 if (lines_.empty())
642 return nullptr;
643
644 DCHECK_EQ(1u, lines_.size());
645 return lines_[0].get();
411 } 646 }
412 647
413 void Label::Init(const base::string16& text, const gfx::FontList& font_list) { 648 void Label::Init(const base::string16& text, const gfx::FontList& font_list) {
414 render_text_.reset(gfx::RenderText::CreateInstance()); 649 render_text_.reset(gfx::RenderText::CreateInstance());
415 render_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 650 render_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
416 render_text_->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); 651 render_text_->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT);
417 // NOTE: |render_text_| should not be elided at all. This is used to keep some 652 // NOTE: |render_text_| should not be elided at all. This is used to keep some
418 // properties and to compute the size of the string. 653 // properties and to compute the size of the string.
419 render_text_->SetElideBehavior(gfx::NO_ELIDE); 654 render_text_->SetElideBehavior(gfx::NO_ELIDE);
420 render_text_->SetFontList(font_list); 655 render_text_->SetFontList(font_list);
421 render_text_->SetCursorEnabled(false); 656 render_text_->SetCursorEnabled(false);
422 render_text_->SetWordWrapBehavior(gfx::TRUNCATE_LONG_WORDS); 657 render_text_->SetWordWrapBehavior(gfx::TRUNCATE_LONG_WORDS);
423 658
424 elide_behavior_ = gfx::ELIDE_TAIL; 659 elide_behavior_ = gfx::ELIDE_TAIL;
425 enabled_color_set_ = disabled_color_set_ = background_color_set_ = false; 660 enabled_color_set_ = disabled_color_set_ = background_color_set_ = false;
661 selection_text_color_set_ = selection_background_color_set_ = false;
426 subpixel_rendering_enabled_ = true; 662 subpixel_rendering_enabled_ = true;
427 auto_color_readability_ = true; 663 auto_color_readability_ = true;
428 multi_line_ = false; 664 multi_line_ = false;
429 UpdateColorsFromTheme(GetNativeTheme()); 665 UpdateColorsFromTheme(GetNativeTheme());
430 handles_tooltips_ = true; 666 handles_tooltips_ = true;
431 collapse_when_hidden_ = false; 667 collapse_when_hidden_ = false;
432 fixed_width_ = 0; 668 fixed_width_ = 0;
433 max_width_ = 0; 669 max_width_ = 0;
434 is_first_paint_text_ = true; 670 is_first_paint_text_ = true;
435 SetText(text); 671 SetText(text);
436 } 672 }
437 673
438 void Label::ResetLayout() { 674 void Label::ResetLayout() {
439 InvalidateLayout(); 675 InvalidateLayout();
440 PreferredSizeChanged(); 676 PreferredSizeChanged();
441 SchedulePaint(); 677 SchedulePaint();
442 lines_.clear(); 678 ClearRenderTextLines();
443 } 679 }
444 680
445 void Label::MaybeBuildRenderTextLines() { 681 void Label::MaybeBuildRenderTextLines() const {
446 if (!lines_.empty()) 682 if (!lines_.empty())
447 return; 683 return;
448 684
449 gfx::Rect rect = GetContentsBounds(); 685 gfx::Rect rect = GetContentsBounds();
450 if (focus_behavior() != FocusBehavior::NEVER) 686 if (focus_behavior() != FocusBehavior::NEVER)
451 rect.Inset(kFocusBorderPadding, kFocusBorderPadding); 687 rect.Inset(kFocusBorderPadding, kFocusBorderPadding);
452 if (rect.IsEmpty()) 688 if (rect.IsEmpty())
453 return; 689 return;
454 rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); 690 rect.Inset(-gfx::ShadowValue::GetMargin(shadows()));
455 691
(...skipping 12 matching lines...) Expand all
468 // Text eliding is not supported for multi-lined Labels. 704 // Text eliding is not supported for multi-lined Labels.
469 // TODO(mukai): Add multi-lined elided text support. 705 // TODO(mukai): Add multi-lined elided text support.
470 gfx::ElideBehavior elide_behavior = 706 gfx::ElideBehavior elide_behavior =
471 multi_line() ? gfx::NO_ELIDE : elide_behavior_; 707 multi_line() ? gfx::NO_ELIDE : elide_behavior_;
472 if (!multi_line() || render_text_->MultilineSupported()) { 708 if (!multi_line() || render_text_->MultilineSupported()) {
473 std::unique_ptr<gfx::RenderText> render_text = 709 std::unique_ptr<gfx::RenderText> render_text =
474 CreateRenderText(text(), alignment, directionality, elide_behavior); 710 CreateRenderText(text(), alignment, directionality, elide_behavior);
475 render_text->SetDisplayRect(rect); 711 render_text->SetDisplayRect(rect);
476 render_text->SetMultiline(multi_line()); 712 render_text->SetMultiline(multi_line());
477 render_text->SetWordWrapBehavior(render_text_->word_wrap_behavior()); 713 render_text->SetWordWrapBehavior(render_text_->word_wrap_behavior());
714
715 // Setup render text for selection controller.
716 if (IsSelectable()) {
717 render_text->set_focused(HasFocus());
718 if (last_selection_range_)
719 render_text->SelectRange(last_selection_range_.value());
720 }
721
478 lines_.push_back(std::move(render_text)); 722 lines_.push_back(std::move(render_text));
479 } else { 723 } else {
480 std::vector<base::string16> lines = GetLinesForWidth(rect.width()); 724 std::vector<base::string16> lines = GetLinesForWidth(rect.width());
481 if (lines.size() > 1) 725 if (lines.size() > 1)
482 rect.set_height(std::max(line_height(), font_list().GetHeight())); 726 rect.set_height(std::max(line_height(), font_list().GetHeight()));
483 727
484 const int bottom = GetContentsBounds().bottom(); 728 const int bottom = GetContentsBounds().bottom();
485 for (size_t i = 0; i < lines.size() && rect.y() <= bottom; ++i) { 729 for (size_t i = 0; i < lines.size() && rect.y() <= bottom; ++i) {
486 std::unique_ptr<gfx::RenderText> line = 730 std::unique_ptr<gfx::RenderText> line =
487 CreateRenderText(lines[i], alignment, directionality, elide_behavior); 731 CreateRenderText(lines[i], alignment, directionality, elide_behavior);
488 line->SetDisplayRect(rect); 732 line->SetDisplayRect(rect);
489 lines_.push_back(std::move(line)); 733 lines_.push_back(std::move(line));
490 rect.set_y(rect.y() + rect.height()); 734 rect.set_y(rect.y() + rect.height());
491 } 735 }
492 // Append the remaining text to the last visible line. 736 // Append the remaining text to the last visible line.
493 for (size_t i = lines_.size(); i < lines.size(); ++i) 737 for (size_t i = lines_.size(); i < lines.size(); ++i)
494 lines_.back()->SetText(lines_.back()->text() + lines[i]); 738 lines_.back()->SetText(lines_.back()->text() + lines[i]);
495 } 739 }
740
741 last_selection_range_.reset();
496 ApplyTextColors(); 742 ApplyTextColors();
497 } 743 }
498 744
499 gfx::Rect Label::GetFocusBounds() { 745 gfx::Rect Label::GetFocusBounds() const {
500 MaybeBuildRenderTextLines(); 746 MaybeBuildRenderTextLines();
501 747
502 gfx::Rect focus_bounds; 748 gfx::Rect focus_bounds;
503 if (lines_.empty()) { 749 if (lines_.empty()) {
504 focus_bounds = gfx::Rect(GetTextSize()); 750 focus_bounds = gfx::Rect(GetTextSize());
505 } else { 751 } else {
506 for (size_t i = 0; i < lines_.size(); ++i) { 752 for (size_t i = 0; i < lines_.size(); ++i) {
507 gfx::Point origin; 753 gfx::Point origin;
508 origin += lines_[i]->GetLineOffset(0); 754 origin += lines_[i]->GetLineOffset(0);
509 focus_bounds.Union(gfx::Rect(origin, lines_[i]->GetStringSize())); 755 focus_bounds.Union(gfx::Rect(origin, lines_[i]->GetStringSize()));
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
564 810
565 void Label::RecalculateColors() { 811 void Label::RecalculateColors() {
566 actual_enabled_color_ = auto_color_readability_ ? 812 actual_enabled_color_ = auto_color_readability_ ?
567 color_utils::GetReadableColor(requested_enabled_color_, 813 color_utils::GetReadableColor(requested_enabled_color_,
568 background_color_) : 814 background_color_) :
569 requested_enabled_color_; 815 requested_enabled_color_;
570 actual_disabled_color_ = auto_color_readability_ ? 816 actual_disabled_color_ = auto_color_readability_ ?
571 color_utils::GetReadableColor(requested_disabled_color_, 817 color_utils::GetReadableColor(requested_disabled_color_,
572 background_color_) : 818 background_color_) :
573 requested_disabled_color_; 819 requested_disabled_color_;
820 actual_selection_text_color_ =
821 auto_color_readability_
822 ? color_utils::GetReadableColor(requested_selection_text_color_,
823 selection_background_color_)
824 : requested_selection_text_color_;
574 825
575 ApplyTextColors(); 826 ApplyTextColors();
576 SchedulePaint(); 827 SchedulePaint();
577 } 828 }
578 829
579 void Label::ApplyTextColors() { 830 void Label::ApplyTextColors() const {
580 SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_; 831 SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_;
581 bool subpixel_rendering_suppressed = 832 bool subpixel_rendering_suppressed =
582 SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_; 833 SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_;
583 for (size_t i = 0; i < lines_.size(); ++i) { 834 for (size_t i = 0; i < lines_.size(); ++i) {
584 lines_[i]->SetColor(color); 835 lines_[i]->SetColor(color);
836 lines_[i]->set_selection_color(actual_selection_text_color_);
837 lines_[i]->set_selection_background_focused_color(
838 selection_background_color_);
585 lines_[i]->set_subpixel_rendering_suppressed(subpixel_rendering_suppressed); 839 lines_[i]->set_subpixel_rendering_suppressed(subpixel_rendering_suppressed);
586 } 840 }
587 } 841 }
588 842
589 void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) { 843 void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) {
590 if (!enabled_color_set_) { 844 if (!enabled_color_set_) {
591 requested_enabled_color_ = theme->GetSystemColor( 845 requested_enabled_color_ = theme->GetSystemColor(
592 ui::NativeTheme::kColorId_LabelEnabledColor); 846 ui::NativeTheme::kColorId_LabelEnabledColor);
593 } 847 }
594 if (!disabled_color_set_) { 848 if (!disabled_color_set_) {
595 requested_disabled_color_ = theme->GetSystemColor( 849 requested_disabled_color_ = theme->GetSystemColor(
596 ui::NativeTheme::kColorId_LabelDisabledColor); 850 ui::NativeTheme::kColorId_LabelDisabledColor);
597 } 851 }
598 if (!background_color_set_) { 852 if (!background_color_set_) {
599 background_color_ = 853 background_color_ =
600 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); 854 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
601 } 855 }
856 if (!selection_text_color_set_) {
857 requested_selection_text_color_ = theme->GetSystemColor(
858 ui::NativeTheme::kColorId_LabelTextSelectionColor);
859 }
860 if (!selection_background_color_set_) {
861 selection_background_color_ = theme->GetSystemColor(
862 ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused);
863 }
602 RecalculateColors(); 864 RecalculateColors();
603 } 865 }
604 866
605 bool Label::ShouldShowDefaultTooltip() const { 867 bool Label::ShouldShowDefaultTooltip() const {
606 const gfx::Size text_size = GetTextSize(); 868 const gfx::Size text_size = GetTextSize();
607 const gfx::Size size = GetContentsBounds().size(); 869 const gfx::Size size = GetContentsBounds().size();
608 return !obscured() && (text_size.width() > size.width() || 870 return !obscured() && (text_size.width() > size.width() ||
609 (multi_line() && text_size.height() > size.height())); 871 (multi_line() && text_size.height() > size.height()));
610 } 872 }
611 873
874 void Label::ClearRenderTextLines() const {
875 // Persist the selection range if there is an active selection.
876 if (HasSelection())
877 last_selection_range_ = GetRenderTextForSelectionController()->selection();
878 lines_.clear();
879 }
880
612 } // namespace views 881 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698