Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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::SetSelectable(bool value) { | |
| 250 if (value == selectable()) | |
| 251 return true; | |
| 252 | |
| 253 if (!value) { | |
| 254 ClearSelection(); | |
| 255 selection_controller_.reset(); | |
| 256 return true; | |
| 257 } | |
| 258 | |
| 259 if (!IsSelectionSupported()) | |
| 260 return false; | |
| 261 | |
| 262 selection_controller_.reset(new SelectionController(this)); | |
|
sky
2016/11/03 03:23:20
MakeUnique.
karandeepb
2016/11/03 10:38:56
Done.
| |
| 263 | |
| 264 // On Linux, update the selection clipboard on a text selection. | |
|
sky
2016/11/03 03:23:20
It seems like this should be the default, meaning
karandeepb
2016/11/03 10:38:56
Moved to the SelectionController constructor.
| |
| 265 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
| 266 selection_controller_->set_handles_selection_clipboard(true); | |
| 267 #endif | |
| 268 | |
| 269 return true; | |
| 270 } | |
| 271 | |
| 272 bool Label::HasSelection() const { | |
| 273 const gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 274 return render_text ? !render_text->selection().is_empty() : false; | |
| 275 } | |
| 276 | |
| 277 void Label::SelectAll() { | |
| 278 gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 279 if (!render_text) | |
| 280 return; | |
| 281 render_text->SelectAll(false); | |
| 282 SchedulePaint(); | |
| 283 } | |
| 284 | |
| 285 void Label::ClearSelection() { | |
| 286 gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 287 if (!render_text) | |
| 288 return; | |
| 289 render_text->ClearSelection(); | |
| 290 SchedulePaint(); | |
| 291 } | |
| 292 | |
| 293 void Label::SelectRange(const gfx::Range& range) { | |
| 294 gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 295 if (render_text && render_text->SelectRange(range)) | |
| 296 SchedulePaint(); | |
| 297 } | |
| 298 | |
| 220 gfx::Insets Label::GetInsets() const { | 299 gfx::Insets Label::GetInsets() const { |
| 221 gfx::Insets insets = View::GetInsets(); | 300 gfx::Insets insets = View::GetInsets(); |
| 222 if (focus_behavior() != FocusBehavior::NEVER) { | 301 if (focus_behavior() != FocusBehavior::NEVER) { |
| 223 insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding, | 302 insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding, |
| 224 kFocusBorderPadding, kFocusBorderPadding); | 303 kFocusBorderPadding, kFocusBorderPadding); |
| 225 } | 304 } |
| 226 return insets; | 305 return insets; |
| 227 } | 306 } |
| 228 | 307 |
| 229 int Label::GetBaseline() const { | 308 int Label::GetBaseline() const { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 289 height = render_text_->GetStringSize().height(); | 368 height = render_text_->GetStringSize().height(); |
| 290 } else { | 369 } else { |
| 291 std::vector<base::string16> lines = GetLinesForWidth(w); | 370 std::vector<base::string16> lines = GetLinesForWidth(w); |
| 292 height = lines.size() * std::max(line_height(), font_list().GetHeight()); | 371 height = lines.size() * std::max(line_height(), font_list().GetHeight()); |
| 293 } | 372 } |
| 294 height -= gfx::ShadowValue::GetMargin(render_text_->shadows()).height(); | 373 height -= gfx::ShadowValue::GetMargin(render_text_->shadows()).height(); |
| 295 return height + GetInsets().height(); | 374 return height + GetInsets().height(); |
| 296 } | 375 } |
| 297 | 376 |
| 298 void Label::Layout() { | 377 void Label::Layout() { |
| 299 lines_.clear(); | 378 ClearRenderTextLines(); |
| 300 } | 379 } |
| 301 | 380 |
| 302 const char* Label::GetClassName() const { | 381 const char* Label::GetClassName() const { |
| 303 return kViewClassName; | 382 return kViewClassName; |
| 304 } | 383 } |
| 305 | 384 |
| 306 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) { | 385 View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) { |
| 307 if (!handles_tooltips_ || | 386 if (!handles_tooltips_ || |
| 308 (tooltip_text_.empty() && !ShouldShowDefaultTooltip())) | 387 (tooltip_text_.empty() && !ShouldShowDefaultTooltip())) |
| 309 return NULL; | 388 return NULL; |
| 310 | 389 |
| 311 return HitTestPoint(point) ? this : NULL; | 390 return HitTestPoint(point) ? this : NULL; |
| 312 } | 391 } |
| 313 | 392 |
| 314 bool Label::CanProcessEventsWithinSubtree() const { | 393 bool Label::CanProcessEventsWithinSubtree() const { |
| 315 // Send events to the parent view for handling. | 394 return !!GetRenderTextForSelectionController(); |
| 316 return false; | |
| 317 } | 395 } |
| 318 | 396 |
| 319 void Label::GetAccessibleState(ui::AXViewState* state) { | 397 void Label::GetAccessibleState(ui::AXViewState* state) { |
| 320 state->role = ui::AX_ROLE_STATIC_TEXT; | 398 state->role = ui::AX_ROLE_STATIC_TEXT; |
| 321 state->AddStateFlag(ui::AX_STATE_READ_ONLY); | 399 state->AddStateFlag(ui::AX_STATE_READ_ONLY); |
| 322 // Note that |render_text_| is never elided (see the comment in Init() too). | 400 // Note that |render_text_| is never elided (see the comment in Init() too). |
| 323 state->name = render_text_->GetDisplayText(); | 401 state->name = render_text_->GetDisplayText(); |
| 324 } | 402 } |
| 325 | 403 |
| 326 bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { | 404 bool Label::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 343 | 421 |
| 344 void Label::OnEnabledChanged() { | 422 void Label::OnEnabledChanged() { |
| 345 ApplyTextColors(); | 423 ApplyTextColors(); |
| 346 View::OnEnabledChanged(); | 424 View::OnEnabledChanged(); |
| 347 } | 425 } |
| 348 | 426 |
| 349 std::unique_ptr<gfx::RenderText> Label::CreateRenderText( | 427 std::unique_ptr<gfx::RenderText> Label::CreateRenderText( |
| 350 const base::string16& text, | 428 const base::string16& text, |
| 351 gfx::HorizontalAlignment alignment, | 429 gfx::HorizontalAlignment alignment, |
| 352 gfx::DirectionalityMode directionality, | 430 gfx::DirectionalityMode directionality, |
| 353 gfx::ElideBehavior elide_behavior) { | 431 gfx::ElideBehavior elide_behavior) const { |
| 354 std::unique_ptr<gfx::RenderText> render_text( | 432 std::unique_ptr<gfx::RenderText> render_text( |
| 355 render_text_->CreateInstanceOfSameType()); | 433 render_text_->CreateInstanceOfSameType()); |
| 356 render_text->SetHorizontalAlignment(alignment); | 434 render_text->SetHorizontalAlignment(alignment); |
| 357 render_text->SetDirectionalityMode(directionality); | 435 render_text->SetDirectionalityMode(directionality); |
| 358 render_text->SetElideBehavior(elide_behavior); | 436 render_text->SetElideBehavior(elide_behavior); |
| 359 render_text->SetObscured(obscured()); | 437 render_text->SetObscured(obscured()); |
| 360 render_text->SetMinLineHeight(line_height()); | 438 render_text->SetMinLineHeight(line_height()); |
| 361 render_text->SetFontList(font_list()); | 439 render_text->SetFontList(font_list()); |
| 362 render_text->set_shadows(shadows()); | 440 render_text->set_shadows(shadows()); |
| 363 render_text->SetCursorEnabled(false); | 441 render_text->SetCursorEnabled(false); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 382 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is | 460 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is |
| 383 // fixed. | 461 // fixed. |
| 384 tracked_objects::ScopedTracker tracking_profile( | 462 tracked_objects::ScopedTracker tracking_profile( |
| 385 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()")); | 463 FROM_HERE_WITH_EXPLICIT_FUNCTION("441028 First PaintText()")); |
| 386 | 464 |
| 387 is_first_paint_text_ = false; | 465 is_first_paint_text_ = false; |
| 388 PaintText(canvas); | 466 PaintText(canvas); |
| 389 } else { | 467 } else { |
| 390 PaintText(canvas); | 468 PaintText(canvas); |
| 391 } | 469 } |
| 392 if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial()) | 470 |
| 471 // Check for IsAccessibilityFocusable() to prevent drawing a focus rect for | |
| 472 // non-focusable labels with selection, which are given focus explicitly in | |
| 473 // OnMousePressed. | |
| 474 if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial() && | |
| 475 IsAccessibilityFocusable()) { | |
| 393 canvas->DrawFocusRect(GetFocusBounds()); | 476 canvas->DrawFocusRect(GetFocusBounds()); |
| 477 } | |
| 394 } | 478 } |
| 395 | 479 |
| 396 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { | 480 void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| 397 UpdateColorsFromTheme(theme); | 481 UpdateColorsFromTheme(theme); |
| 398 } | 482 } |
| 399 | 483 |
| 484 gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) { | |
| 485 return GetRenderTextForSelectionController() ? GetNativeIBeamCursor() | |
| 486 : gfx::kNullCursor; | |
| 487 } | |
| 488 | |
| 489 void Label::OnFocus() { | |
| 490 gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 491 if (render_text) { | |
| 492 render_text->set_focused(true); | |
| 493 SchedulePaint(); | |
| 494 } | |
| 495 View::OnFocus(); | |
| 496 } | |
| 497 | |
| 498 void Label::OnBlur() { | |
| 499 gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 500 if (render_text) { | |
| 501 render_text->set_focused(false); | |
| 502 SchedulePaint(); | |
| 503 } | |
| 504 View::OnBlur(); | |
| 505 } | |
| 506 | |
| 507 bool Label::OnMousePressed(const ui::MouseEvent& event) { | |
| 508 if (!GetRenderTextForSelectionController()) | |
| 509 return false; | |
| 510 | |
| 511 // RequestFocus() won't work when the label has FocusBehavior::NEVER. Hence | |
| 512 // explicitly set the focused view. | |
| 513 // TODO(karandeepb): If a widget with a label having FocusBehavior::NEVER as | |
| 514 // the currently focused view (due to selection) was to lose focus, focus | |
| 515 // won't be restored to the label (and hence a text selection won't be drawn) | |
| 516 // when the widget gets focus again. Fix this. | |
| 517 // Tracked in https://crbug.com/630365. | |
| 518 if ((event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) && | |
| 519 GetFocusManager()) { | |
| 520 GetFocusManager()->SetFocusedView(this); | |
| 521 } | |
| 522 | |
| 523 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
| 524 if (event.IsOnlyMiddleMouseButton() && GetFocusManager()) | |
| 525 GetFocusManager()->SetFocusedView(this); | |
| 526 #endif | |
| 527 | |
| 528 return selection_controller_->OnMousePressed(event, false); | |
| 529 } | |
| 530 | |
| 531 bool Label::OnMouseDragged(const ui::MouseEvent& event) { | |
| 532 if (!GetRenderTextForSelectionController()) | |
| 533 return false; | |
| 534 | |
| 535 return selection_controller_->OnMouseDragged(event); | |
| 536 } | |
| 537 | |
| 538 void Label::OnMouseReleased(const ui::MouseEvent& event) { | |
| 539 if (!GetRenderTextForSelectionController()) | |
| 540 return; | |
| 541 | |
| 542 selection_controller_->OnMouseReleased(event); | |
| 543 } | |
| 544 | |
| 545 void Label::OnMouseCaptureLost() { | |
| 546 if (!GetRenderTextForSelectionController()) | |
| 547 return; | |
| 548 | |
| 549 selection_controller_->OnMouseCaptureLost(); | |
| 550 } | |
| 551 | |
| 400 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) { | 552 void Label::OnDeviceScaleFactorChanged(float device_scale_factor) { |
| 401 View::OnDeviceScaleFactorChanged(device_scale_factor); | 553 View::OnDeviceScaleFactorChanged(device_scale_factor); |
| 402 // When the device scale factor is changed, some font rendering parameters is | 554 // 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 | 555 // changed (especially, hinting). The bounding box of the text has to be |
| 404 // re-computed based on the new parameters. See crbug.com/441439 | 556 // re-computed based on the new parameters. See crbug.com/441439 |
| 405 ResetLayout(); | 557 ResetLayout(); |
| 406 } | 558 } |
| 407 | 559 |
| 408 void Label::VisibilityChanged(View* starting_from, bool is_visible) { | 560 void Label::VisibilityChanged(View* starting_from, bool is_visible) { |
| 409 if (!is_visible) | 561 if (!is_visible) |
| 410 lines_.clear(); | 562 ClearRenderTextLines(); |
| 563 } | |
| 564 | |
| 565 gfx::RenderText* Label::GetRenderTextForSelectionController() { | |
| 566 return const_cast<gfx::RenderText*>( | |
| 567 static_cast<const Label*>(this)->GetRenderTextForSelectionController()); | |
| 568 } | |
| 569 | |
| 570 bool Label::IsReadOnly() const { | |
| 571 return true; | |
| 572 } | |
| 573 | |
| 574 bool Label::SupportsDrag() const { | |
| 575 // TODO(crbug.com/661379): Labels should support dragging selected text. | |
| 576 return false; | |
| 577 } | |
| 578 | |
| 579 bool Label::HasTextBeingDragged() const { | |
| 580 return false; | |
| 581 } | |
| 582 | |
| 583 void Label::SetTextBeingDragged(bool value) { | |
| 584 NOTREACHED(); | |
| 585 } | |
| 586 | |
| 587 int Label::GetViewHeight() const { | |
| 588 return height(); | |
| 589 } | |
| 590 | |
| 591 int Label::GetViewWidth() const { | |
| 592 return width(); | |
| 593 } | |
| 594 | |
| 595 int Label::GetDragSelectionDelay() const { | |
| 596 // Labels don't need to use a repeating timer to update the drag selection. | |
| 597 // Since the cursor is disabled for labels, a selection outside the display | |
| 598 // area won't change the text in the display area. It is expected that all the | |
| 599 // text will fit in the display area for labels anyway. | |
| 600 return 0; | |
| 601 } | |
| 602 | |
| 603 void Label::OnBeforePointerAction() {} | |
| 604 | |
| 605 void Label::OnAfterPointerAction(bool text_changed, bool selection_changed) { | |
| 606 DCHECK(!text_changed); | |
| 607 if (selection_changed) | |
| 608 SchedulePaint(); | |
| 609 } | |
| 610 | |
| 611 bool Label::PasteSelectionClipboard() { | |
| 612 NOTREACHED(); | |
| 613 return false; | |
| 614 } | |
| 615 | |
| 616 void Label::UpdateSelectionClipboard() { | |
| 617 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
| 618 if (!obscured()) { | |
| 619 const gfx::RenderText* render_text = GetRenderTextForSelectionController(); | |
| 620 DCHECK(render_text); | |
| 621 const base::string16 selected_text = | |
| 622 render_text->GetTextFromRange(render_text->selection()); | |
| 623 ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION) | |
| 624 .WriteText(selected_text); | |
| 625 } | |
| 626 #endif | |
| 627 } | |
| 628 | |
| 629 const gfx::RenderText* Label::GetRenderTextForSelectionController() const { | |
| 630 if (!selectable()) | |
| 631 return nullptr; | |
| 632 MaybeBuildRenderTextLines(); | |
| 633 | |
| 634 // This may happen when the content bounds of the view are empty. | |
| 635 if (lines_.empty()) | |
| 636 return nullptr; | |
| 637 | |
| 638 DCHECK_EQ(1u, lines_.size()); | |
| 639 return lines_[0].get(); | |
| 411 } | 640 } |
| 412 | 641 |
| 413 void Label::Init(const base::string16& text, const gfx::FontList& font_list) { | 642 void Label::Init(const base::string16& text, const gfx::FontList& font_list) { |
| 414 render_text_.reset(gfx::RenderText::CreateInstance()); | 643 render_text_.reset(gfx::RenderText::CreateInstance()); |
| 415 render_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER); | 644 render_text_->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| 416 render_text_->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); | 645 render_text_->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); |
| 417 // NOTE: |render_text_| should not be elided at all. This is used to keep some | 646 // 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. | 647 // properties and to compute the size of the string. |
| 419 render_text_->SetElideBehavior(gfx::NO_ELIDE); | 648 render_text_->SetElideBehavior(gfx::NO_ELIDE); |
| 420 render_text_->SetFontList(font_list); | 649 render_text_->SetFontList(font_list); |
| 421 render_text_->SetCursorEnabled(false); | 650 render_text_->SetCursorEnabled(false); |
| 422 render_text_->SetWordWrapBehavior(gfx::TRUNCATE_LONG_WORDS); | 651 render_text_->SetWordWrapBehavior(gfx::TRUNCATE_LONG_WORDS); |
| 423 | 652 |
| 424 elide_behavior_ = gfx::ELIDE_TAIL; | 653 elide_behavior_ = gfx::ELIDE_TAIL; |
| 654 stored_selection_range_ = gfx::Range::InvalidRange(); | |
| 425 enabled_color_set_ = disabled_color_set_ = background_color_set_ = false; | 655 enabled_color_set_ = disabled_color_set_ = background_color_set_ = false; |
| 656 selection_text_color_set_ = selection_background_color_set_ = false; | |
| 426 subpixel_rendering_enabled_ = true; | 657 subpixel_rendering_enabled_ = true; |
| 427 auto_color_readability_ = true; | 658 auto_color_readability_ = true; |
| 428 multi_line_ = false; | 659 multi_line_ = false; |
| 429 UpdateColorsFromTheme(GetNativeTheme()); | 660 UpdateColorsFromTheme(GetNativeTheme()); |
| 430 handles_tooltips_ = true; | 661 handles_tooltips_ = true; |
| 431 collapse_when_hidden_ = false; | 662 collapse_when_hidden_ = false; |
| 432 fixed_width_ = 0; | 663 fixed_width_ = 0; |
| 433 max_width_ = 0; | 664 max_width_ = 0; |
| 434 is_first_paint_text_ = true; | 665 is_first_paint_text_ = true; |
| 435 SetText(text); | 666 SetText(text); |
| 436 } | 667 } |
| 437 | 668 |
| 438 void Label::ResetLayout() { | 669 void Label::ResetLayout() { |
| 439 InvalidateLayout(); | 670 InvalidateLayout(); |
| 440 PreferredSizeChanged(); | 671 PreferredSizeChanged(); |
| 441 SchedulePaint(); | 672 SchedulePaint(); |
| 442 lines_.clear(); | 673 ClearRenderTextLines(); |
| 443 } | 674 } |
| 444 | 675 |
| 445 void Label::MaybeBuildRenderTextLines() { | 676 void Label::MaybeBuildRenderTextLines() const { |
| 446 if (!lines_.empty()) | 677 if (!lines_.empty()) |
| 447 return; | 678 return; |
| 448 | 679 |
| 449 gfx::Rect rect = GetContentsBounds(); | 680 gfx::Rect rect = GetContentsBounds(); |
| 450 if (focus_behavior() != FocusBehavior::NEVER) | 681 if (focus_behavior() != FocusBehavior::NEVER) |
| 451 rect.Inset(kFocusBorderPadding, kFocusBorderPadding); | 682 rect.Inset(kFocusBorderPadding, kFocusBorderPadding); |
| 452 if (rect.IsEmpty()) | 683 if (rect.IsEmpty()) |
| 453 return; | 684 return; |
| 454 rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); | 685 rect.Inset(-gfx::ShadowValue::GetMargin(shadows())); |
| 455 | 686 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 468 // Text eliding is not supported for multi-lined Labels. | 699 // Text eliding is not supported for multi-lined Labels. |
| 469 // TODO(mukai): Add multi-lined elided text support. | 700 // TODO(mukai): Add multi-lined elided text support. |
| 470 gfx::ElideBehavior elide_behavior = | 701 gfx::ElideBehavior elide_behavior = |
| 471 multi_line() ? gfx::NO_ELIDE : elide_behavior_; | 702 multi_line() ? gfx::NO_ELIDE : elide_behavior_; |
| 472 if (!multi_line() || render_text_->MultilineSupported()) { | 703 if (!multi_line() || render_text_->MultilineSupported()) { |
| 473 std::unique_ptr<gfx::RenderText> render_text = | 704 std::unique_ptr<gfx::RenderText> render_text = |
| 474 CreateRenderText(text(), alignment, directionality, elide_behavior); | 705 CreateRenderText(text(), alignment, directionality, elide_behavior); |
| 475 render_text->SetDisplayRect(rect); | 706 render_text->SetDisplayRect(rect); |
| 476 render_text->SetMultiline(multi_line()); | 707 render_text->SetMultiline(multi_line()); |
| 477 render_text->SetWordWrapBehavior(render_text_->word_wrap_behavior()); | 708 render_text->SetWordWrapBehavior(render_text_->word_wrap_behavior()); |
| 709 | |
| 710 // Setup render text for selection controller. | |
| 711 if (selectable()) { | |
| 712 render_text->set_focused(HasFocus()); | |
| 713 if (stored_selection_range_.IsValid()) | |
| 714 render_text->SelectRange(stored_selection_range_); | |
| 715 } | |
| 716 | |
| 478 lines_.push_back(std::move(render_text)); | 717 lines_.push_back(std::move(render_text)); |
| 479 } else { | 718 } else { |
| 480 std::vector<base::string16> lines = GetLinesForWidth(rect.width()); | 719 std::vector<base::string16> lines = GetLinesForWidth(rect.width()); |
| 481 if (lines.size() > 1) | 720 if (lines.size() > 1) |
| 482 rect.set_height(std::max(line_height(), font_list().GetHeight())); | 721 rect.set_height(std::max(line_height(), font_list().GetHeight())); |
| 483 | 722 |
| 484 const int bottom = GetContentsBounds().bottom(); | 723 const int bottom = GetContentsBounds().bottom(); |
| 485 for (size_t i = 0; i < lines.size() && rect.y() <= bottom; ++i) { | 724 for (size_t i = 0; i < lines.size() && rect.y() <= bottom; ++i) { |
| 486 std::unique_ptr<gfx::RenderText> line = | 725 std::unique_ptr<gfx::RenderText> line = |
| 487 CreateRenderText(lines[i], alignment, directionality, elide_behavior); | 726 CreateRenderText(lines[i], alignment, directionality, elide_behavior); |
| 488 line->SetDisplayRect(rect); | 727 line->SetDisplayRect(rect); |
| 489 lines_.push_back(std::move(line)); | 728 lines_.push_back(std::move(line)); |
| 490 rect.set_y(rect.y() + rect.height()); | 729 rect.set_y(rect.y() + rect.height()); |
| 491 } | 730 } |
| 492 // Append the remaining text to the last visible line. | 731 // Append the remaining text to the last visible line. |
| 493 for (size_t i = lines_.size(); i < lines.size(); ++i) | 732 for (size_t i = lines_.size(); i < lines.size(); ++i) |
| 494 lines_.back()->SetText(lines_.back()->text() + lines[i]); | 733 lines_.back()->SetText(lines_.back()->text() + lines[i]); |
| 495 } | 734 } |
| 735 | |
| 736 stored_selection_range_ = gfx::Range::InvalidRange(); | |
| 496 ApplyTextColors(); | 737 ApplyTextColors(); |
| 497 } | 738 } |
| 498 | 739 |
| 499 gfx::Rect Label::GetFocusBounds() { | 740 gfx::Rect Label::GetFocusBounds() const { |
| 500 MaybeBuildRenderTextLines(); | 741 MaybeBuildRenderTextLines(); |
| 501 | 742 |
| 502 gfx::Rect focus_bounds; | 743 gfx::Rect focus_bounds; |
| 503 if (lines_.empty()) { | 744 if (lines_.empty()) { |
| 504 focus_bounds = gfx::Rect(GetTextSize()); | 745 focus_bounds = gfx::Rect(GetTextSize()); |
| 505 } else { | 746 } else { |
| 506 for (size_t i = 0; i < lines_.size(); ++i) { | 747 for (size_t i = 0; i < lines_.size(); ++i) { |
| 507 gfx::Point origin; | 748 gfx::Point origin; |
| 508 origin += lines_[i]->GetLineOffset(0); | 749 origin += lines_[i]->GetLineOffset(0); |
| 509 focus_bounds.Union(gfx::Rect(origin, lines_[i]->GetStringSize())); | 750 focus_bounds.Union(gfx::Rect(origin, lines_[i]->GetStringSize())); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 564 | 805 |
| 565 void Label::RecalculateColors() { | 806 void Label::RecalculateColors() { |
| 566 actual_enabled_color_ = auto_color_readability_ ? | 807 actual_enabled_color_ = auto_color_readability_ ? |
| 567 color_utils::GetReadableColor(requested_enabled_color_, | 808 color_utils::GetReadableColor(requested_enabled_color_, |
| 568 background_color_) : | 809 background_color_) : |
| 569 requested_enabled_color_; | 810 requested_enabled_color_; |
| 570 actual_disabled_color_ = auto_color_readability_ ? | 811 actual_disabled_color_ = auto_color_readability_ ? |
| 571 color_utils::GetReadableColor(requested_disabled_color_, | 812 color_utils::GetReadableColor(requested_disabled_color_, |
| 572 background_color_) : | 813 background_color_) : |
| 573 requested_disabled_color_; | 814 requested_disabled_color_; |
| 815 actual_selection_text_color_ = | |
| 816 auto_color_readability_ | |
| 817 ? color_utils::GetReadableColor(requested_selection_text_color_, | |
| 818 selection_background_color_) | |
| 819 : requested_selection_text_color_; | |
| 574 | 820 |
| 575 ApplyTextColors(); | 821 ApplyTextColors(); |
| 576 SchedulePaint(); | 822 SchedulePaint(); |
| 577 } | 823 } |
| 578 | 824 |
| 579 void Label::ApplyTextColors() { | 825 void Label::ApplyTextColors() const { |
| 580 SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_; | 826 SkColor color = enabled() ? actual_enabled_color_ : actual_disabled_color_; |
| 581 bool subpixel_rendering_suppressed = | 827 bool subpixel_rendering_suppressed = |
| 582 SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_; | 828 SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_; |
| 583 for (size_t i = 0; i < lines_.size(); ++i) { | 829 for (size_t i = 0; i < lines_.size(); ++i) { |
| 584 lines_[i]->SetColor(color); | 830 lines_[i]->SetColor(color); |
| 831 lines_[i]->set_selection_color(actual_selection_text_color_); | |
| 832 lines_[i]->set_selection_background_focused_color( | |
| 833 selection_background_color_); | |
| 585 lines_[i]->set_subpixel_rendering_suppressed(subpixel_rendering_suppressed); | 834 lines_[i]->set_subpixel_rendering_suppressed(subpixel_rendering_suppressed); |
| 586 } | 835 } |
| 587 } | 836 } |
| 588 | 837 |
| 589 void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) { | 838 void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) { |
| 590 if (!enabled_color_set_) { | 839 if (!enabled_color_set_) { |
| 591 requested_enabled_color_ = theme->GetSystemColor( | 840 requested_enabled_color_ = theme->GetSystemColor( |
| 592 ui::NativeTheme::kColorId_LabelEnabledColor); | 841 ui::NativeTheme::kColorId_LabelEnabledColor); |
| 593 } | 842 } |
| 594 if (!disabled_color_set_) { | 843 if (!disabled_color_set_) { |
| 595 requested_disabled_color_ = theme->GetSystemColor( | 844 requested_disabled_color_ = theme->GetSystemColor( |
| 596 ui::NativeTheme::kColorId_LabelDisabledColor); | 845 ui::NativeTheme::kColorId_LabelDisabledColor); |
| 597 } | 846 } |
| 598 if (!background_color_set_) { | 847 if (!background_color_set_) { |
| 599 background_color_ = | 848 background_color_ = |
| 600 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); | 849 theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground); |
| 601 } | 850 } |
| 851 if (!selection_text_color_set_) { | |
| 852 requested_selection_text_color_ = theme->GetSystemColor( | |
| 853 ui::NativeTheme::kColorId_LabelTextSelectionColor); | |
| 854 } | |
| 855 if (!selection_background_color_set_) { | |
| 856 selection_background_color_ = theme->GetSystemColor( | |
| 857 ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused); | |
| 858 } | |
| 602 RecalculateColors(); | 859 RecalculateColors(); |
| 603 } | 860 } |
| 604 | 861 |
| 605 bool Label::ShouldShowDefaultTooltip() const { | 862 bool Label::ShouldShowDefaultTooltip() const { |
| 606 const gfx::Size text_size = GetTextSize(); | 863 const gfx::Size text_size = GetTextSize(); |
| 607 const gfx::Size size = GetContentsBounds().size(); | 864 const gfx::Size size = GetContentsBounds().size(); |
| 608 return !obscured() && (text_size.width() > size.width() || | 865 return !obscured() && (text_size.width() > size.width() || |
| 609 (multi_line() && text_size.height() > size.height())); | 866 (multi_line() && text_size.height() > size.height())); |
| 610 } | 867 } |
| 611 | 868 |
| 869 void Label::ClearRenderTextLines() const { | |
| 870 // Persist the selection range if there is an active selection. | |
| 871 if (HasSelection()) { | |
| 872 stored_selection_range_ = | |
| 873 GetRenderTextForSelectionController()->selection(); | |
| 874 } | |
| 875 lines_.clear(); | |
| 876 } | |
| 877 | |
| 612 } // namespace views | 878 } // namespace views |
| OLD | NEW |