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

Unified Diff: ui/views/controls/label.cc

Issue 2379993005: Label selection experiment (Closed)
Patch Set: -- Created 4 years, 2 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/views/controls/label.h ('k') | ui/views/controls/link.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/views/controls/label.cc
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 0c4667dad17cab28ccb7493022d918842fb7fa85..b21fdd6938242c97668ce690a72b33130b0d2190 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -18,6 +18,8 @@
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_view_state.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/cursor/cursor.h"
#include "ui/base/default_style.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/base/resource/resource_bundle.h"
@@ -26,6 +28,8 @@
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/text_elider.h"
#include "ui/native_theme/native_theme.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/native_cursor.h"
namespace views {
// static
@@ -100,6 +104,24 @@ void Label::SetBackgroundColor(SkColor color) {
RecalculateColors();
}
+void Label::SetSelectionTextColor(SkColor color) {
+ if (selection_text_color_set_ && requested_selection_text_color_ == color)
+ return;
+ is_first_paint_text_ = true;
+ requested_selection_text_color_ = color;
+ selection_text_color_set_ = true;
+ RecalculateColors();
+}
+
+void Label::SetSelectionBackgroundColor(SkColor color) {
+ if (selection_background_color_set_ && selection_background_color_ == color)
+ return;
+ is_first_paint_text_ = true;
+ selection_background_color_ = color;
+ selection_background_color_set_ = true;
+ RecalculateColors();
+}
+
void Label::SetShadows(const gfx::ShadowValues& shadows) {
// TODO(mukai): early exit if the specified shadows are same.
is_first_paint_text_ = true;
@@ -147,6 +169,8 @@ void Label::SetMultiLine(bool multi_line) {
if (render_text_->MultilineSupported())
render_text_->SetMultiline(multi_line);
render_text_->SetReplaceNewlineCharsWithSymbols(!multi_line);
+ if (multi_line)
+ SetSelectable(false);
ResetLayout();
}
@@ -204,7 +228,7 @@ void Label::SetMaximumWidth(int max_width) {
}
base::string16 Label::GetDisplayTextForTesting() {
- lines_.clear();
+ ClearLines();
MaybeBuildRenderTextLines();
base::string16 result;
if (lines_.empty())
@@ -217,6 +241,48 @@ base::string16 Label::GetDisplayTextForTesting() {
return result;
}
+bool Label::IsSelectionSupported() const {
+ return !multi_line() && render_text_->IsSelectionSupported();
+}
+
+bool Label::SetSelectable(bool value) {
+ if (value == selectable())
+ return true;
+
+ if (value && !IsSelectionSupported())
+ return false;
+
+ ClearSelection();
+ selection_controller_.reset(value ? new SelectionController(this) : nullptr);
+ return true;
+}
+
+bool Label::HasSelection() {
+ gfx::RenderText* render_text = GetRenderTextForSelection();
+ return render_text ? !render_text->selection().is_empty() : false;
+}
+
+void Label::SelectAll() {
+ if (!GetRenderTextForSelection())
+ return;
+ GetRenderTextForSelection()->SelectAll(false);
+ OnSelectionUpdated();
+}
+
+void Label::ClearSelection() {
+ if (!GetRenderTextForSelection())
+ return;
+ GetRenderTextForSelection()->ClearSelection();
+ OnSelectionUpdated();
+}
+
+void Label::SelectRange(const gfx::Range& range) {
+ if (!GetRenderTextForSelection())
+ return;
+ if (GetRenderTextForSelection()->SelectRange(range))
+ OnSelectionUpdated();
+}
+
gfx::Insets Label::GetInsets() const {
gfx::Insets insets = View::GetInsets();
if (focus_behavior() != FocusBehavior::NEVER) {
@@ -296,7 +362,7 @@ int Label::GetHeightForWidth(int w) const {
}
void Label::Layout() {
- lines_.clear();
+ ClearLines();
}
const char* Label::GetClassName() const {
@@ -312,8 +378,7 @@ View* Label::GetTooltipHandlerForPoint(const gfx::Point& point) {
}
bool Label::CanProcessEventsWithinSubtree() const {
- // Send events to the parent view for handling.
- return false;
+ return selectable();
}
void Label::GetAccessibleState(ui::AXViewState* state) {
@@ -389,14 +454,68 @@ void Label::OnPaint(gfx::Canvas* canvas) {
} else {
PaintText(canvas);
}
- if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial())
+
+ // Check for IsAccessibilityFocusable() to prevent drawing a focus rect for
+ // non-focusable labels with selection, which are given focus explicitly in
+ // OnMousePressed.
+ if (HasFocus() && !ui::MaterialDesignController::IsSecondaryUiMaterial() &&
+ IsAccessibilityFocusable()) {
canvas->DrawFocusRect(GetFocusBounds());
+ }
}
void Label::OnNativeThemeChanged(const ui::NativeTheme* theme) {
UpdateColorsFromTheme(theme);
}
+gfx::NativeCursor Label::GetCursor(const ui::MouseEvent& event) {
+ return GetRenderTextForSelection() ? GetNativeIBeamCursor()
+ : gfx::kNullCursor;
+}
+
+void Label::OnFocus() {
+ if (GetRenderTextForSelection()) {
+ GetRenderTextForSelection()->set_focused(true);
+ SchedulePaint();
+ }
+ View::OnFocus();
+}
+
+void Label::OnBlur() {
+ if (GetRenderTextForSelection()) {
+ GetRenderTextForSelection()->set_focused(false);
+ SchedulePaint();
+ }
+ View::OnBlur();
+}
+
+bool Label::OnMousePressed(const ui::MouseEvent& event) {
+ DCHECK(selectable());
+ if (!GetRenderTextForSelection())
+ return false;
+
+ // RequestFocus() won't work when the label has FocusBehavior::NEVER. Hence
+ // explicitly set the focused view.
+ // Todo(karandeepb): If a widget with a label having FocusBehavior::NEVER as
+ // the currently focused view (due to selection) was to lose focus, focus
+ // won't be restored to the label (and hence a text selection won't be drawn)
+ // when the widget gets focus again. Fix this.
+ // Todo is giving focus to a view with FocusBehavior::NEVER ok?
+ if (GetFocusManager())
+ GetFocusManager()->SetFocusedView(this);
+ return selection_controller_->OnMousePressed(event, false);
+}
+
+bool Label::OnMouseDragged(const ui::MouseEvent& event) {
+ DCHECK(GetRenderTextForSelection());
+ return selection_controller_->OnMouseDragged(event);
+}
+
+void Label::OnMouseReleased(const ui::MouseEvent& event) {
+ DCHECK(GetRenderTextForSelection());
+ selection_controller_->OnMouseReleased(event);
+}
+
void Label::OnDeviceScaleFactorChanged(float device_scale_factor) {
View::OnDeviceScaleFactorChanged(device_scale_factor);
// When the device scale factor is changed, some font rendering parameters is
@@ -407,7 +526,86 @@ void Label::OnDeviceScaleFactorChanged(float device_scale_factor) {
void Label::VisibilityChanged(View* starting_from, bool is_visible) {
if (!is_visible)
- lines_.clear();
+ ClearLines();
+}
+
+gfx::RenderText* Label::GetRenderTextForSelection() {
+ if (!selectable())
+ return nullptr;
+ DCHECK(IsSelectionSupported());
+ MaybeBuildRenderTextLines();
+ if (lines_.empty())
+ return nullptr;
+ DCHECK_EQ(1u, lines_.size());
+ return lines_[0].get();
+}
+
+bool Label::IsReadOnly() const {
+ return true;
+}
+
+bool Label::SupportsDrag() const {
+ return false;
+}
+
+bool Label::HasTextBeingDragged() const {
+ return false;
+}
+
+void Label::SetTextBeingDragged(bool value) {}
+
+int Label::GetViewHeight() const {
+ return height();
+}
+
+int Label::GetViewWidth() const {
+ return width();
+}
+
+int Label::GetDragSelectionDelay() const {
+ // Labels don't need to use a repeating timer to update drag selection. Since
+ // the cursor is disabled for labels, a selection outside the display area
+ // won't change the text in the display area. It is expected that all the text
+ // will fit in the display area for labels anyway.
+ return 0;
+}
+
+void Label::OnBeforeMouseAction() {
+ DCHECK(!performing_mouse_action_);
+ DCHECK(GetRenderTextForSelection());
+ performing_mouse_action_ = true;
+}
+
+void Label::OnAfterMouseAction(bool text_changed, bool selection_changed) {
+ DCHECK(!text_changed);
+ DCHECK(performing_mouse_action_);
+ DCHECK(GetRenderTextForSelection());
+ performing_mouse_action_ = false;
+ if (selection_changed)
+ OnSelectionUpdated();
+}
+
+void Label::OnBeforeSelectionUpdated() {
+ DCHECK(performing_mouse_action_);
+}
+
+void Label::OnAfterSelectionUpdated() {
+ DCHECK(performing_mouse_action_);
+ OnSelectionUpdated();
+}
+
+void Label::PasteSelectionClipboard(const ui::MouseEvent& event) {}
+
+void Label::UpdateSelectionClipboard() {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ gfx::RenderText* render_text = GetRenderTextForSelection();
+ if (performing_mouse_action_ && !obscured() && render_text) {
+ base::string16 selected_text =
+ render_text->GetTextFromRange(render_text->selection());
+ ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_SELECTION)
+ .WriteText(selected_text);
+ }
+#endif
}
void Label::Init(const base::string16& text, const gfx::FontList& font_list) {
@@ -423,6 +621,7 @@ void Label::Init(const base::string16& text, const gfx::FontList& font_list) {
elide_behavior_ = gfx::ELIDE_TAIL;
enabled_color_set_ = disabled_color_set_ = background_color_set_ = false;
+ selection_text_color_set_ = selection_background_color_set_ = false;
subpixel_rendering_enabled_ = true;
auto_color_readability_ = true;
multi_line_ = false;
@@ -432,6 +631,7 @@ void Label::Init(const base::string16& text, const gfx::FontList& font_list) {
fixed_width_ = 0;
max_width_ = 0;
is_first_paint_text_ = true;
+ performing_mouse_action_ = false;
SetText(text);
}
@@ -439,7 +639,7 @@ void Label::ResetLayout() {
InvalidateLayout();
PreferredSizeChanged();
SchedulePaint();
- lines_.clear();
+ ClearLines();
}
void Label::MaybeBuildRenderTextLines() {
@@ -493,6 +693,15 @@ void Label::MaybeBuildRenderTextLines() {
for (size_t i = lines_.size(); i < lines.size(); ++i)
lines_.back()->SetText(lines_.back()->text() + lines[i]);
}
+
+ if (GetRenderTextForSelection())
+ GetRenderTextForSelection()->set_focused(HasFocus());
+
+ if (last_selection_range_) {
+ SelectRange(last_selection_range_.value());
+ last_selection_range_.reset();
+ }
+
ApplyTextColors();
}
@@ -571,6 +780,11 @@ void Label::RecalculateColors() {
color_utils::GetReadableColor(requested_disabled_color_,
background_color_) :
requested_disabled_color_;
+ actual_selection_text_color_ =
+ auto_color_readability_
+ ? color_utils::GetReadableColor(requested_selection_text_color_,
+ selection_background_color_)
+ : requested_selection_text_color_;
ApplyTextColors();
SchedulePaint();
@@ -582,6 +796,9 @@ void Label::ApplyTextColors() {
SkColorGetA(background_color_) != 0xFF || !subpixel_rendering_enabled_;
for (size_t i = 0; i < lines_.size(); ++i) {
lines_[i]->SetColor(color);
+ lines_[i]->set_selection_color(actual_selection_text_color_);
+ lines_[i]->set_selection_background_focused_color(
+ selection_background_color_);
lines_[i]->set_subpixel_rendering_suppressed(subpixel_rendering_suppressed);
}
}
@@ -599,6 +816,15 @@ void Label::UpdateColorsFromTheme(const ui::NativeTheme* theme) {
background_color_ =
theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground);
}
+ if (!selection_text_color_set_) {
+ // Todo create new default for labels or rename Textfield to Text?
+ requested_selection_text_color_ = theme->GetSystemColor(
+ ui::NativeTheme::kColorId_TextfieldSelectionColor);
+ }
+ if (!selection_background_color_set_) {
+ selection_background_color_ = theme->GetSystemColor(
+ ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused);
+ }
RecalculateColors();
}
@@ -609,4 +835,17 @@ bool Label::ShouldShowDefaultTooltip() const {
(multi_line() && text_size.height() > size.height()));
}
+void Label::OnSelectionUpdated() {
+ if (performing_mouse_action_)
+ return;
+ SchedulePaint();
+}
+
+void Label::ClearLines() {
+ // Persist the selection range if there is an active selection.
+ if (HasSelection())
+ last_selection_range_ = GetRenderTextForSelection()->selection();
+ lines_.clear();
+}
+
} // namespace views
« no previous file with comments | « ui/views/controls/label.h ('k') | ui/views/controls/link.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698