summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/ui/views/omnibox/omnibox_view_views.cc58
-rw-r--r--chrome/browser/ui/views/omnibox/omnibox_view_views.h14
-rwxr-xr-xui/gfx/render_text.cc506
-rwxr-xr-xui/gfx/render_text.h224
-rwxr-xr-xui/gfx/render_text_linux.cc20
-rwxr-xr-xui/gfx/render_text_linux.h25
-rwxr-xr-xui/gfx/render_text_unittest.cc180
-rwxr-xr-xui/gfx/render_text_win.cc20
-rwxr-xr-xui/gfx/render_text_win.h25
-rw-r--r--ui/ui.gyp16
-rw-r--r--ui/ui_unittests.gypi5
-rw-r--r--views/controls/textfield/native_textfield_gtk.cc10
-rw-r--r--views/controls/textfield/native_textfield_gtk.h6
-rw-r--r--views/controls/textfield/native_textfield_views.cc295
-rw-r--r--views/controls/textfield/native_textfield_views.h32
-rw-r--r--views/controls/textfield/native_textfield_views_unittest.cc10
-rw-r--r--views/controls/textfield/native_textfield_win.cc10
-rw-r--r--views/controls/textfield/native_textfield_win.h6
-rw-r--r--views/controls/textfield/native_textfield_wrapper.h17
-rw-r--r--views/controls/textfield/text_style.cc54
-rw-r--r--views/controls/textfield/text_style.h65
-rw-r--r--views/controls/textfield/textfield.cc14
-rw-r--r--views/controls/textfield/textfield.h31
-rw-r--r--views/controls/textfield/textfield_views_model.cc522
-rw-r--r--views/controls/textfield/textfield_views_model.h122
-rw-r--r--views/controls/textfield/textfield_views_model_unittest.cc922
-rw-r--r--views/examples/textfield_example.cc36
-rw-r--r--views/examples/textfield_example.h9
-rw-r--r--views/views.gyp2
29 files changed, 1643 insertions, 1613 deletions
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 08b65b9..015cbbf 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -27,8 +27,8 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/font.h"
+#include "ui/gfx/render_text.h"
#include "views/border.h"
-#include "views/controls/textfield/text_style.h"
#include "views/controls/textfield/textfield.h"
#include "views/layout/fill_layout.h"
@@ -105,6 +105,19 @@ PropertyAccessor<AutocompleteEditState>* GetStateAccessor() {
return &state;
}
+// A convenience method for applying URL styles.
+void ApplyURLStyle(views::Textfield* textfield,
+ size_t start,
+ size_t end,
+ SkColor color,
+ bool strike) {
+ gfx::StyleRange style;
+ style.foreground = color;
+ style.range = ui::Range(start, end);
+ style.strike = strike;
+ textfield->ApplyStyleRange(style);
+}
+
const int kAutocompleteVerticalMargin = 4;
// TODO(oshima): I'm currently using slightly different color than
@@ -135,11 +148,7 @@ OmniboxViewViews::OmniboxViewViews(AutocompleteEditController* controller,
popup_window_mode_(popup_window_mode),
security_level_(ToolbarModel::NONE),
ime_composing_before_change_(false),
- delete_at_end_pressed_(false),
- faded_text_style_(NULL),
- normal_text_style_(NULL),
- security_error_scheme_style_(NULL),
- secure_scheme_style_(NULL) {
+ delete_at_end_pressed_(false) {
set_border(views::Border::CreateEmptyBorder(kAutocompleteVerticalMargin, 0,
kAutocompleteVerticalMargin, 0));
}
@@ -623,7 +632,6 @@ size_t OmniboxViewViews::GetTextLength() const {
}
void OmniboxViewViews::EmphasizeURLComponents() {
- InitTextStyles();
// See whether the contents are a URL with a non-empty host portion, which we
// should emphasize. To check for a URL, rather than using the type returned
// by Parse(), ask the model, which will check the desired page transition for
@@ -635,29 +643,24 @@ void OmniboxViewViews::EmphasizeURLComponents() {
AutocompleteInput::ParseForEmphasizeComponents(
text, model_->GetDesiredTLD(), &scheme, &host);
const bool emphasize = model_->CurrentTextIsURL() && (host.len > 0);
-
- textfield_->ClearAllTextStyles();
- if (emphasize) {
- textfield_->ApplyTextStyle(faded_text_style_, ui::Range(0, text.length()));
- textfield_->ApplyTextStyle(normal_text_style_,
- ui::Range(host.begin, host.end()));
- } else {
- textfield_->ApplyTextStyle(normal_text_style_, ui::Range(0, text.length()));
- }
+ SkColor base_color = emphasize ? kFadedTextColor : kNormalTextColor;
+ ApplyURLStyle(textfield_, 0, text.length(), base_color, false);
+ if (emphasize)
+ ApplyURLStyle(textfield_, host.begin, host.end(), kNormalTextColor, false);
// Emphasize the scheme for security UI display purposes (if necessary).
if (!model_->user_input_in_progress() && scheme.is_nonempty() &&
(security_level_ != ToolbarModel::NONE)) {
- ui::Range scheme_range(scheme.begin, scheme.end());
+ const size_t start = scheme.begin, end = scheme.end();
switch (security_level_) {
case ToolbarModel::SECURITY_ERROR:
- textfield_->ApplyTextStyle(security_error_scheme_style_, scheme_range);
+ ApplyURLStyle(textfield_, start, end, kSecurityErrorSchemeColor, true);
break;
case ToolbarModel::SECURITY_WARNING:
- textfield_->ApplyTextStyle(faded_text_style_, scheme_range);
+ ApplyURLStyle(textfield_, start, end, kFadedTextColor, false);
break;
case ToolbarModel::EV_SECURE:
case ToolbarModel::SECURE:
- textfield_->ApplyTextStyle(secure_scheme_style_, scheme_range);
+ ApplyURLStyle(textfield_, start, end, kSecureSchemeColor, false);
break;
default:
NOTREACHED() << "Unknown SecurityLevel:" << security_level_;
@@ -698,18 +701,3 @@ AutocompletePopupView* OmniboxViewViews::CreatePopupView(
return new AutocompleteContentsView(
gfx::Font(), this, model_.get(), profile, location_bar);
}
-
-void OmniboxViewViews::InitTextStyles() {
- if (faded_text_style_)
- return;
- faded_text_style_ = textfield_->CreateTextStyle();
- normal_text_style_ = textfield_->CreateTextStyle();
- security_error_scheme_style_ = textfield_->CreateTextStyle();
- secure_scheme_style_ = textfield_->CreateTextStyle();
-
- faded_text_style_->set_foreground(kFadedTextColor);
- normal_text_style_->set_foreground(kNormalTextColor);
- secure_scheme_style_->set_foreground(kSecureSchemeColor);
- security_error_scheme_style_->set_foreground(kSecurityErrorSchemeColor);
- security_error_scheme_style_->set_strike(true);
-}
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index 7b3ae8f..553b335 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -26,10 +26,6 @@ class AutocompletePopupView;
class Profile;
class TabContents;
-namespace views {
-class TextStyle;
-}
-
// Views-implementation of OmniboxView. This is based on gtk implementation.
// The following features are not yet supported.
//
@@ -163,10 +159,6 @@ class OmniboxViewViews : public views::View,
AutocompletePopupView* CreatePopupView(Profile* profile,
const View* location_bar);
- // Lazy initialization of TextStyle objects. They can not be
- // initialized in Init as native wrapper isn't available at that time.
- void InitTextStyles();
-
views::Textfield* textfield_;
scoped_ptr<AutocompleteEditModel> model_;
@@ -196,12 +188,6 @@ class OmniboxViewViews : public views::View,
// Was the delete key pressed with an empty selection at the end of the edit?
bool delete_at_end_pressed_;
- // TextStyles for URL decoration. They're owned by textfield_.
- views::TextStyle* faded_text_style_;
- views::TextStyle* normal_text_style_;
- views::TextStyle* security_error_scheme_style_;
- views::TextStyle* secure_scheme_style_;
-
DISALLOW_COPY_AND_ASSIGN(OmniboxViewViews);
};
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
new file mode 100755
index 0000000..c656792
--- /dev/null
+++ b/ui/gfx/render_text.cc
@@ -0,0 +1,506 @@
+// Copyright (c) 2011 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.
+
+#include "ui/gfx/render_text.h"
+
+#include <algorithm>
+
+#include "base/i18n/break_iterator.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/canvas_skia.h"
+
+namespace {
+
+#ifndef NDEBUG
+// Check StyleRanges invariant conditions: sorted and non-overlapping ranges.
+void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) {
+ if (length == 0) {
+ DCHECK(style_ranges.empty()) << "Style ranges exist for empty text.";
+ return;
+ }
+ for (gfx::StyleRanges::size_type i = 0; i < style_ranges.size() - 1; i++) {
+ const ui::Range& former = style_ranges[i].range;
+ const ui::Range& latter = style_ranges[i + 1].range;
+ DCHECK(!former.is_empty()) << "Empty range at " << i << ":" << former;
+ DCHECK(former.IsValid()) << "Invalid range at " << i << ":" << former;
+ DCHECK(!former.is_reversed()) << "Reversed range at " << i << ":" << former;
+ DCHECK(former.end() == latter.start()) << "Ranges gap/overlap/unsorted." <<
+ "former:" << former << ", latter:" << latter;
+ }
+ const gfx::StyleRange& end_style = *style_ranges.rbegin();
+ DCHECK(!end_style.range.is_empty()) << "Empty range at end.";
+ DCHECK(end_style.range.IsValid()) << "Invalid range at end.";
+ DCHECK(!end_style.range.is_reversed()) << "Reversed range at end.";
+ DCHECK(end_style.range.end() == length) << "Style and text length mismatch.";
+}
+#endif
+
+void ApplyStyleRangeImpl(gfx::StyleRanges& style_ranges,
+ gfx::StyleRange style_range) {
+ const ui::Range& new_range = style_range.range;
+ // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges.
+ gfx::StyleRanges::iterator i;
+ for (i = style_ranges.begin(); i != style_ranges.end();) {
+ if (i->range.end() < new_range.start()) {
+ i++;
+ } else if (i->range.start() == new_range.end()) {
+ break;
+ } else if (new_range.Contains(i->range)) {
+ i = style_ranges.erase(i);
+ if (i == style_ranges.end())
+ break;
+ } else if (i->range.start() < new_range.start() &&
+ i->range.end() > new_range.end()) {
+ // Split the current style into two styles.
+ gfx::StyleRange split_style = gfx::StyleRange(*i);
+ split_style.range.set_end(new_range.start());
+ i = style_ranges.insert(i, split_style) + 1;
+ i->range.set_start(new_range.end());
+ break;
+ } else if (i->range.start() < new_range.start()) {
+ i->range.set_end(new_range.start());
+ i++;
+ } else if (i->range.end() > new_range.end()) {
+ i->range.set_start(new_range.end());
+ break;
+ } else
+ NOTREACHED();
+ }
+ // Add the new range in its sorted location.
+ style_ranges.insert(i, style_range);
+}
+
+} // namespace
+
+namespace gfx {
+
+StyleRange::StyleRange()
+ : font(),
+ foreground(SK_ColorBLACK),
+ strike(false),
+ underline(false),
+ range() {
+}
+
+void RenderText::SetText(const string16& text) {
+ size_t old_text_length = text_.length();
+ text_ = text;
+
+ // Update the style ranges as needed.
+ if (text_.empty()) {
+ style_ranges_.clear();
+ } else if (style_ranges_.empty()) {
+ ApplyDefaultStyle();
+ } else if (text_.length() > old_text_length) {
+ style_ranges_.back().range.set_end(text_.length());
+ } else if (text_.length() < old_text_length) {
+ StyleRanges::iterator i;
+ for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
+ if (i->range.start() >= text_.length()) {
+ i = style_ranges_.erase(i);
+ if (i == style_ranges_.end())
+ break;
+ } else if (i->range.end() > text_.length()) {
+ i->range.set_end(text_.length());
+ }
+ }
+ style_ranges_.back().range.set_end(text_.length());
+ }
+#ifndef NDEBUG
+ CheckStyleRanges(style_ranges_, text_.length());
+#endif
+}
+
+size_t RenderText::GetCursorPosition() const {
+ return GetSelection().end();
+}
+
+void RenderText::SetCursorPosition(const size_t position) {
+ SetSelection(ui::Range(position, position));
+}
+
+void RenderText::MoveCursorLeft(BreakType break_type, bool select) {
+ if (break_type == LINE_BREAK) {
+ MoveCursorTo(0, select);
+ return;
+ }
+ size_t position = GetCursorPosition();
+ // Cancelling a selection moves to the edge of the selection.
+ if (!GetSelection().is_empty() && !select) {
+ // Use the selection start if it is left of the selection end.
+ if (GetCursorBounds(GetSelection().start(), false).x() <
+ GetCursorBounds(position, false).x())
+ position = GetSelection().start();
+ // If |move_by_word|, use the nearest word boundary left of the selection.
+ if (break_type == WORD_BREAK)
+ position = GetLeftCursorPosition(position, true);
+ } else {
+ position = GetLeftCursorPosition(position, break_type == WORD_BREAK);
+ }
+ MoveCursorTo(position, select);
+}
+
+void RenderText::MoveCursorRight(BreakType break_type, bool select) {
+ if (break_type == LINE_BREAK) {
+ MoveCursorTo(text().length(), select);
+ return;
+ }
+ size_t position = GetCursorPosition();
+ // Cancelling a selection moves to the edge of the selection.
+ if (!GetSelection().is_empty() && !select) {
+ // Use the selection start if it is right of the selection end.
+ if (GetCursorBounds(GetSelection().start(), false).x() >
+ GetCursorBounds(position, false).x())
+ position = GetSelection().start();
+ // If |move_by_word|, use the nearest word boundary right of the selection.
+ if (break_type == WORD_BREAK)
+ position = GetRightCursorPosition(position, true);
+ } else {
+ position = GetRightCursorPosition(position, break_type == WORD_BREAK);
+ }
+ MoveCursorTo(position, select);
+}
+
+bool RenderText::MoveCursorTo(size_t position, bool select) {
+ bool changed = GetCursorPosition() != position ||
+ select == GetSelection().is_empty();
+ if (select)
+ SetSelection(ui::Range(GetSelection().start(), position));
+ else
+ SetSelection(ui::Range(position, position));
+ return changed;
+}
+
+bool RenderText::MoveCursorTo(const gfx::Point& point, bool select) {
+ // TODO(msw): Make this function support cursor placement via mouse near BiDi
+ // level changes. The visual cursor appearance will depend on the location
+ // clicked, not solely the resulting logical cursor position. See the TODO
+ // note pertaining to selection_range_ for more information.
+ return MoveCursorTo(FindCursorPosition(point), select);
+}
+
+const ui::Range& RenderText::GetSelection() const {
+ return selection_range_;
+}
+
+void RenderText::SetSelection(const ui::Range& range) {
+ selection_range_.set_end(std::min(range.end(), text().length()));
+ selection_range_.set_start(std::min(range.start(), text().length()));
+
+ // Update |display_offset_| to ensure the current cursor is visible.
+ gfx::Rect cursor_bounds(GetCursorBounds(GetCursorPosition(), insert_mode()));
+ int display_width = display_rect_.width();
+ int string_width = GetStringWidth();
+ if (string_width < display_width) {
+ // Show all text whenever the text fits to the size.
+ display_offset_.set_x(0);
+ } else if ((display_offset_.x() + cursor_bounds.right()) > display_width) {
+ // Pan to show the cursor when it overflows to the right,
+ display_offset_.set_x(display_width - cursor_bounds.right());
+ } else if ((display_offset_.x() + cursor_bounds.x()) < 0) {
+ // Pan to show the cursor when it overflows to the left.
+ display_offset_.set_x(-cursor_bounds.x());
+ }
+}
+
+bool RenderText::IsPointInSelection(const gfx::Point& point) const {
+ size_t pos = FindCursorPosition(point);
+ return (pos >= GetSelection().GetMin() && pos < GetSelection().GetMax());
+}
+
+void RenderText::ClearSelection() {
+ SetCursorPosition(GetCursorPosition());
+}
+
+void RenderText::SelectAll() {
+ SetSelection(ui::Range(0, text().length()));
+}
+
+void RenderText::SelectWord() {
+ size_t selection_start = GetSelection().start();
+ size_t cursor_position = GetCursorPosition();
+ // First we setup selection_start_ and cursor_pos_. There are so many cases
+ // because we try to emulate what select-word looks like in a gtk textfield.
+ // See associated testcase for different cases.
+ if (cursor_position > 0 && cursor_position < text().length()) {
+ if (isalnum(text()[cursor_position])) {
+ selection_start = cursor_position;
+ cursor_position++;
+ } else
+ selection_start = cursor_position - 1;
+ } else if (cursor_position == 0) {
+ selection_start = cursor_position;
+ if (text().length() > 0)
+ cursor_position++;
+ } else {
+ selection_start = cursor_position - 1;
+ }
+
+ // Now we move selection_start_ to beginning of selection. Selection boundary
+ // is defined as the position where we have alpha-num character on one side
+ // and non-alpha-num char on the other side.
+ for (; selection_start > 0; selection_start--) {
+ if (IsPositionAtWordSelectionBoundary(selection_start))
+ break;
+ }
+
+ // Now we move cursor_pos_ to end of selection. Selection boundary
+ // is defined as the position where we have alpha-num character on one side
+ // and non-alpha-num char on the other side.
+ for (; cursor_position < text().length(); cursor_position++) {
+ if (IsPositionAtWordSelectionBoundary(cursor_position))
+ break;
+ }
+
+ SetSelection(ui::Range(selection_start, cursor_position));
+}
+
+const ui::Range& RenderText::GetCompositionRange() const {
+ return composition_range_;
+}
+
+void RenderText::SetCompositionRange(const ui::Range& composition_range) {
+ CHECK(!composition_range.IsValid() ||
+ ui::Range(0, text_.length()).Contains(composition_range));
+ composition_range_.set_end(composition_range.end());
+ composition_range_.set_start(composition_range.start());
+}
+
+void RenderText::ApplyStyleRange(StyleRange style_range) {
+ const ui::Range& new_range = style_range.range;
+ if (!new_range.IsValid() || new_range.is_empty())
+ return;
+ CHECK(!new_range.is_reversed());
+ CHECK(ui::Range(0, text_.length()).Contains(new_range));
+ ApplyStyleRangeImpl(style_ranges_, style_range);
+#ifndef NDEBUG
+ CheckStyleRanges(style_ranges_, text_.length());
+#endif
+}
+
+void RenderText::ApplyDefaultStyle() {
+ style_ranges_.clear();
+ StyleRange style = StyleRange(default_style_);
+ style.range.set_end(text_.length());
+ style_ranges_.push_back(style);
+}
+
+base::i18n::TextDirection RenderText::GetTextDirection() const {
+ // TODO(msw): Bidi implementation, intended to replace the functionality added
+ // in crrev.com/91881 (discussed in codereview.chromium.org/7324011).
+ return base::i18n::LEFT_TO_RIGHT;
+}
+
+int RenderText::GetStringWidth() const {
+ return GetSubstringBounds(ui::Range(0, text_.length()))[0].width();
+}
+
+void RenderText::Draw(gfx::Canvas* canvas) {
+ // Clip the canvas to the text display area.
+ canvas->ClipRectInt(display_rect_.x(), display_rect_.y(),
+ display_rect_.width(), display_rect_.height());
+
+ // Draw the selection.
+ std::vector<gfx::Rect> selection(GetSubstringBounds(GetSelection()));
+ SkColor selection_color =
+ focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
+ for (std::vector<gfx::Rect>::const_iterator i = selection.begin();
+ i < selection.end(); ++i) {
+ gfx::Rect r(*i);
+ r.Offset(display_offset_);
+ canvas->FillRectInt(selection_color, r.x(), r.y(), r.width(), r.height());
+ }
+
+ // Create a temporary copy of the style ranges for composition and selection.
+ // TODO(msw): This pattern ought to be reconsidered; what about composition
+ // and selection overlaps, retain existing local style features?
+ StyleRanges style_ranges(style_ranges_);
+ // Apply a composition style override to a copy of the style ranges.
+ if (composition_range_.IsValid() && !composition_range_.is_empty()) {
+ StyleRange composition_style(default_style_);
+ composition_style.underline = true;
+ composition_style.range.set_start(composition_range_.start());
+ composition_style.range.set_end(composition_range_.end());
+ ApplyStyleRangeImpl(style_ranges, composition_style);
+ }
+ // Apply a selection style override to a copy of the style ranges.
+ if (selection_range_.IsValid() && !selection_range_.is_empty()) {
+ StyleRange selection_style(default_style_);
+ selection_style.foreground = kSelectedTextColor;
+ selection_style.range.set_start(selection_range_.GetMin());
+ selection_style.range.set_end(selection_range_.GetMax());
+ ApplyStyleRangeImpl(style_ranges, selection_style);
+ }
+
+ // Draw the text.
+ gfx::Rect bounds(display_rect_);
+ bounds.Offset(display_offset_);
+ for (StyleRanges::const_iterator i = style_ranges.begin();
+ i < style_ranges.end(); ++i) {
+ Font font = !i->underline ? i->font :
+ i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED);
+ string16 text = text_.substr(i->range.start(), i->range.length());
+ bounds.set_width(font.GetStringWidth(text));
+ canvas->DrawStringInt(text, font, i->foreground, bounds);
+
+ // Draw the strikethrough.
+ if (i->strike) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(i->foreground);
+ paint.setStrokeWidth(kStrikeWidth);
+ canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()),
+ SkIntToScalar(bounds.bottom()),
+ SkIntToScalar(bounds.right()),
+ SkIntToScalar(bounds.y()),
+ paint);
+ }
+
+ bounds.set_x(bounds.x() + bounds.width());
+ }
+
+ // Paint cursor. Replace cursor is drawn as rectangle for now.
+ if (cursor_visible() && focused()) {
+ bounds = GetCursorBounds(GetCursorPosition(), insert_mode());
+ bounds.Offset(display_offset_);
+ if (!bounds.IsEmpty())
+ canvas->DrawRectInt(kCursorColor,
+ bounds.x(),
+ bounds.y(),
+ bounds.width(),
+ bounds.height());
+ }
+}
+
+size_t RenderText::FindCursorPosition(const gfx::Point& point) const {
+ const gfx::Font& font = Font();
+ int left = 0;
+ int left_pos = 0;
+ int right = font.GetStringWidth(text());
+ int right_pos = text().length();
+
+ int x = point.x();
+ if (x <= left) return left_pos;
+ if (x >= right) return right_pos;
+ // binary searching the cursor position.
+ // TODO(oshima): use the center of character instead of edge.
+ // Binary search may not work for language like arabic.
+ while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) {
+ int pivot_pos = left_pos + (right_pos - left_pos) / 2;
+ int pivot = font.GetStringWidth(text().substr(0, pivot_pos));
+ if (pivot < x) {
+ left = pivot;
+ left_pos = pivot_pos;
+ } else if (pivot == x) {
+ return pivot_pos;
+ } else {
+ right = pivot;
+ right_pos = pivot_pos;
+ }
+ }
+ return left_pos;
+}
+
+std::vector<gfx::Rect> RenderText::GetSubstringBounds(
+ const ui::Range& range) const {
+ size_t start = range.GetMin();
+ size_t end = range.GetMax();
+ gfx::Font font;
+ int start_x = font.GetStringWidth(text().substr(0, start));
+ int end_x = font.GetStringWidth(text().substr(0, end));
+ std::vector<gfx::Rect> bounds;
+ bounds.push_back(gfx::Rect(start_x, 0, end_x - start_x, font.GetHeight()));
+ return bounds;
+}
+
+gfx::Rect RenderText::GetCursorBounds(size_t cursor_pos,
+ bool insert_mode) const {
+ gfx::Font font;
+ int x = font.GetStringWidth(text_.substr(0U, cursor_pos));
+ DCHECK_GE(x, 0);
+ int h = std::min(display_rect_.height(), font.GetHeight());
+ gfx::Rect bounds(x, (display_rect_.height() - h) / 2, 1, h);
+ if (!insert_mode && text_.length() != cursor_pos)
+ bounds.set_width(font.GetStringWidth(text_.substr(0, cursor_pos + 1)) - x);
+ return bounds;
+}
+
+size_t RenderText::GetLeftCursorPosition(size_t position,
+ bool move_by_word) const {
+ if (!move_by_word)
+ return position == 0? position : position - 1;
+ // Notes: We always iterate words from the begining.
+ // This is probably fast enough for our usage, but we may
+ // want to modify WordIterator so that it can start from the
+ // middle of string and advance backwards.
+ base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
+ bool success = iter.Init();
+ DCHECK(success);
+ if (!success)
+ return position;
+ int last = 0;
+ while (iter.Advance()) {
+ if (iter.IsWord()) {
+ size_t begin = iter.pos() - iter.GetString().length();
+ if (begin == position) {
+ // The cursor is at the beginning of a word.
+ // Move to previous word.
+ break;
+ } else if(iter.pos() >= position) {
+ // The cursor is in the middle or at the end of a word.
+ // Move to the top of current word.
+ last = begin;
+ break;
+ } else {
+ last = iter.pos() - iter.GetString().length();
+ }
+ }
+ }
+
+ return last;
+}
+
+size_t RenderText::GetRightCursorPosition(size_t position,
+ bool move_by_word) const {
+ if (!move_by_word)
+ return std::min(position + 1, text().length());
+ base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
+ bool success = iter.Init();
+ DCHECK(success);
+ if (!success)
+ return position;
+ size_t pos = 0;
+ while (iter.Advance()) {
+ pos = iter.pos();
+ if (iter.IsWord() && pos > position) {
+ break;
+ }
+ }
+ return pos;
+}
+
+RenderText::RenderText()
+ : text_(),
+ selection_range_(),
+ cursor_visible_(false),
+ insert_mode_(true),
+ composition_range_(),
+ style_ranges_(),
+ default_style_(),
+ display_rect_(),
+ display_offset_() {
+}
+
+RenderText::~RenderText() {
+}
+
+bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) {
+ return pos == 0 || (isalnum(text()[pos - 1]) && !isalnum(text()[pos])) ||
+ (!isalnum(text()[pos - 1]) && isalnum(text()[pos]));
+}
+
+} // namespace gfx
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
new file mode 100755
index 0000000..bcc2175
--- /dev/null
+++ b/ui/gfx/render_text.h
@@ -0,0 +1,224 @@
+// Copyright (c) 2011 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_GFX_RENDER_TEXT_H_
+#define UI_GFX_RENDER_TEXT_H_
+#pragma once
+
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/i18n/rtl.h"
+#include "base/string16.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/range/range.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/point.h"
+
+namespace {
+
+// Strike line width.
+const int kStrikeWidth = 2;
+
+// Color settings for text, backgrounds and cursor.
+// These are tentative, and should be derived from theme, system
+// settings and current settings.
+// TODO(oshima): Change this to match the standard chrome
+// before dogfooding textfield views.
+const SkColor kSelectedTextColor = SK_ColorWHITE;
+const SkColor kFocusedSelectionColor = SK_ColorCYAN;
+const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
+const SkColor kCursorColor = SK_ColorBLACK;
+
+} // namespace
+
+namespace gfx {
+
+class Canvas;
+class RenderTextTest;
+
+// A visual style applicable to a range of text.
+struct StyleRange {
+ StyleRange();
+
+ Font font;
+ SkColor foreground;
+ bool strike;
+ bool underline;
+ ui::Range range;
+};
+
+typedef std::vector<StyleRange> StyleRanges;
+
+// TODO(msw): Distinguish between logical character and glyph?
+enum BreakType {
+ CHARACTER_BREAK,
+ WORD_BREAK,
+ LINE_BREAK,
+};
+
+// TODO(msw): Implement RenderText[Win|Linux] for Uniscribe/Pango BiDi...
+
+// RenderText represents an abstract model of styled text and its corresponding
+// visual layout. Support is built in for a cursor, a selection, simple styling,
+// complex scripts, and bi-directional text. Implementations provide mechanisms
+// for rendering and translation between logical and visual data.
+class RenderText {
+
+ public:
+ virtual ~RenderText();
+
+ // Creates a platform-specific RenderText instance.
+ static RenderText* CreateRenderText();
+
+ const string16& text() const { return text_; }
+ virtual void SetText(const string16& text);
+
+ bool cursor_visible() const { return cursor_visible_; }
+ void set_cursor_visible(bool visible) { cursor_visible_ = visible; }
+
+ bool insert_mode() const { return insert_mode_; }
+ void toggle_insert_mode() { insert_mode_ = !insert_mode_; }
+
+ bool focused() const { return focused_; }
+ void set_focused(bool focused) { focused_ = focused; }
+
+ const StyleRange& default_style() const { return default_style_; }
+ void set_default_style(StyleRange style) { default_style_ = style; }
+
+ const gfx::Rect& display_rect() const { return display_rect_; }
+ void set_display_rect(const gfx::Rect& r) { display_rect_ = r; }
+
+ size_t GetCursorPosition() const;
+ void SetCursorPosition(const size_t position);
+
+ // Moves the cursor left or right. Cursor movement is visual, meaning that
+ // left and right are relative to screen, not the directionality of the text.
+ // If |select| is false, the selection range is emptied at the new position.
+ // If |break_type| is CHARACTER_BREAK, move to the neighboring character.
+ // If |break_type| is WORD_BREAK, move to the nearest word boundary.
+ // If |break_type| is LINE_BREAK, move to text edge as shown on screen.
+ void MoveCursorLeft(BreakType break_type, bool select);
+ void MoveCursorRight(BreakType break_type, bool select);
+
+ // Moves the cursor to the specified logical |position|.
+ // If |select| is false, the selection range is emptied at the new position.
+ // Returns true if the cursor position or selection range changed.
+ bool MoveCursorTo(size_t position, bool select);
+
+ // Move the cursor to the position associated with the clicked point.
+ // If |select| is false, the selection range is emptied at the new position.
+ bool MoveCursorTo(const gfx::Point& point, bool select);
+
+ const ui::Range& GetSelection() const;
+ void SetSelection(const ui::Range& range);
+
+ // Returns true if the local point is over selected text.
+ bool IsPointInSelection(const gfx::Point& point) const;
+
+ // Selects no text, all text, or the word at the current cursor position.
+ void ClearSelection();
+ void SelectAll();
+ void SelectWord();
+
+ const ui::Range& GetCompositionRange() const;
+ void SetCompositionRange(const ui::Range& composition_range);
+
+ // Apply |style_range| to the internal style model.
+ virtual void ApplyStyleRange(StyleRange style_range);
+
+ // Apply |default_style_| over the entire text range.
+ virtual void ApplyDefaultStyle();
+
+ base::i18n::TextDirection GetTextDirection() const;
+
+ // Get the width of the entire string.
+ int GetStringWidth() const;
+
+ virtual void Draw(gfx::Canvas* canvas);
+
+ // TODO(msw): Deprecate this function. Logical and visual cursors are not
+ // mapped one-to-one. See the selection_range_ TODO for more information.
+ // Get the logical cursor position from a visual point in local coordinates.
+ virtual size_t FindCursorPosition(const gfx::Point& point) const;
+
+ // Get the visual bounds containing the logical substring within |range|.
+ // These bounds could be visually discontiguous if the logical selection range
+ // is split by an odd number of LTR/RTL level change.
+ virtual std::vector<gfx::Rect> GetSubstringBounds(
+ const ui::Range& range) const;
+
+ // Get the visual bounds describing the cursor at |position|. These bounds
+ // typically represent a vertical line, but if |insert_mode| is true they
+ // contain the bounds of the associated glyph.
+ virtual gfx::Rect GetCursorBounds(size_t position, bool insert_mode) const;
+
+ protected:
+ RenderText();
+
+ const StyleRanges& style_ranges() const { return style_ranges_; }
+
+ const gfx::Point& display_offset() const { return display_offset_; }
+
+ // Get the cursor position that visually neighbors |position|.
+ // If |move_by_word| is true, return the neighboring word delimiter position.
+ virtual size_t GetLeftCursorPosition(size_t position,
+ bool move_by_word) const;
+ virtual size_t GetRightCursorPosition(size_t position,
+ bool move_by_word) const;
+
+ private:
+ friend class RenderTextTest;
+
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, DefaultStyle);
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, CustomDefaultStyle);
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange);
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust);
+
+ // Clear out |style_ranges_|.
+ void ClearStyleRanges();
+
+ bool IsPositionAtWordSelectionBoundary(size_t pos);
+
+ // Logical UTF-16 string data to be drawn.
+ string16 text_;
+
+ // TODO(msw): A single logical cursor position doesn't support two potential
+ // visual cursor positions. For example, clicking right of 'c' & 'D' yeilds:
+ // (visually: 'abc|FEDghi' and 'abcFED|ghi', both logically: 'abc|DEFghi').
+ // Similarly, one visual position may have two associated logical positions.
+ // For example, clicking the right side of 'D' and left side of 'g' yields:
+ // (both visually: 'abcFED|ghi', logically: 'abc|DEFghi' and 'abcDEF|ghi').
+ // Update the cursor model with a leading/trailing flag, a level association,
+ // or a disjoint visual position to satisfy the proposed visual behavior.
+ // Logical selection range; the range end is also the logical cursor position.
+ ui::Range selection_range_;
+
+ // The cursor visibility and insert mode.
+ bool cursor_visible_;
+ bool insert_mode_;
+
+ // The focus state of the text.
+ bool focused_;
+
+ // Composition text range.
+ ui::Range composition_range_;
+
+ // List of style ranges. Elements in the list never overlap each other.
+ StyleRanges style_ranges_;
+ // The default text style.
+ StyleRange default_style_;
+
+ // The local display area for rendering the text.
+ gfx::Rect display_rect_;
+ // The offset for the text to be drawn, relative to the display area.
+ gfx::Point display_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderText);
+};
+
+} // namespace gfx
+
+#endif // UI_GFX_RENDER_TEXT_H_
diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc
new file mode 100755
index 0000000..21b3ac4
--- /dev/null
+++ b/ui/gfx/render_text_linux.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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.
+
+#include "ui/gfx/render_text_linux.h"
+
+namespace gfx {
+
+RenderTextLinux::RenderTextLinux()
+ : RenderText() {
+}
+
+RenderTextLinux::~RenderTextLinux() {
+}
+
+RenderText* RenderText::CreateRenderText() {
+ return new RenderTextLinux;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/render_text_linux.h b/ui/gfx/render_text_linux.h
new file mode 100755
index 0000000..2709bea
--- /dev/null
+++ b/ui/gfx/render_text_linux.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 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_GFX_RENDER_TEXT_LINUX_H_
+#define UI_GFX_RENDER_TEXT_LINUX_H_
+#pragma once
+
+#include "ui/gfx/render_text.h"
+
+namespace gfx {
+
+// RenderTextLinux is the Linux implementation of RenderText using Pango.
+class RenderTextLinux : public RenderText {
+ public:
+ RenderTextLinux();
+ virtual ~RenderTextLinux();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(RenderTextLinux);
+};
+
+} // namespace gfx;
+
+#endif // UI_GFX_RENDER_TEXT_LINUX_H_
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
new file mode 100755
index 0000000..e809d41
--- /dev/null
+++ b/ui/gfx/render_text_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright (c) 2011 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.
+
+#include "ui/gfx/render_text.h"
+
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+class RenderTextTest : public testing::Test {
+};
+
+TEST_F(RenderTextTest, DefaultStyle) {
+ // Defaults to empty text with no styles.
+ gfx::RenderText* render_text = gfx::RenderText::CreateRenderText();
+ EXPECT_TRUE(render_text->text().empty());
+ EXPECT_TRUE(render_text->style_ranges().empty());
+
+ // Test that the built-in default style is applied for new text.
+ render_text->SetText(ASCIIToUTF16("abc"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ gfx::StyleRange style;
+ EXPECT_EQ(style.font.GetFontName(),
+ render_text->style_ranges()[0].font.GetFontName());
+ EXPECT_EQ(style.foreground, render_text->style_ranges()[0].foreground);
+ EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
+ EXPECT_EQ(style.strike, render_text->style_ranges()[0].strike);
+ EXPECT_EQ(style.underline, render_text->style_ranges()[0].underline);
+
+ // Test that clearing the text also clears the styles.
+ render_text->SetText(string16());
+ EXPECT_TRUE(render_text->text().empty());
+ EXPECT_TRUE(render_text->style_ranges().empty());
+}
+
+TEST_F(RenderTextTest, CustomDefaultStyle) {
+ // Test a custom default style.
+ gfx::RenderText* render_text = gfx::RenderText::CreateRenderText();
+ gfx::StyleRange color;
+ color.foreground = SK_ColorRED;
+ render_text->set_default_style(color);
+ render_text->SetText(ASCIIToUTF16("abc"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(color.foreground, render_text->style_ranges()[0].foreground);
+
+ // Test that the custom default style persists across clearing text.
+ render_text->SetText(string16());
+ EXPECT_TRUE(render_text->style_ranges().empty());
+ render_text->SetText(ASCIIToUTF16("abc"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(color.foreground, render_text->style_ranges()[0].foreground);
+
+ // Test ApplyDefaultStyle after setting a new default.
+ gfx::StyleRange strike;
+ strike.strike = true;
+ render_text->set_default_style(strike);
+ render_text->ApplyDefaultStyle();
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(true, render_text->style_ranges()[0].strike);
+ EXPECT_EQ(strike.foreground, render_text->style_ranges()[0].foreground);
+}
+
+TEST_F(RenderTextTest, ApplyStyleRange) {
+ gfx::RenderText* render_text = gfx::RenderText::CreateRenderText();
+ render_text->SetText(ASCIIToUTF16("01234"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+
+ // Test ApplyStyleRange (no-op on empty range).
+ gfx::StyleRange empty;
+ empty.range = ui::Range(1, 1);
+ render_text->ApplyStyleRange(empty);
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+
+ // Test ApplyStyleRange (no-op on invalid range).
+ gfx::StyleRange invalid;
+ invalid.range = ui::Range::InvalidRange();
+ render_text->ApplyStyleRange(invalid);
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+
+ // Apply a style with a range contained by an existing range.
+ gfx::StyleRange underline;
+ underline.underline = true;
+ underline.range = ui::Range(2, 3);
+ render_text->ApplyStyleRange(underline);
+ EXPECT_EQ(3U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
+ EXPECT_FALSE(render_text->style_ranges()[0].underline);
+ EXPECT_EQ(ui::Range(2, 3), render_text->style_ranges()[1].range);
+ EXPECT_TRUE(render_text->style_ranges()[1].underline);
+ EXPECT_EQ(ui::Range(3, 5), render_text->style_ranges()[2].range);
+ EXPECT_FALSE(render_text->style_ranges()[2].underline);
+
+ // Apply a style with a range equal to another range.
+ gfx::StyleRange color;
+ color.foreground = SK_ColorWHITE;
+ color.range = ui::Range(2, 3);
+ render_text->ApplyStyleRange(color);
+ EXPECT_EQ(3U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
+ EXPECT_NE(SK_ColorWHITE, render_text->style_ranges()[0].foreground);
+ EXPECT_FALSE(render_text->style_ranges()[0].underline);
+ EXPECT_EQ(ui::Range(2, 3), render_text->style_ranges()[1].range);
+ EXPECT_EQ(SK_ColorWHITE, render_text->style_ranges()[1].foreground);
+ EXPECT_FALSE(render_text->style_ranges()[1].underline);
+ EXPECT_EQ(ui::Range(3, 5), render_text->style_ranges()[2].range);
+ EXPECT_NE(SK_ColorWHITE, render_text->style_ranges()[2].foreground);
+ EXPECT_FALSE(render_text->style_ranges()[2].underline);
+
+ // Apply a style with a range containing an existing range.
+ // This new style also overlaps portions of neighboring ranges.
+ gfx::StyleRange strike;
+ strike.strike = true;
+ strike.range = ui::Range(1, 4);
+ render_text->ApplyStyleRange(strike);
+ EXPECT_EQ(3U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 1), render_text->style_ranges()[0].range);
+ EXPECT_FALSE(render_text->style_ranges()[0].strike);
+ EXPECT_EQ(ui::Range(1, 4), render_text->style_ranges()[1].range);
+ EXPECT_TRUE(render_text->style_ranges()[1].strike);
+ EXPECT_EQ(ui::Range(4, 5), render_text->style_ranges()[2].range);
+ EXPECT_FALSE(render_text->style_ranges()[2].strike);
+
+ // Apply a style overlapping all ranges.
+ gfx::StyleRange strike_underline;
+ strike_underline.strike = true;
+ strike_underline.underline = true;
+ strike_underline.range = ui::Range(0, render_text->text().length());
+ render_text->ApplyStyleRange(strike_underline);
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 5), render_text->style_ranges()[0].range);
+ EXPECT_TRUE(render_text->style_ranges()[0].underline);
+ EXPECT_TRUE(render_text->style_ranges()[0].strike);
+
+ // Apply the default style.
+ render_text->ApplyDefaultStyle();
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 5), render_text->style_ranges()[0].range);
+ EXPECT_FALSE(render_text->style_ranges()[0].underline);
+ EXPECT_FALSE(render_text->style_ranges()[0].strike);
+}
+
+TEST_F(RenderTextTest, StyleRangesAdjust) {
+ // Test that style ranges adjust to the text size.
+ gfx::RenderText* render_text = gfx::RenderText::CreateRenderText();
+ render_text->SetText(ASCIIToUTF16("abcdef"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 6), render_text->style_ranges()[0].range);
+
+ // Test that the range is clipped to the length of shorter text.
+ render_text->SetText(ASCIIToUTF16("abc"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
+
+ // Test that the last range extends to the length of longer text.
+ gfx::StyleRange strike;
+ strike.strike = true;
+ strike.range = ui::Range(2, 3);
+ render_text->ApplyStyleRange(strike);
+ render_text->SetText(ASCIIToUTF16("abcdefghi"));
+ EXPECT_EQ(2U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
+ EXPECT_EQ(ui::Range(2, 9), render_text->style_ranges()[1].range);
+ EXPECT_TRUE(render_text->style_ranges()[1].strike);
+
+ // Test that ranges are removed if they're outside the range of shorter text.
+ render_text->SetText(ASCIIToUTF16("ab"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
+ EXPECT_FALSE(render_text->style_ranges()[0].strike);
+
+ // Test that previously removed ranges don't return.
+ render_text->SetText(ASCIIToUTF16("abcdef"));
+ EXPECT_EQ(1U, render_text->style_ranges().size());
+ EXPECT_EQ(ui::Range(0, 6), render_text->style_ranges()[0].range);
+ EXPECT_FALSE(render_text->style_ranges()[0].strike);
+}
+
+} // namespace gfx
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
new file mode 100755
index 0000000..9f47946
--- /dev/null
+++ b/ui/gfx/render_text_win.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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.
+
+#include "ui/gfx/render_text_win.h"
+
+namespace gfx {
+
+RenderTextWin::RenderTextWin()
+ : RenderText() {
+}
+
+RenderTextWin::~RenderTextWin() {
+}
+
+RenderText* RenderText::CreateRenderText() {
+ return new RenderTextWin;
+}
+
+} // namespace gfx
diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h
new file mode 100755
index 0000000..829888b
--- /dev/null
+++ b/ui/gfx/render_text_win.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 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_GFX_RENDER_TEXT_WIN_H_
+#define UI_GFX_RENDER_TEXT_WIN_H_
+#pragma once
+
+#include "ui/gfx/render_text.h"
+
+namespace gfx {
+
+// RenderTextWin is the Windows implementation of RenderText using Uniscribe.
+class RenderTextWin : public RenderText {
+ public:
+ RenderTextWin();
+ virtual ~RenderTextWin();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderTextWin);
+};
+
+} // namespace gfx;
+
+#endif // UI_GFX_RENDER_TEXT_WIN_H_
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 39cedb6..9306181 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -249,6 +249,12 @@
'gfx/point.h',
'gfx/rect.cc',
'gfx/rect.h',
+ 'gfx/render_text.cc',
+ 'gfx/render_text.h',
+ 'gfx/render_text_linux.cc',
+ 'gfx/render_text_linux.h',
+ 'gfx/render_text_win.cc',
+ 'gfx/render_text_win.h',
'gfx/screen.h',
'gfx/screen_gtk.cc',
'gfx/screen_win.cc',
@@ -396,6 +402,16 @@
'gfx/native_theme_chromeos.h',
],
}],
+ ['toolkit_views==0', {
+ 'sources/': [
+ ['exclude', '^gfx/render_text.cc'],
+ ['exclude', '^gfx/render_text.h'],
+ ['exclude', '^gfx/render_text_linux.cc'],
+ ['exclude', '^gfx/render_text_linux.h'],
+ ['exclude', '^gfx/render_text_win.cc'],
+ ['exclude', '^gfx/render_text_win.h'],
+ ],
+ }],
],
},
{
diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi
index 5c7f9b7..9839df6 100644
--- a/ui/ui_unittests.gypi
+++ b/ui/ui_unittests.gypi
@@ -126,6 +126,11 @@
}],
],
}],
+ ['toolkit_views==1', {
+ 'sources': [
+ 'gfx/render_text_unittest.cc',
+ ],
+ }],
],
},
],
diff --git a/views/controls/textfield/native_textfield_gtk.cc b/views/controls/textfield/native_textfield_gtk.cc
index 78fb2f9..3d799a8 100644
--- a/views/controls/textfield/native_textfield_gtk.cc
+++ b/views/controls/textfield/native_textfield_gtk.cc
@@ -283,17 +283,11 @@ TextInputClient* NativeTextfieldGtk::GetTextInputClient() {
return NULL;
}
-TextStyle* NativeTextfieldGtk::CreateTextStyle() {
- NOTREACHED();
- return NULL;
-}
-
-void NativeTextfieldGtk::ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) {
+void NativeTextfieldGtk::ApplyStyleRange(const gfx::StyleRange& style) {
NOTREACHED();
}
-void NativeTextfieldGtk::ClearAllTextStyles() {
+void NativeTextfieldGtk::ApplyDefaultStyle() {
NOTREACHED();
}
diff --git a/views/controls/textfield/native_textfield_gtk.h b/views/controls/textfield/native_textfield_gtk.h
index 92b4e36..8420fcb 100644
--- a/views/controls/textfield/native_textfield_gtk.h
+++ b/views/controls/textfield/native_textfield_gtk.h
@@ -57,10 +57,8 @@ class NativeTextfieldGtk : public NativeControlGtk,
virtual void HandleFocus() OVERRIDE;
virtual void HandleBlur() OVERRIDE;
virtual TextInputClient* GetTextInputClient() OVERRIDE;
- virtual TextStyle* CreateTextStyle() OVERRIDE;
- virtual void ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) OVERRIDE;
- virtual void ClearAllTextStyles() OVERRIDE;
+ virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE;
+ virtual void ApplyDefaultStyle() OVERRIDE;
virtual void ClearEditHistory() OVERRIDE;
// Overridden from NativeControlGtk:
diff --git a/views/controls/textfield/native_textfield_views.cc b/views/controls/textfield/native_textfield_views.cc
index adc6288..b3f005d 100644
--- a/views/controls/textfield/native_textfield_views.cc
+++ b/views/controls/textfield/native_textfield_views.cc
@@ -16,12 +16,12 @@
#include "ui/base/range/range.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/insets.h"
+#include "ui/gfx/render_text.h"
#include "views/background.h"
#include "views/border.h"
#include "views/controls/focusable_border.h"
#include "views/controls/menu/menu_item_view.h"
#include "views/controls/menu/menu_model_adapter.h"
-#include "views/controls/textfield/text_style.h"
#include "views/controls/textfield/textfield.h"
#include "views/controls/textfield/textfield_controller.h"
#include "views/controls/textfield/textfield_views_model.h"
@@ -37,15 +37,8 @@
namespace {
-// Color settings for text, backgrounds and cursor.
-// These are tentative, and should be derived from theme, system
-// settings and current settings.
-// TODO(oshima): Change this to match the standard chrome
-// before dogfooding textfield views.
-const SkColor kSelectedTextColor = SK_ColorWHITE;
-const SkColor kFocusedSelectionColor = SK_ColorCYAN;
-const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY;
-const SkColor kCursorColor = SK_ColorBLACK;
+// Text color for read only.
+const SkColor kReadonlyTextColor = SK_ColorDKGRAY;
// Parameters to control cursor blinking.
const int kCursorVisibleTimeMs = 800;
@@ -62,9 +55,8 @@ NativeTextfieldViews::NativeTextfieldViews(Textfield* parent)
: textfield_(parent),
ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))),
text_border_(new FocusableBorder()),
- text_offset_(0),
- insert_(true),
is_cursor_visible_(false),
+ is_drop_cursor_visible_(false),
skip_input_method_cancel_composition_(false),
initiating_drag_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)),
@@ -76,6 +68,12 @@ NativeTextfieldViews::NativeTextfieldViews(Textfield* parent)
// Lowercase is not supported.
DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE);
+ // Set the default text style.
+ gfx::StyleRange default_style;
+ default_style.font = textfield_->font();
+ default_style.foreground = textfield_->text_color();
+ GetRenderText()->set_default_style(default_style);
+
set_context_menu_controller(this);
set_drag_controller(this);
}
@@ -105,7 +103,7 @@ bool NativeTextfieldViews::OnMousePressed(const MouseEvent& event) {
initiating_drag_ = false;
switch(aggregated_clicks_) {
case 0:
- if (!IsPointInSelection(event.location()))
+ if (!GetRenderText()->IsPointInSelection(event.location()))
MoveCursorTo(event.location(), event.IsShiftDown());
else
initiating_drag_ = true;
@@ -176,14 +174,14 @@ bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) {
int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) {
DCHECK(CanDrop(event.data()));
- bool is_point_in_selection = IsPointInSelection(event.location());
- is_drop_cursor_visible_ = !is_point_in_selection;
+ bool in_selection = GetRenderText()->IsPointInSelection(event.location());
+ is_drop_cursor_visible_ = !in_selection;
// TODO(msw): Pan over text when the user drags to the visible text edge.
- UpdateCursorBoundsAndTextOffset(FindCursorPosition(event.location()), true);
+ OnCaretBoundsChanged();
SchedulePaint();
if (initiating_drag_) {
- if (is_point_in_selection)
+ if (in_selection)
return ui::DragDropTypes::DRAG_NONE;
return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY :
ui::DragDropTypes::DRAG_MOVE;
@@ -193,11 +191,14 @@ int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) {
int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) {
DCHECK(CanDrop(event.data()));
- DCHECK(!initiating_drag_ || !IsPointInSelection(event.location()));
+ DCHECK(!initiating_drag_ ||
+ !GetRenderText()->IsPointInSelection(event.location()));
OnBeforeUserAction();
skip_input_method_cancel_composition_ = true;
- size_t drop_destination = FindCursorPosition(event.location());
+ // TODO(msw): Remove final reference to FindCursorPosition.
+ size_t drop_destination =
+ GetRenderText()->FindCursorPosition(event.location());
string16 text;
event.data().GetString(&text);
@@ -215,7 +216,7 @@ int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) {
model_->DeleteSelectionAndInsertTextAt(text, drop_destination);
} else {
model_->MoveCursorTo(drop_destination, false);
- // Drop always inserts a text even if insert_ == false.
+ // Drop always inserts text even if the textfield is not in insert mode.
model_->InsertText(text);
}
skip_input_method_cancel_composition_ = false;
@@ -246,14 +247,15 @@ void NativeTextfieldViews::OnBlur() {
}
gfx::NativeCursor NativeTextfieldViews::GetCursor(const MouseEvent& event) {
- bool text = !initiating_drag_ && (event.type() == ui::ET_MOUSE_DRAGGED ||
- !IsPointInSelection(event.location()));
+ bool in_selection = GetRenderText()->IsPointInSelection(event.location());
+ bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED;
+ bool text_cursor = !initiating_drag_ && (drag_event || !in_selection);
#if defined(OS_WIN)
static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM);
static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW);
- return text ? ibeam : arrow;
+ return text_cursor ? ibeam : arrow;
#else
- return text ? gfx::GetCursor(GDK_XTERM) : NULL;
+ return text_cursor ? gfx::GetCursor(GDK_XTERM) : NULL;
#endif
}
@@ -282,7 +284,7 @@ void NativeTextfieldViews::WriteDragDataForView(views::View* sender,
int NativeTextfieldViews::GetDragOperationsForView(views::View* sender,
const gfx::Point& p) {
- if (!textfield_->IsEnabled() || !IsPointInSelection(p))
+ if (!textfield_->IsEnabled() || !GetRenderText()->IsPointInSelection(p))
return ui::DragDropTypes::DRAG_NONE;
if (sender == this && !textfield_->read_only())
return ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY;
@@ -292,19 +294,19 @@ int NativeTextfieldViews::GetDragOperationsForView(views::View* sender,
bool NativeTextfieldViews::CanStartDragForView(View* sender,
const gfx::Point& press_pt,
const gfx::Point& p) {
- return IsPointInSelection(press_pt);
+ return GetRenderText()->IsPointInSelection(press_pt);
}
/////////////////////////////////////////////////////////////////
// NativeTextfieldViews, NativeTextifieldWrapper overrides:
string16 NativeTextfieldViews::GetText() const {
- return model_->text();
+ return model_->GetText();
}
void NativeTextfieldViews::UpdateText() {
model_->SetText(textfield_->text());
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
SchedulePaint();
}
@@ -312,7 +314,7 @@ void NativeTextfieldViews::AppendText(const string16& text) {
if (text.empty())
return;
model_->Append(text);
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
SchedulePaint();
}
@@ -353,17 +355,24 @@ void NativeTextfieldViews::UpdateBackgroundColor() {
}
void NativeTextfieldViews::UpdateReadOnly() {
+ // Update the default text style.
+ gfx::StyleRange default_style(GetRenderText()->default_style());
+ default_style.foreground = textfield_->read_only() ? kReadonlyTextColor :
+ textfield_->text_color();
+ GetRenderText()->set_default_style(default_style);
+ GetRenderText()->ApplyDefaultStyle();
+
SchedulePaint();
OnTextInputTypeChanged();
}
void NativeTextfieldViews::UpdateFont() {
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
}
void NativeTextfieldViews::UpdateIsPassword() {
model_->set_is_password(textfield_->IsPassword());
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
SchedulePaint();
OnTextInputTypeChanged();
}
@@ -385,7 +394,7 @@ void NativeTextfieldViews::UpdateHorizontalMargins() {
gfx::Insets inset = GetInsets();
text_border_->SetInsets(inset.top(), left, inset.bottom(), right);
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
}
void NativeTextfieldViews::UpdateVerticalMargins() {
@@ -393,9 +402,8 @@ void NativeTextfieldViews::UpdateVerticalMargins() {
if (!textfield_->GetVerticalMargins(&top, &bottom))
return;
gfx::Insets inset = GetInsets();
-
text_border_->SetInsets(top, inset.left(), bottom, inset.right());
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
}
bool NativeTextfieldViews::SetFocus() {
@@ -421,12 +429,12 @@ void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const {
void NativeTextfieldViews::SelectRange(const ui::Range& range) {
model_->SelectRange(range);
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
SchedulePaint();
}
size_t NativeTextfieldViews::GetCursorPosition() const {
- return model_->cursor_pos();
+ return model_->GetCursorPosition();
}
bool NativeTextfieldViews::HandleKeyPressed(const KeyEvent& e) {
@@ -442,6 +450,7 @@ bool NativeTextfieldViews::HandleKeyReleased(const KeyEvent& e) {
}
void NativeTextfieldViews::HandleFocus() {
+ GetRenderText()->set_focused(true);
is_cursor_visible_ = true;
SchedulePaint();
OnCaretBoundsChanged();
@@ -453,6 +462,7 @@ void NativeTextfieldViews::HandleFocus() {
}
void NativeTextfieldViews::HandleBlur() {
+ GetRenderText()->set_focused(false);
// Stop blinking cursor.
cursor_timer_.RevokeAll();
if (is_cursor_visible_) {
@@ -536,23 +546,25 @@ void NativeTextfieldViews::ExecuteCommand(int command_id) {
OnAfterUserAction();
}
-TextStyle* NativeTextfieldViews::CreateTextStyle() {
- return model_->CreateTextStyle();
-}
-
-void NativeTextfieldViews::ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) {
- model_->ApplyTextStyle(style, range);
+void NativeTextfieldViews::ApplyStyleRange(const gfx::StyleRange& style) {
+ GetRenderText()->ApplyStyleRange(style);
SchedulePaint();
}
-void NativeTextfieldViews::ClearAllTextStyles() {
- model_->ClearAllTextStyles();
+void NativeTextfieldViews::ApplyDefaultStyle() {
+ GetRenderText()->ApplyDefaultStyle();
SchedulePaint();
}
void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) {
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ // Set the RenderText display area.
+ gfx::Insets insets = GetInsets();
+ gfx::Rect display_rect(insets.left(),
+ insets.top(),
+ width() - insets.width(),
+ height() - insets.height());
+ GetRenderText()->set_display_rect(display_rect);
+ OnCaretBoundsChanged();
}
///////////////////////////////////////////////////////////////////////////////
@@ -602,7 +614,7 @@ void NativeTextfieldViews::InsertText(const string16& text) {
OnBeforeUserAction();
skip_input_method_cancel_composition_ = true;
- if (insert_)
+ if (GetRenderText()->insert_mode())
model_->InsertText(text);
else
model_->ReplaceText(text);
@@ -619,7 +631,7 @@ void NativeTextfieldViews::InsertChar(char16 ch, int flags) {
OnBeforeUserAction();
skip_input_method_cancel_composition_ = true;
- if (insert_)
+ if (GetRenderText()->insert_mode())
model_->InsertChar(ch);
else
model_->ReplaceChar(ch);
@@ -637,7 +649,9 @@ ui::TextInputType NativeTextfieldViews::GetTextInputType() {
}
gfx::Rect NativeTextfieldViews::GetCaretBounds() {
- return cursor_bounds_;
+ gfx::RenderText* render_text = GetRenderText();
+ return render_text->GetCursorBounds(render_text->GetCursorPosition(),
+ render_text->insert_mode());
}
bool NativeTextfieldViews::HasCompositionText() {
@@ -725,12 +739,8 @@ void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() {
textfield_->GetInputMethod()->CancelComposition(textfield_);
}
-const gfx::Font& NativeTextfieldViews::GetFont() const {
- return textfield_->font();
-}
-
-SkColor NativeTextfieldViews::GetTextColor() const {
- return textfield_->text_color();
+gfx::RenderText* NativeTextfieldViews::GetRenderText() const {
+ return model_->render_text();
}
void NativeTextfieldViews::UpdateCursor() {
@@ -743,108 +753,18 @@ void NativeTextfieldViews::UpdateCursor() {
}
void NativeTextfieldViews::RepaintCursor() {
- gfx::Rect r = cursor_bounds_;
+ gfx::Rect r(GetCaretBounds());
r.Inset(-1, -1, -1, -1);
SchedulePaintInRect(r);
}
-gfx::Rect NativeTextfieldViews::GetCursorBounds(size_t cursor_pos,
- bool insert_mode) const {
- string16 text = model_->GetVisibleText();
- const gfx::Font& font = GetFont();
- int x = font.GetStringWidth(text.substr(0U, cursor_pos));
- DCHECK_GE(x, 0);
- int h = std::min(height() - GetInsets().height(), font.GetHeight());
- gfx::Rect bounds(x, (height() - h) / 2, 0, h);
- if (!insert_mode && text.length() != cursor_pos)
- bounds.set_width(font.GetStringWidth(text.substr(0, cursor_pos + 1)) - x);
- return bounds;
-}
-
-
-void NativeTextfieldViews::UpdateCursorBoundsAndTextOffset(size_t cursor_pos,
- bool insert_mode) {
- if (bounds().IsEmpty())
- return;
-
- // TODO(oshima): bidi
- int width = bounds().width() - GetInsets().width();
- int full_width = GetFont().GetStringWidth(model_->GetVisibleText());
- cursor_bounds_ = GetCursorBounds(cursor_pos, insert_mode);
-
- if (full_width < width) {
- // Show all text whenever the text fits to the size.
- text_offset_ = 0;
- } else if ((text_offset_ + cursor_bounds_.right()) > width) {
- // when the cursor overflows to the right
- text_offset_ = width - cursor_bounds_.right();
- } else if ((text_offset_ + cursor_bounds_.x()) < 0) {
- // when the cursor overflows to the left
- text_offset_ = -cursor_bounds_.x();
- } else if (full_width > width && text_offset_ + full_width < width) {
- // when the cursor moves within the textfield with the text
- // longer than the field.
- text_offset_ = width - full_width;
- } else {
- // move cursor freely.
- }
- // shift cursor bounds to fit insets.
- cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + GetInsets().left());
-
- OnCaretBoundsChanged();
-}
-
void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) {
- gfx::Insets insets = GetInsets();
-
canvas->Save();
- canvas->ClipRectInt(insets.left(), insets.top(),
- width() - insets.width(), height() - insets.height());
-
- // TODO(oshima): bidi support
- // TODO(varunjain): re-implement this so only that dirty text is painted.
- TextfieldViewsModel::TextFragments fragments;
- model_->GetFragments(&fragments);
- int x_offset = text_offset_ + insets.left();
- int y = insets.top();
- int text_height = height() - insets.height();
- SkColor selection_color =
- textfield_->HasFocus() ?
- kFocusedSelectionColor : kUnfocusedSelectionColor;
- gfx::Font font = GetFont();
- gfx::Rect selection_bounds = model_->GetSelectionBounds(font);
-
- if (!selection_bounds.IsEmpty()) {
- canvas->FillRectInt(selection_color,
- x_offset + selection_bounds.x(),
- (height() - selection_bounds.height()) / 2,
- selection_bounds.width(),
- selection_bounds.height());
- }
-
- for (TextfieldViewsModel::TextFragments::const_iterator iter =
- fragments.begin();
- iter != fragments.end();
- iter++) {
- string16 text = model_->GetVisibleText(iter->range.start(),
- iter->range.end());
- // TODO(oshima): This does not give the accurate position due to
- // kerning. Figure out how to do.
- int width = font.GetStringWidth(text);
- iter->style->DrawString(canvas, text, font, textfield_->read_only(),
- x_offset, y, width, text_height);
- x_offset += width;
- }
+ GetRenderText()->set_cursor_visible(is_drop_cursor_visible_ ||
+ (is_cursor_visible_ && !model_->HasSelection()));
+ // Draw the text, cursor, and selection.
+ GetRenderText()->Draw(canvas);
canvas->Restore();
-
- // Paint cursor. Replace cursor is drawn as rectangle for now.
- if (textfield_->IsEnabled() && (is_drop_cursor_visible_ ||
- (is_cursor_visible_ && !model_->HasSelection())))
- canvas->DrawRectInt(kCursorColor,
- cursor_bounds_.x(),
- cursor_bounds_.y(),
- cursor_bounds_.width(),
- cursor_bounds_.height());
}
bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
@@ -889,21 +809,21 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
cursor_changed = text_changed = Paste();
break;
case ui::VKEY_RIGHT:
- control ? model_->MoveCursorToNextWord(selection)
- : model_->MoveCursorRight(selection);
+ model_->MoveCursorRight(
+ control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection);
cursor_changed = true;
break;
case ui::VKEY_LEFT:
- control ? model_->MoveCursorToPreviousWord(selection)
- : model_->MoveCursorLeft(selection);
+ model_->MoveCursorLeft(
+ control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection);
cursor_changed = true;
break;
case ui::VKEY_END:
- model_->MoveCursorToEnd(selection);
+ model_->MoveCursorRight(gfx::LINE_BREAK, selection);
cursor_changed = true;
break;
case ui::VKEY_HOME:
- model_->MoveCursorToHome(selection);
+ model_->MoveCursorLeft(gfx::LINE_BREAK, selection);
cursor_changed = true;
break;
case ui::VKEY_BACK:
@@ -916,11 +836,11 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
#if defined(OS_WIN)
break;
#else
- model_->MoveCursorToHome(true);
+ model_->MoveCursorLeft(gfx::LINE_BREAK, true);
#endif
} else if (control) {
// If only control is pressed, then erase the previous word.
- model_->MoveCursorToPreviousWord(true);
+ model_->MoveCursorLeft(gfx::WORD_BREAK, true);
}
}
text_changed = model_->Backspace();
@@ -936,17 +856,17 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
#if defined(OS_WIN)
break;
#else
- model_->MoveCursorToEnd(true);
+ model_->MoveCursorRight(gfx::LINE_BREAK, true);
#endif
} else if (control) {
// If only control is pressed, then erase the next word.
- model_->MoveCursorToNextWord(true);
+ model_->MoveCursorRight(gfx::WORD_BREAK, true);
}
}
cursor_changed = text_changed = model_->Delete();
break;
case ui::VKEY_INSERT:
- insert_ = !insert_;
+ GetRenderText()->toggle_insert_mode();
cursor_changed = true;
break;
default:
@@ -963,52 +883,11 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
return false;
}
-size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const {
- // TODO(oshima): BIDI/i18n support.
- gfx::Font font = GetFont();
- gfx::Insets insets = GetInsets();
- string16 text = model_->GetVisibleText();
- int left = 0;
- int left_pos = 0;
- int right = font.GetStringWidth(text);
- int right_pos = text.length();
-
- int x = point.x() - insets.left() - text_offset_;
- if (x <= left) return left_pos;
- if (x >= right) return right_pos;
- // binary searching the cursor position.
- // TODO(oshima): use the center of character instead of edge.
- // Binary search may not work for language like arabic.
- while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) {
- int pivot_pos = left_pos + (right_pos - left_pos) / 2;
- int pivot = font.GetStringWidth(text.substr(0, pivot_pos));
- if (pivot < x) {
- left = pivot;
- left_pos = pivot_pos;
- } else if (pivot == x) {
- return pivot_pos;
- } else {
- right = pivot;
- right_pos = pivot_pos;
- }
- }
- return left_pos;
-}
-
-bool NativeTextfieldViews::IsPointInSelection(const gfx::Point& point) const {
- ui::Range range;
- GetSelectedRange(&range);
- size_t pos = FindCursorPosition(point);
- return (pos >= range.GetMin() && pos < range.GetMax());
-}
-
bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) {
- size_t pos = FindCursorPosition(point);
- if (model_->MoveCursorTo(pos, select)) {
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
- return true;
- }
- return false;
+ if (!model_->MoveCursorTo(point, select))
+ return false;
+ OnCaretBoundsChanged();
+ return true;
}
void NativeTextfieldViews::PropagateTextChange() {
@@ -1024,7 +903,7 @@ void NativeTextfieldViews::UpdateAfterChange(bool text_changed,
RepaintCursor();
}
if (text_changed || cursor_changed) {
- UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_);
+ OnCaretBoundsChanged();
SchedulePaint();
}
}
@@ -1078,7 +957,7 @@ bool NativeTextfieldViews::Paste() {
// Calls TextfieldController::ContentsChanged() explicitly if the paste action
// did not change the content at all. See http://crbug.com/79002
- if (success && model_->text() == textfield_->text()) {
+ if (success && GetText() == textfield_->text()) {
TextfieldController* controller = textfield_->GetController();
if (controller)
controller->ContentsChanged(textfield_, textfield_->text());
diff --git a/views/controls/textfield/native_textfield_views.h b/views/controls/textfield/native_textfield_views.h
index 4851549..ea6fec8 100644
--- a/views/controls/textfield/native_textfield_views.h
+++ b/views/controls/textfield/native_textfield_views.h
@@ -114,10 +114,8 @@ class NativeTextfieldViews : public View,
virtual void HandleFocus() OVERRIDE;
virtual void HandleBlur() OVERRIDE;
virtual TextInputClient* GetTextInputClient() OVERRIDE;
- virtual TextStyle* CreateTextStyle() OVERRIDE;
- virtual void ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) OVERRIDE;
- virtual void ClearAllTextStyles() OVERRIDE;
+ virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE;
+ virtual void ApplyDefaultStyle() OVERRIDE;
virtual void ClearEditHistory() OVERRIDE;
// ui::SimpleMenuModel::Delegate overrides
@@ -164,11 +162,8 @@ class NativeTextfieldViews : public View,
// Overridden from TextfieldViewsModel::Delegate:
virtual void OnCompositionTextConfirmedOrCleared() OVERRIDE;
- // Returns the Textfield's font.
- const gfx::Font& GetFont() const;
-
- // Returns the Textfield's text color.
- SkColor GetTextColor() const;
+ // Returns the TextfieldViewsModel's text/cursor/selection rendering model.
+ gfx::RenderText* GetRenderText() const;
// A callback function to periodically update the cursor state.
void UpdateCursor();
@@ -176,9 +171,6 @@ class NativeTextfieldViews : public View,
// Repaint the cursor.
void RepaintCursor();
- // Returns the bounds of character at the current cursor.
- gfx::Rect GetCursorBounds(size_t cursor_pos, bool insert_mode) const;
-
// Update the cursor_bounds and text_offset.
void UpdateCursorBoundsAndTextOffset(size_t cursor_pos, bool insert_mode);
@@ -187,12 +179,6 @@ class NativeTextfieldViews : public View,
// Handle the keyevent.
bool HandleKeyEvent(const KeyEvent& key_event);
- // Find a cusor position for given |point| in this views coordinates.
- size_t FindCursorPosition(const gfx::Point& point) const;
-
- // Returns true if the local point is over the selected range of text.
- bool IsPointInSelection(const gfx::Point& point) const;
-
// Helper function to call MoveCursorTo on the TextfieldViewsModel.
bool MoveCursorTo(const gfx::Point& point, bool select);
@@ -236,16 +222,8 @@ class NativeTextfieldViews : public View,
// The reference to the border class. The object is owned by View::border_.
FocusableBorder* text_border_;
- // The x offset for the text to be drawn, without insets;
- int text_offset_;
-
- // True if the textfield is in insert mode.
- bool insert_;
-
- // The local bounds and visibility of the textfield's text cursor.
- gfx::Rect cursor_bounds_;
+ // The textfield's text and drop cursor visibility.
bool is_cursor_visible_;
-
// The drop cursor is a visual cue for where dragged text will be dropped.
bool is_drop_cursor_visible_;
diff --git a/views/controls/textfield/native_textfield_views_unittest.cc b/views/controls/textfield/native_textfield_views_unittest.cc
index 31a5e51..0c8ec2e 100644
--- a/views/controls/textfield/native_textfield_views_unittest.cc
+++ b/views/controls/textfield/native_textfield_views_unittest.cc
@@ -15,6 +15,7 @@
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/gfx/render_text.h"
#include "views/controls/textfield/native_textfield_views.h"
#include "views/controls/textfield/textfield.h"
#include "views/controls/textfield/textfield_controller.h"
@@ -218,9 +219,8 @@ class NativeTextfieldViewsTest : public ViewsTestBase,
}
int GetCursorPositionX(int cursor_pos) {
- const string16 text = textfield_->text().substr(0, cursor_pos);
- return textfield_view_->GetInsets().left() + textfield_view_->text_offset_ +
- textfield_view_->GetFont().GetStringWidth(text);
+ gfx::RenderText* render_text = textfield_view_->GetRenderText();
+ return render_text->GetCursorBounds(cursor_pos, false).x();
}
// We need widget to populate wrapper class.
@@ -254,12 +254,12 @@ TEST_F(NativeTextfieldViewsTest, ModelChangesTest) {
last_contents_.clear();
textfield_->SetText(ASCIIToUTF16("this is"));
- EXPECT_STR_EQ("this is", model_->text());
+ EXPECT_STR_EQ("this is", model_->GetText());
EXPECT_STR_EQ("this is", textfield_->text());
EXPECT_TRUE(last_contents_.empty());
textfield_->AppendText(ASCIIToUTF16(" a test"));
- EXPECT_STR_EQ("this is a test", model_->text());
+ EXPECT_STR_EQ("this is a test", model_->GetText());
EXPECT_STR_EQ("this is a test", textfield_->text());
EXPECT_TRUE(last_contents_.empty());
diff --git a/views/controls/textfield/native_textfield_win.cc b/views/controls/textfield/native_textfield_win.cc
index f53e67b..97e876d6 100644
--- a/views/controls/textfield/native_textfield_win.cc
+++ b/views/controls/textfield/native_textfield_win.cc
@@ -367,17 +367,11 @@ TextInputClient* NativeTextfieldWin::GetTextInputClient() {
return NULL;
}
-TextStyle* NativeTextfieldWin::CreateTextStyle() {
- NOTREACHED();
- return NULL;
-}
-
-void NativeTextfieldWin::ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) {
+void NativeTextfieldWin::ApplyStyleRange(const gfx::StyleRange& style) {
NOTREACHED();
}
-void NativeTextfieldWin::ClearAllTextStyles() {
+void NativeTextfieldWin::ApplyDefaultStyle() {
NOTREACHED();
}
diff --git a/views/controls/textfield/native_textfield_win.h b/views/controls/textfield/native_textfield_win.h
index bb0fc2b..ceabb34 100644
--- a/views/controls/textfield/native_textfield_win.h
+++ b/views/controls/textfield/native_textfield_win.h
@@ -86,10 +86,8 @@ class NativeTextfieldWin
virtual void HandleFocus() OVERRIDE;
virtual void HandleBlur() OVERRIDE;
virtual TextInputClient* GetTextInputClient() OVERRIDE;
- virtual TextStyle* CreateTextStyle() OVERRIDE;
- virtual void ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) OVERRIDE;
- virtual void ClearAllTextStyles() OVERRIDE;
+ virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE;
+ virtual void ApplyDefaultStyle() OVERRIDE;
virtual void ClearEditHistory() OVERRIDE;
// Overridden from ui::SimpleMenuModel::Delegate:
diff --git a/views/controls/textfield/native_textfield_wrapper.h b/views/controls/textfield/native_textfield_wrapper.h
index 8d694eb..6cef09f 100644
--- a/views/controls/textfield/native_textfield_wrapper.h
+++ b/views/controls/textfield/native_textfield_wrapper.h
@@ -11,6 +11,7 @@
namespace gfx {
class Insets;
+struct StyleRange;
} // namespace gfx
namespace ui {
@@ -22,7 +23,6 @@ namespace views {
class KeyEvent;
class Textfield;
class TextInputClient;
-class TextStyle;
class View;
// An interface implemented by an object that provides a platform-native
@@ -126,17 +126,12 @@ class NativeTextfieldWrapper {
// support text input.
virtual TextInputClient* GetTextInputClient() = 0;
- // Creates a new TextStyle for this textfield.
- // See |Textfield::CreateTextStyle| for detail.
- virtual TextStyle* CreateTextStyle() = 0;
+ // Applies the |style| to the text specified by its range.
+ // See |Textfield::ApplyStyleRange| for detail.
+ virtual void ApplyStyleRange(const gfx::StyleRange& style) = 0;
- // Applies the |style| to the text specified by the |range|.
- // See |Textfield::ApplyTextStyle| for detail.
- virtual void ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) = 0;
-
- // Clears all text styles in this textfield.
- virtual void ClearAllTextStyles() = 0;
+ // Applies the default style to the textfield.
+ virtual void ApplyDefaultStyle() = 0;
// Clears Edit history.
virtual void ClearEditHistory() = 0;
diff --git a/views/controls/textfield/text_style.cc b/views/controls/textfield/text_style.cc
deleted file mode 100644
index 18400c6..0000000
--- a/views/controls/textfield/text_style.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "views/controls/textfield/text_style.h"
-
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/canvas_skia.h"
-#include "ui/gfx/font.h"
-
-namespace {
-// Text color for read only.
-const SkColor kReadonlyTextColor = SK_ColorDKGRAY;
-
-// Strike line width.
-const int kStrikeWidth = 2;
-}
-
-namespace views {
-
-TextStyle::TextStyle()
- : foreground_(SK_ColorBLACK),
- strike_(false),
- underline_(false) {
-}
-
-TextStyle::~TextStyle() {
-}
-
-void TextStyle::DrawString(gfx::Canvas* canvas,
- string16& text,
- gfx::Font& base_font,
- bool readonly,
- int x, int y, int width, int height) const {
- SkColor text_color = readonly ? kReadonlyTextColor : foreground_;
-
- gfx::Font font = underline_ ?
- base_font.DeriveFont(0, base_font.GetStyle() | gfx::Font::UNDERLINED) :
- base_font;
- canvas->DrawStringInt(text, font, text_color, x, y, width, height);
- if (strike_) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kFill_Style);
- paint.setColor(text_color);
- paint.setStrokeWidth(kStrikeWidth);
- canvas->AsCanvasSkia()->drawLine(
- SkIntToScalar(x), SkIntToScalar(y + height),
- SkIntToScalar(x + width), SkIntToScalar(y),
- paint);
- }
-}
-
-} // namespace views
diff --git a/views/controls/textfield/text_style.h b/views/controls/textfield/text_style.h
deleted file mode 100644
index e489aee..0000000
--- a/views/controls/textfield/text_style.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2011 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 VIEWS_CONTROLS_TEXTFIELD_TEXT_STYLE_H_
-#define VIEWS_CONTROLS_TEXTFIELD_TEXT_STYLE_H_
-#pragma once
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "base/string16.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace gfx {
-class Canvas;
-class Font;
-}
-
-namespace views {
-
-// A class that specifies text style for TextfieldViews.
-// TODO(suzhe|oshima): support underline color and thick style.
-class TextStyle {
- public:
- // Foreground color for the text.
- void set_foreground(SkColor color) { foreground_ = color; }
-
- // Draws diagnoal strike acrosss the text.
- void set_strike(bool strike) { strike_ = strike; }
-
- // Adds underline to the text.
- void set_underline(bool underline) { underline_ = underline; }
-
- private:
- friend class NativeTextfieldViews;
- friend class TextfieldViewsModel;
-
- FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, TextStyleTest);
- FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_CompositionText);
- FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, CompositionTextTest);
-
- TextStyle();
- virtual ~TextStyle();
-
- SkColor foreground() const { return foreground_; }
- bool underline() const { return underline_; }
-
- // Draw string to the canvas within the region given
- // by |x|,|y|,|width|,|height|.
- void DrawString(gfx::Canvas* canvas,
- string16& text,
- gfx::Font& base_font,
- bool read_only,
- int x, int y, int width, int height) const;
-
- SkColor foreground_;
- bool strike_;
- bool underline_;
-
- DISALLOW_COPY_AND_ASSIGN(TextStyle);
-};
-
-} // namespace views
-
-#endif // VIEWS_CONTROLS_TEXTFIELD_TEXT_STYLE_H_
diff --git a/views/controls/textfield/textfield.cc b/views/controls/textfield/textfield.cc
index 9cd6185..a51beda 100644
--- a/views/controls/textfield/textfield.cc
+++ b/views/controls/textfield/textfield.cc
@@ -261,20 +261,14 @@ size_t Textfield::GetCursorPosition() const {
return native_wrapper_->GetCursorPosition();
}
-TextStyle* Textfield::CreateTextStyle() {
+void Textfield::ApplyStyleRange(const gfx::StyleRange& style) {
DCHECK(native_wrapper_);
- return native_wrapper_->CreateTextStyle();
+ return native_wrapper_->ApplyStyleRange(style);
}
-void Textfield::ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) {
+void Textfield::ApplyDefaultStyle() {
DCHECK(native_wrapper_);
- return native_wrapper_->ApplyTextStyle(style, range);
-}
-
-void Textfield::ClearAllTextStyles() {
- DCHECK(native_wrapper_);
- native_wrapper_->ClearAllTextStyles();
+ native_wrapper_->ApplyDefaultStyle();
}
void Textfield::ClearEditHistory() {
diff --git a/views/controls/textfield/textfield.h b/views/controls/textfield/textfield.h
index 3965050..66675d5 100644
--- a/views/controls/textfield/textfield.h
+++ b/views/controls/textfield/textfield.h
@@ -30,6 +30,10 @@
#include "views/controls/textfield/native_textfield_wrapper.h"
#endif
+namespace gfx {
+struct StyleRange;
+} // namespace gfx
+
namespace ui {
class Range;
} // namespace ui
@@ -39,7 +43,6 @@ namespace views {
class KeyEvent;
class NativeTextfieldWrapper;
class TextfieldController;
-class TextStyle;
// This class implements a View that wraps a native text (edit) field.
class Textfield : public View {
@@ -186,24 +189,14 @@ class Textfield : public View {
// only and has to be called after the wrapper is created.
size_t GetCursorPosition() const;
- // Creates a new TextStyle for this textfield. The object is owned
- // by the textfield and gets deleted when the textfield is deleted.
- // This is views-implementation only and has to be called after the
- // wrapper is created.
- TextStyle* CreateTextStyle();
-
- // Applies the |style| to the text specified by the |range|. If
- // there is already a style applied in the |range|, the style of the
- // overlapping part will be replaced by this sytle. The style will
- // be ignored if range is empty or invalid. This is
- // views-implementation only and has to be called after the wrapper
- // is created.
- void ApplyTextStyle(const TextStyle* style, const ui::Range& range);
-
- // Clears All TextStyles.
- // This is views-implementation only and has to be called after the
- // wrapper is created.
- void ClearAllTextStyles();
+ // Applies |style| to the text specified by its range. The style will be
+ // ignored if range is empty or invalid. This is views-implementation only and
+ // has to be called after the wrapper is created.
+ void ApplyStyleRange(const gfx::StyleRange& style);
+
+ // Applies the default style to the textfield. This is views-implementation
+ // only and has to be called after the wrapper is created.
+ void ApplyDefaultStyle();
// Clears Edit history.
void ClearEditHistory();
diff --git a/views/controls/textfield/textfield_views_model.cc b/views/controls/textfield/textfield_views_model.cc
index 1605546..548e014 100644
--- a/views/controls/textfield/textfield_views_model.cc
+++ b/views/controls/textfield/textfield_views_model.cc
@@ -13,8 +13,9 @@
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/range/range.h"
+#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
-#include "views/controls/textfield/text_style.h"
+#include "ui/gfx/render_text.h"
#include "views/controls/textfield/textfield.h"
#include "views/views_delegate.h"
@@ -256,116 +257,8 @@ class DeleteEdit : public Edit {
}
};
-struct TextStyleRange {
- TextStyleRange(const TextStyle* s, size_t start, size_t end)
- : style(s),
- range(start, end) {
- }
- TextStyleRange(const TextStyle* s, const ui::Range& r)
- : style(s),
- range(r) {
- }
- const TextStyle *style;
- ui::Range range;
-};
-
} // namespace internal
-namespace {
-
-using views::internal::TextStyleRange;
-
-static bool TextStyleRangeComparator(const TextStyleRange* i,
- const TextStyleRange* j) {
- return i->range.start() < j->range.start();
-}
-
-#ifndef NDEBUG
-// A test function to check TextStyleRanges' invariant condition:
-// "no overlapping range".
-bool CheckInvariant(const TextStyleRanges* style_ranges) {
- TextStyleRanges copy = *style_ranges;
- std::sort(copy.begin(), copy.end(), TextStyleRangeComparator);
-
- for (TextStyleRanges::size_type i = 0; i < copy.size() - 1; i++) {
- ui::Range& former = copy[i]->range;
- ui::Range& latter = copy[i + 1]->range;
- if (former.is_empty()) {
- LOG(WARNING) << "Empty range at " << i << " :" << former;
- return false;
- }
- if (!former.IsValid()) {
- LOG(WARNING) << "Invalid range at " << i << " :" << former;
- return false;
- }
- if (former.GetMax() > latter.GetMin()) {
- LOG(WARNING) <<
- "Sorting error. former:" << former << " latter:" << latter;
- return false;
- }
- if (former.Intersects(latter)) {
- LOG(ERROR) << "overlapping style range found: former=" << former
- << ", latter=" << latter;
- return false;
- }
- }
- if ((*copy.rbegin())->range.is_empty()) {
- LOG(WARNING) << "Empty range at end";
- return false;
- }
- if (!(*copy.rbegin())->range.IsValid()) {
- LOG(WARNING) << "Invalid range at end";
- return false;
- }
- return true;
-}
-#endif
-
-void InsertStyle(TextStyleRanges* style_ranges,
- TextStyleRange* text_style_range) {
- const ui::Range& range = text_style_range->range;
- if (range.is_empty() || !range.IsValid()) {
- delete text_style_range;
- return;
- }
- CHECK(!range.is_reversed());
-
- // Invariant condition: all items in the range has no overlaps.
- TextStyleRanges::size_type index = 0;
- while (index < style_ranges->size()) {
- TextStyleRange* current = (*style_ranges)[index];
- if (range.Contains(current->range)) {
- style_ranges->erase(style_ranges->begin() + index);
- delete current;
- continue;
- } else if (current->range.Contains(range) &&
- range.start() != current->range.start() &&
- range.end() != current->range.end()) {
- // Split current style into two styles.
- style_ranges->push_back(
- new TextStyleRange(current->style,
- range.GetMax(), current->range.GetMax()));
- current->range.set_end(range.GetMin());
- } else if (range.Intersects(current->range)) {
- if (current->range.GetMax() <= range.GetMax()) {
- current->range.set_end(range.GetMin());
- } else {
- current->range.set_start(range.GetMax());
- }
- } else {
- // No change needed. Pass it through.
- }
- index ++;
- }
- // Add the new range at the end.
- style_ranges->push_back(text_style_range);
-#ifndef NDEBUG
- DCHECK(CheckInvariant(style_ranges));
-#endif
-}
-
-} // namespace
-
using internal::Edit;
using internal::DeleteEdit;
using internal::InsertEdit;
@@ -383,70 +276,18 @@ TextfieldViewsModel::Delegate::~Delegate() {
TextfieldViewsModel::TextfieldViewsModel(Delegate* delegate)
: delegate_(delegate),
- cursor_pos_(0),
- selection_start_(0),
- composition_start_(0),
- composition_end_(0),
+ render_text_(gfx::RenderText::CreateRenderText()),
is_password_(false),
- current_edit_(edit_history_.end()),
- sort_style_ranges_(false) {
+ current_edit_(edit_history_.end()) {
}
TextfieldViewsModel::~TextfieldViewsModel() {
ClearEditHistory();
ClearComposition();
- ClearAllTextStyles();
- TextStyles::iterator begin = text_styles_.begin();
- TextStyles::iterator end = text_styles_.end();
- while (begin != end) {
- TextStyles::iterator temp = begin;
- ++begin;
- delete *temp;
- }
}
-void TextfieldViewsModel::GetFragments(TextFragments* fragments) {
- static const TextStyle* kNormalStyle = new TextStyle();
-
- if (sort_style_ranges_) {
- sort_style_ranges_ = false;
- std::sort(style_ranges_.begin(), style_ranges_.end(),
- TextStyleRangeComparator);
- }
-
- // If a user is compositing text, use composition's style.
- // TODO(oshima): ask suzhe for expected behavior.
- const TextStyleRanges& ranges = composition_style_ranges_.size() > 0 ?
- composition_style_ranges_ : style_ranges_;
- TextStyleRanges::const_iterator next_ = ranges.begin();
-
- DCHECK(fragments);
- fragments->clear();
- size_t current = 0;
- size_t end = text_.length();
- while(next_ != ranges.end()) {
- const TextStyleRange* text_style_range = *next_++;
- const ui::Range& range = text_style_range->range;
- const TextStyle* style = text_style_range->style;
-
- DCHECK(!range.is_empty());
- DCHECK(range.IsValid());
- if (range.is_empty() || !range.IsValid())
- continue;
-
- size_t start = std::min(range.start(), end);
-
- if (start == end) // Exit loop if it reached the end.
- break;
- else if (current < start) // Fill the gap to next style with normal text.
- fragments->push_back(TextFragment(current, start, kNormalStyle));
-
- current = std::min(range.end(), end);
- fragments->push_back(TextFragment(start, current, style));
- }
- // If there is any text left add it as normal text.
- if (current != end)
- fragments->push_back(TextFragment(current, end, kNormalStyle));
+const string16& TextfieldViewsModel::GetText() const {
+ return render_text_->text();
}
bool TextfieldViewsModel::SetText(const string16& text) {
@@ -455,10 +296,10 @@ bool TextfieldViewsModel::SetText(const string16& text) {
ConfirmCompositionText();
changed = true;
}
- if (text_ != text) {
+ if (GetText() != text) {
if (changed) // No need to remember composition.
Undo();
- size_t old_cursor = cursor_pos_;
+ size_t old_cursor = GetCursorPosition();
size_t new_cursor = old_cursor > text.length() ? text.length() : old_cursor;
SelectAll();
// If there is a composition text, don't merge with previous edit.
@@ -469,7 +310,7 @@ bool TextfieldViewsModel::SetText(const string16& text) {
new_cursor,
text,
0U);
- cursor_pos_ = new_cursor;
+ render_text_->SetCursorPosition(new_cursor);
}
ClearSelection();
return changed;
@@ -478,10 +319,10 @@ bool TextfieldViewsModel::SetText(const string16& text) {
void TextfieldViewsModel::Append(const string16& text) {
if (HasCompositionText())
ConfirmCompositionText();
- size_t save = cursor_pos_;
- MoveCursorToEnd(false);
+ size_t save = GetCursorPosition();
+ MoveCursorRight(gfx::LINE_BREAK, false);
InsertText(text);
- cursor_pos_ = save;
+ render_text_->SetCursorPosition(save);
ClearSelection();
}
@@ -495,8 +336,9 @@ bool TextfieldViewsModel::Delete() {
DeleteSelection();
return true;
}
- if (text_.length() > cursor_pos_) {
- ExecuteAndRecordDelete(cursor_pos_, cursor_pos_ + 1, true);
+ if (GetText().length() > GetCursorPosition()) {
+ size_t cursor_position = GetCursorPosition();
+ ExecuteAndRecordDelete(cursor_position, cursor_position + 1, true);
return true;
}
return false;
@@ -512,194 +354,79 @@ bool TextfieldViewsModel::Backspace() {
DeleteSelection();
return true;
}
- if (cursor_pos_ > 0) {
- ExecuteAndRecordDelete(cursor_pos_, cursor_pos_ - 1, true);
+ if (GetCursorPosition() > 0) {
+ size_t cursor_position = GetCursorPosition();
+ ExecuteAndRecordDelete(cursor_position, cursor_position - 1, true);
return true;
}
return false;
}
-void TextfieldViewsModel::MoveCursorLeft(bool select) {
- if (HasCompositionText())
- ConfirmCompositionText();
- // TODO(oshima): support BIDI
- if (select) {
- if (cursor_pos_ > 0)
- cursor_pos_--;
- } else {
- if (HasSelection())
- cursor_pos_ = std::min(cursor_pos_, selection_start_);
- else if (cursor_pos_ > 0)
- cursor_pos_--;
- ClearSelection();
- }
+size_t TextfieldViewsModel::GetCursorPosition() const {
+ return render_text_->GetCursorPosition();
}
-void TextfieldViewsModel::MoveCursorRight(bool select) {
+void TextfieldViewsModel::MoveCursorLeft(gfx::BreakType break_type,
+ bool select) {
if (HasCompositionText())
ConfirmCompositionText();
- // TODO(oshima): support BIDI
- if (select) {
- cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1);
- } else {
- if (HasSelection())
- cursor_pos_ = std::max(cursor_pos_, selection_start_);
- else
- cursor_pos_ = std::min(text_.length(), cursor_pos_ + 1);
- ClearSelection();
- }
+ render_text_->MoveCursorLeft(break_type, select);
}
-void TextfieldViewsModel::MoveCursorToPreviousWord(bool select) {
+void TextfieldViewsModel::MoveCursorRight(gfx::BreakType break_type,
+ bool select) {
if (HasCompositionText())
ConfirmCompositionText();
- // Notes: We always iterate words from the begining.
- // This is probably fast enough for our usage, but we may
- // want to modify WordIterator so that it can start from the
- // middle of string and advance backwards.
- base::i18n::BreakIterator iter(text_, base::i18n::BreakIterator::BREAK_WORD);
- bool success = iter.Init();
- DCHECK(success);
- if (!success)
- return;
- int last = 0;
- while (iter.Advance()) {
- if (iter.IsWord()) {
- size_t begin = iter.pos() - iter.GetString().length();
- if (begin == cursor_pos_) {
- // The cursor is at the beginning of a word.
- // Move to previous word.
- break;
- } else if(iter.pos() >= cursor_pos_) {
- // The cursor is in the middle or at the end of a word.
- // Move to the top of current word.
- last = begin;
- break;
- } else {
- last = iter.pos() - iter.GetString().length();
- }
- }
- }
-
- cursor_pos_ = last;
- if (!select)
- ClearSelection();
+ render_text_->MoveCursorRight(break_type, select);
}
-void TextfieldViewsModel::MoveCursorToNextWord(bool select) {
+bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) {
if (HasCompositionText())
ConfirmCompositionText();
- base::i18n::BreakIterator iter(text_, base::i18n::BreakIterator::BREAK_WORD);
- bool success = iter.Init();
- DCHECK(success);
- if (!success)
- return;
- size_t pos = 0;
- while (iter.Advance()) {
- pos = iter.pos();
- if (iter.IsWord() && pos > cursor_pos_) {
- break;
- }
- }
- cursor_pos_ = pos;
- if (!select)
- ClearSelection();
-}
-
-void TextfieldViewsModel::MoveCursorToHome(bool select) {
- MoveCursorTo(0, select);
-}
-
-void TextfieldViewsModel::MoveCursorToEnd(bool select) {
- MoveCursorTo(text_.length(), select);
+ return render_text_->MoveCursorTo(pos, select);
}
-bool TextfieldViewsModel::MoveCursorTo(size_t pos, bool select) {
+bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) {
if (HasCompositionText())
ConfirmCompositionText();
- bool changed = cursor_pos_ != pos || select != HasSelection();
- cursor_pos_ = pos;
- if (!select)
- ClearSelection();
- return changed;
+ return render_text_->MoveCursorTo(point, select);
}
-gfx::Rect TextfieldViewsModel::GetSelectionBounds(const gfx::Font& font) const {
- if (!HasSelection())
- return gfx::Rect();
- size_t start = std::min(selection_start_, cursor_pos_);
- size_t end = std::max(selection_start_, cursor_pos_);
- int start_x = font.GetStringWidth(text_.substr(0, start));
- int end_x = font.GetStringWidth(text_.substr(0, end));
- return gfx::Rect(start_x, 0, end_x - start_x, font.GetHeight());
+std::vector<gfx::Rect> TextfieldViewsModel::GetSelectionBounds() const {
+ return render_text_->GetSubstringBounds(render_text_->GetSelection());
}
string16 TextfieldViewsModel::GetSelectedText() const {
- return text_.substr(
- std::min(cursor_pos_, selection_start_),
- std::abs(static_cast<long>(cursor_pos_ - selection_start_)));
+ ui::Range selection = render_text_->GetSelection();
+ return GetText().substr(selection.GetMin(), selection.length());
}
void TextfieldViewsModel::GetSelectedRange(ui::Range* range) const {
- *range = ui::Range(selection_start_, cursor_pos_);
+ *range = render_text_->GetSelection();
}
void TextfieldViewsModel::SelectRange(const ui::Range& range) {
if (HasCompositionText())
ConfirmCompositionText();
- selection_start_ = GetSafePosition(range.start());
- cursor_pos_ = GetSafePosition(range.end());
+ render_text_->SetSelection(range);
}
void TextfieldViewsModel::SelectAll() {
if (HasCompositionText())
ConfirmCompositionText();
- // SelectAll selects towards the end.
- cursor_pos_ = text_.length();
- selection_start_ = 0;
+ render_text_->SelectAll();
}
void TextfieldViewsModel::SelectWord() {
if (HasCompositionText())
ConfirmCompositionText();
- // First we setup selection_start_ and cursor_pos_. There are so many cases
- // because we try to emulate what select-word looks like in a gtk textfield.
- // See associated testcase for different cases.
- if (cursor_pos_ > 0 && cursor_pos_ < text_.length()) {
- if (isalnum(text_[cursor_pos_])) {
- selection_start_ = cursor_pos_;
- cursor_pos_++;
- } else
- selection_start_ = cursor_pos_ - 1;
- } else if (cursor_pos_ == 0) {
- selection_start_ = cursor_pos_;
- if (text_.length() > 0)
- cursor_pos_++;
- } else {
- selection_start_ = cursor_pos_ - 1;
- }
-
- // Now we move selection_start_ to beginning of selection. Selection boundary
- // is defined as the position where we have alpha-num character on one side
- // and non-alpha-num char on the other side.
- for (; selection_start_ > 0; selection_start_--) {
- if (IsPositionAtWordSelectionBoundary(selection_start_))
- break;
- }
-
- // Now we move cursor_pos_ to end of selection. Selection boundary
- // is defined as the position where we have alpha-num character on one side
- // and non-alpha-num char on the other side.
- for (; cursor_pos_ < text_.length(); cursor_pos_++) {
- if (IsPositionAtWordSelectionBoundary(cursor_pos_))
- break;
- }
+ render_text_->SelectWord();
}
void TextfieldViewsModel::ClearSelection() {
if (HasCompositionText())
ConfirmCompositionText();
- selection_start_ = cursor_pos_;
+ render_text_->ClearSelection();
}
bool TextfieldViewsModel::CanUndo() {
@@ -723,8 +450,8 @@ bool TextfieldViewsModel::Undo() {
if (HasCompositionText()) // safe guard for release build.
CancelCompositionText();
- string16 old = text_;
- size_t old_cursor = cursor_pos_;
+ string16 old = GetText();
+ size_t old_cursor = GetCursorPosition();
(*current_edit_)->Commit();
(*current_edit_)->Undo(this);
@@ -732,7 +459,7 @@ bool TextfieldViewsModel::Undo() {
current_edit_ = edit_history_.end();
else
current_edit_--;
- return old != text_ || old_cursor != cursor_pos_;
+ return old != GetText() || old_cursor != GetCursorPosition();
}
bool TextfieldViewsModel::Redo() {
@@ -746,10 +473,14 @@ bool TextfieldViewsModel::Redo() {
current_edit_ = edit_history_.begin();
else
current_edit_ ++;
- string16 old = text_;
- size_t old_cursor = cursor_pos_;
+ string16 old = GetText();
+ size_t old_cursor = GetCursorPosition();
(*current_edit_)->Redo(this);
- return old != text_ || old_cursor != cursor_pos_;
+ return old != GetText() || old_cursor != GetCursorPosition();
+}
+
+string16 TextfieldViewsModel::GetVisibleText() const {
+ return GetVisibleText(0U, GetText().length());
}
bool TextfieldViewsModel::Cut() {
@@ -761,7 +492,8 @@ bool TextfieldViewsModel::Cut() {
// than beginning, unlike Delete/Backspace.
// TODO(oshima): Change Delete/Backspace to use DeleteSelection,
// update DeleteEdit and remove this trick.
- std::swap(cursor_pos_, selection_start_);
+ ui::Range selection = render_text_->GetSelection();
+ render_text_->SetSelection(ui::Range(selection.end(), selection.start()));
DeleteSelection();
return true;
}
@@ -787,13 +519,14 @@ bool TextfieldViewsModel::Paste() {
}
bool TextfieldViewsModel::HasSelection() const {
- return selection_start_ != cursor_pos_;
+ return !render_text_->GetSelection().is_empty();
}
void TextfieldViewsModel::DeleteSelection() {
DCHECK(!HasCompositionText());
DCHECK(HasSelection());
- ExecuteAndRecordDelete(selection_start_, cursor_pos_, false);
+ ui::Range selection = render_text_->GetSelection();
+ ExecuteAndRecordDelete(selection.start(), selection.end(), false);
}
void TextfieldViewsModel::DeleteSelectionAndInsertTextAt(
@@ -801,26 +534,24 @@ void TextfieldViewsModel::DeleteSelectionAndInsertTextAt(
if (HasCompositionText())
CancelCompositionText();
ExecuteAndRecordReplace(DO_NOT_MERGE,
- cursor_pos_,
+ GetCursorPosition(),
position + text.length(),
text,
position);
}
string16 TextfieldViewsModel::GetTextFromRange(const ui::Range& range) const {
- if (range.IsValid() && range.GetMin() < text_.length())
- return text_.substr(range.GetMin(), range.length());
+ if (range.IsValid() && range.GetMin() < GetText().length())
+ return GetText().substr(range.GetMin(), range.length());
return string16();
}
void TextfieldViewsModel::GetTextRange(ui::Range* range) const {
- *range = ui::Range(0, text_.length());
+ *range = ui::Range(0, GetText().length());
}
void TextfieldViewsModel::SetCompositionText(
const ui::CompositionText& composition) {
- static const TextStyle* composition_style = CreateUnderlineStyle();
-
if (HasCompositionText())
CancelCompositionText();
else if (HasSelection())
@@ -829,95 +560,55 @@ void TextfieldViewsModel::SetCompositionText(
if (composition.text.empty())
return;
- size_t length = composition.text.length();
- text_.insert(cursor_pos_, composition.text);
- composition_start_ = cursor_pos_;
- composition_end_ = composition_start_ + length;
- for (ui::CompositionUnderlines::const_iterator iter =
- composition.underlines.begin();
- iter != composition.underlines.end();
- iter++) {
- size_t start = composition_start_ + iter->start_offset;
- size_t end = composition_start_ + iter->end_offset;
- InsertStyle(&composition_style_ranges_,
- new TextStyleRange(composition_style, start, end));
- }
- std::sort(composition_style_ranges_.begin(),
- composition_style_ranges_.end(), TextStyleRangeComparator);
-
- if (composition.selection.IsValid()) {
- selection_start_ =
- std::min(composition_start_ + composition.selection.start(),
- composition_end_);
- cursor_pos_ =
- std::min(composition_start_ + composition.selection.end(),
- composition_end_);
- } else {
- cursor_pos_ = composition_end_;
- ClearSelection();
- }
+ size_t cursor = GetCursorPosition();
+ string16 new_text = GetText();
+ render_text_->SetText(new_text.insert(cursor, composition.text));
+ ui::Range range(cursor, cursor + composition.text.length());
+ render_text_->SetCompositionRange(range);
+ // TODO(msw): Support multiple composition underline ranges.
+
+ if (composition.selection.IsValid())
+ render_text_->SetSelection(ui::Range(
+ std::min(range.start() + composition.selection.start(), range.end()),
+ std::min(range.start() + composition.selection.end(), range.end())));
+ else
+ render_text_->SetCursorPosition(range.end());
}
void TextfieldViewsModel::ConfirmCompositionText() {
DCHECK(HasCompositionText());
- string16 new_text =
- text_.substr(composition_start_, composition_end_ - composition_start_);
+ ui::Range range = render_text_->GetCompositionRange();
+ string16 text = GetText().substr(range.start(), range.length());
// TODO(oshima): current behavior on ChromeOS is a bit weird and not
// sure exactly how this should work. Find out and fix if necessary.
- AddOrMergeEditHistory(new InsertEdit(false, new_text, composition_start_));
- cursor_pos_ = composition_end_;
+ AddOrMergeEditHistory(new InsertEdit(false, text, range.start()));
+ render_text_->SetCursorPosition(range.end());
ClearComposition();
- ClearSelection();
if (delegate_)
delegate_->OnCompositionTextConfirmedOrCleared();
}
void TextfieldViewsModel::CancelCompositionText() {
DCHECK(HasCompositionText());
- text_.erase(composition_start_, composition_end_ - composition_start_);
- cursor_pos_ = composition_start_;
+ ui::Range range = render_text_->GetCompositionRange();
+ string16 new_text = GetText();
+ render_text_->SetText(new_text.erase(range.start(), range.length()));
+ render_text_->SetCursorPosition(range.start());
ClearComposition();
- ClearSelection();
if (delegate_)
delegate_->OnCompositionTextConfirmedOrCleared();
}
void TextfieldViewsModel::ClearComposition() {
- composition_start_ = composition_end_ = string16::npos;
- STLDeleteContainerPointers(composition_style_ranges_.begin(),
- composition_style_ranges_.end());
- composition_style_ranges_.clear();
-}
-
-void TextfieldViewsModel::ApplyTextStyle(const TextStyle* style,
- const ui::Range& range) {
- TextStyleRange* new_text_style_range = range.is_reversed() ?
- new TextStyleRange(style, ui::Range(range.end(), range.start())) :
- new TextStyleRange(style, range);
- InsertStyle(&style_ranges_, new_text_style_range);
- sort_style_ranges_ = true;
+ render_text_->SetCompositionRange(ui::Range::InvalidRange());
}
void TextfieldViewsModel::GetCompositionTextRange(ui::Range* range) const {
- if (HasCompositionText())
- *range = ui::Range(composition_start_, composition_end_);
- else
- *range = ui::Range::InvalidRange();
+ *range = ui::Range(render_text_->GetCompositionRange());
}
bool TextfieldViewsModel::HasCompositionText() const {
- return composition_start_ != composition_end_;
-}
-
-TextStyle* TextfieldViewsModel::CreateTextStyle() {
- TextStyle* style = new TextStyle();
- text_styles_.push_back(style);
- return style;
-}
-
-void TextfieldViewsModel::ClearAllTextStyles() {
- STLDeleteContainerPointers(style_ranges_.begin(), style_ranges_.end());
- style_ranges_.clear();
+ return !render_text_->GetCompositionRange().is_empty();
}
/////////////////////////////////////////////////////////////////
@@ -927,19 +618,7 @@ string16 TextfieldViewsModel::GetVisibleText(size_t begin, size_t end) const {
DCHECK(end >= begin);
if (is_password_)
return string16(end - begin, '*');
- return text_.substr(begin, end - begin);
-}
-
-bool TextfieldViewsModel::IsPositionAtWordSelectionBoundary(size_t pos) {
- return (isalnum(text_[pos - 1]) && !isalnum(text_[pos])) ||
- (!isalnum(text_[pos - 1]) && isalnum(text_[pos]));
-}
-
-size_t TextfieldViewsModel::GetSafePosition(size_t position) const {
- if (position > text_.length()) {
- return text_.length();
- }
- return position;
+ return GetText().substr(begin, end - begin);
}
void TextfieldViewsModel::InsertTextInternal(const string16& text,
@@ -957,10 +636,12 @@ void TextfieldViewsModel::InsertTextInternal(const string16& text,
void TextfieldViewsModel::ReplaceTextInternal(const string16& text,
bool mergeable) {
- if (HasCompositionText())
+ if (HasCompositionText()) {
CancelCompositionText();
- else if (!HasSelection())
- SelectRange(ui::Range(cursor_pos_ + text.length(), cursor_pos_));
+ } else if (!HasSelection()) {
+ size_t cursor = GetCursorPosition();
+ render_text_->SetSelection(ui::Range(cursor + text.length(), cursor));
+ }
// Edit history is recorded in InsertText.
InsertTextInternal(text, mergeable);
}
@@ -989,7 +670,7 @@ void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from,
size_t to,
bool mergeable) {
size_t old_text_start = std::min(from, to);
- const string16 text = text_.substr(old_text_start,
+ const string16 text = GetText().substr(old_text_start,
std::abs(static_cast<long>(from - to)));
bool backward = from > to;
Edit* edit = new DeleteEdit(mergeable, text, old_text_start, backward);
@@ -1001,10 +682,10 @@ void TextfieldViewsModel::ExecuteAndRecordDelete(size_t from,
void TextfieldViewsModel::ExecuteAndRecordReplaceSelection(
MergeType merge_type, const string16& new_text) {
- size_t new_text_start = std::min(cursor_pos_, selection_start_);
+ size_t new_text_start = render_text_->GetSelection().GetMin();
size_t new_cursor_pos = new_text_start + new_text.length();
ExecuteAndRecordReplace(merge_type,
- cursor_pos_,
+ GetCursorPosition(),
new_cursor_pos,
new_text,
new_text_start);
@@ -1015,8 +696,8 @@ void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type,
size_t new_cursor_pos,
const string16& new_text,
size_t new_text_start) {
- size_t old_text_start = std::min(cursor_pos_, selection_start_);
- bool backward = selection_start_ > cursor_pos_;
+ size_t old_text_start = render_text_->GetSelection().GetMin();
+ bool backward = render_text_->GetSelection().is_reversed();
Edit* edit = new ReplaceEdit(merge_type,
GetSelectedText(),
old_cursor_pos,
@@ -1033,7 +714,7 @@ void TextfieldViewsModel::ExecuteAndRecordReplace(MergeType merge_type,
void TextfieldViewsModel::ExecuteAndRecordInsert(const string16& text,
bool mergeable) {
- Edit* edit = new InsertEdit(mergeable, text, cursor_pos_);
+ Edit* edit = new InsertEdit(mergeable, text, GetCursorPosition());
bool delete_edit = AddOrMergeEditHistory(edit);
edit->Redo(this);
if (delete_edit)
@@ -1067,21 +748,14 @@ void TextfieldViewsModel::ModifyText(size_t delete_from,
size_t new_text_insert_at,
size_t new_cursor_pos) {
DCHECK_LE(delete_from, delete_to);
+ string16 text = GetText();
if (delete_from != delete_to)
- text_.erase(delete_from, delete_to - delete_from);
+ render_text_->SetText(text.erase(delete_from, delete_to - delete_from));
if (!new_text.empty())
- text_.insert(new_text_insert_at, new_text);
- cursor_pos_ = new_cursor_pos;
- ClearSelection();
+ render_text_->SetText(text.insert(new_text_insert_at, new_text));
+ render_text_->SetCursorPosition(new_cursor_pos);
// TODO(oshima): mac selects the text that is just undone (but gtk doesn't).
// This looks fine feature and we may want to do the same.
}
-// static
-TextStyle* TextfieldViewsModel::CreateUnderlineStyle() {
- TextStyle* style = new TextStyle();
- style->set_underline(true);
- return style;
-}
-
} // namespace views
diff --git a/views/controls/textfield/textfield_views_model.h b/views/controls/textfield/textfield_views_model.h
index 297c8ae..3f63fe4 100644
--- a/views/controls/textfield/textfield_views_model.h
+++ b/views/controls/textfield/textfield_views_model.h
@@ -10,13 +10,18 @@
#include <vector>
#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ime/composition_text.h"
#include "ui/gfx/rect.h"
+#include "ui/gfx/render_text.h"
namespace gfx {
+class Canvas;
class Font;
+class RenderText;
+struct StyleRange;
} // namespace gfx
namespace ui {
@@ -25,15 +30,10 @@ class Range;
namespace views {
-class TextStyle;
-typedef std::vector<TextStyle*> TextStyles;
-
namespace internal {
// Internal Edit class that keeps track of edits for undo/redo.
class Edit;
-struct TextStyleRange;
-
// C++ doesn't allow forward decl enum, so let's define here.
enum MergeType {
// The edit should not be merged with next edit. It still may
@@ -49,8 +49,6 @@ enum MergeType {
} // namespace internal
-typedef std::vector<internal::TextStyleRange*> TextStyleRanges;
-
// A model that represents a text content for TextfieldViews.
// It supports editing, selection and cursor manipulation.
class TextfieldViewsModel {
@@ -70,31 +68,14 @@ class TextfieldViewsModel {
explicit TextfieldViewsModel(Delegate* delegate);
virtual ~TextfieldViewsModel();
- // Text fragment info. Used to draw selected text.
- // We may replace this with TextAttribute class
- // in the future to support multi-color text
- // for omnibox.
- struct TextFragment {
- TextFragment(size_t start, size_t end, const views::TextStyle* s)
- : range(start, end), style(s) {
- }
- // The start and end position of text fragment.
- ui::Range range;
- const TextStyle* style;
- };
- typedef std::vector<TextFragment> TextFragments;
-
- // Gets the text element info.
- void GetFragments(TextFragments* elements);
-
void set_is_password(bool is_password) {
is_password_ = is_password;
}
- const string16& text() const { return text_; }
// Edit related methods.
- // Sest the text. Returns true if the text has been modified. The
+ const string16& GetText() const;
+ // Sets the text. Returns true if the text has been modified. The
// current composition text will be confirmed first. Setting
// the same text will not add edit history because it's not user
// visible change nor user-initiated change. This allow a client
@@ -102,6 +83,8 @@ class TextfieldViewsModel {
// messing edit history.
bool SetText(const string16& text);
+ gfx::RenderText* render_text() { return render_text_.get(); }
+
// Inserts given |text| at the current cursor position.
// The current composition text will be cleared.
void InsertText(const string16& text) {
@@ -143,46 +126,23 @@ class TextfieldViewsModel {
// Cursor related methods.
// Returns the current cursor position.
- size_t cursor_pos() const { return cursor_pos_; }
-
- // Moves the cursor left by one position (as if, the user has pressed the left
- // arrow key). If |select| is true, it updates the selection accordingly.
- // The current composition text will be confirmed.
- void MoveCursorLeft(bool select);
+ size_t GetCursorPosition() const;
- // Moves the cursor right by one position (as if, the user has pressed the
- // right arrow key). If |select| is true, it updates the selection
- // accordingly.
- // The current composition text will be confirmed.
- void MoveCursorRight(bool select);
-
- // Moves the cursor left by one word (word boundry is defined by space).
- // If |select| is true, it updates the selection accordingly.
- // The current composition text will be confirmed.
- void MoveCursorToPreviousWord(bool select);
-
- // Moves the cursor right by one word (word boundry is defined by space).
- // If |select| is true, it updates the selection accordingly.
- // The current composition text will be confirmed.
- void MoveCursorToNextWord(bool select);
-
- // Moves the cursor to start of the textfield contents.
- // If |select| is true, it updates the selection accordingly.
+ // Moves the cursor, see RenderText for additional details.
// The current composition text will be confirmed.
- void MoveCursorToHome(bool select);
-
- // Moves the cursor to end of the textfield contents.
- // If |select| is true, it updates the selection accordingly.
- // The current composition text will be confirmed.
- void MoveCursorToEnd(bool select);
+ void MoveCursorLeft(gfx::BreakType break_type, bool select);
+ void MoveCursorRight(gfx::BreakType break_type, bool select);
// Moves the cursor to the specified |position|.
// If |select| is true, it updates the selection accordingly.
// The current composition text will be confirmed.
bool MoveCursorTo(size_t position, bool select);
+ // Helper function to call MoveCursorTo on the TextfieldViewsModel.
+ bool MoveCursorTo(const gfx::Point& point, bool select);
+
// Returns the bounds of selected text.
- gfx::Rect GetSelectionBounds(const gfx::Font& font) const;
+ std::vector<gfx::Rect> GetSelectionBounds() const;
// Selection related method
@@ -223,9 +183,7 @@ class TextfieldViewsModel {
// Returns visible text. If the field is password, it returns the
// sequence of "*".
- string16 GetVisibleText() const {
- return GetVisibleText(0U, text_.length());
- }
+ string16 GetVisibleText() const;
// Cuts the currently selected text and puts it to clipboard. Returns true
// if text has changed after cutting.
@@ -277,15 +235,10 @@ class TextfieldViewsModel {
// Returns true if there is composition text.
bool HasCompositionText() const;
- TextStyle* CreateTextStyle();
-
- void ClearAllTextStyles();
-
private:
friend class NativeTextfieldViews;
friend class NativeTextfieldViewsTest;
friend class TextfieldViewsModelTest;
- friend class TextStyle;
friend class UndoRedo_BasicTest;
friend class UndoRedo_CutCopyPasteTest;
friend class UndoRedo_ReplaceTest;
@@ -294,18 +247,10 @@ class TextfieldViewsModel {
FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_BasicTest);
FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest);
FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, UndoRedo_ReplaceTest);
- FRIEND_TEST_ALL_PREFIXES(TextfieldViewsModelTest, TextStyleTest);
// Returns the visible text given |start| and |end|.
string16 GetVisibleText(size_t start, size_t end) const;
- // Utility for SelectWord(). Checks whether position pos is at word boundary.
- bool IsPositionAtWordSelectionBoundary(size_t pos);
-
- // Returns the normalized cursor position that does not exceed the
- // text length.
- size_t GetSafePosition(size_t position) const;
-
// Insert the given |text|. |mergeable| indicates if this insert
// operation can be merged to previous edit in the edit history.
void InsertTextInternal(const string16& text, bool mergeable);
@@ -349,26 +294,12 @@ class TextfieldViewsModel {
void ClearComposition();
- void ApplyTextStyle(const TextStyle* style, const ui::Range& range);
-
- static TextStyle* CreateUnderlineStyle();
-
// Pointer to a TextfieldViewsModel::Delegate instance, should be provided by
// the View object.
Delegate* delegate_;
- // The text in utf16 format.
- string16 text_;
-
- // Current cursor position.
- size_t cursor_pos_;
-
- // Selection range.
- size_t selection_start_;
-
- // Composition text range.
- size_t composition_start_;
- size_t composition_end_;
+ // The stylized text, cursor, selection, and the visual layout model.
+ scoped_ptr<gfx::RenderText> render_text_;
// True if the text is the password.
bool is_password_;
@@ -389,19 +320,6 @@ class TextfieldViewsModel {
// 3) redone all undone edits.
EditHistory::iterator current_edit_;
- // This manages all styles objects.
- TextStyles text_styles_;
-
- // List of style ranges. Elements in the list never overlap each other.
- // Elements are not sorted at the time of insertion, and gets sorted
- // when it's painted (if necessary).
- TextStyleRanges style_ranges_;
- // True if the style_ranges_ needs to be sorted.
- bool sort_style_ranges_;
-
- // List of style ranges for composition text.
- TextStyleRanges composition_style_ranges_;
-
DISALLOW_COPY_AND_ASSIGN(TextfieldViewsModel);
};
diff --git a/views/controls/textfield/textfield_views_model_unittest.cc b/views/controls/textfield/textfield_views_model_unittest.cc
index 7e2f7e6..907e353 100644
--- a/views/controls/textfield/textfield_views_model_unittest.cc
+++ b/views/controls/textfield/textfield_views_model_unittest.cc
@@ -10,7 +10,7 @@
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/range/range.h"
-#include "views/controls/textfield/text_style.h"
+#include "ui/gfx/render_text.h"
#include "views/controls/textfield/textfield.h"
#include "views/controls/textfield/textfield_views_model.h"
#include "views/test/test_views_delegate.h"
@@ -19,8 +19,6 @@
namespace views {
-#include "views/test/views_test_base.h"
-
class TextfieldViewsModelTest : public ViewsTestBase,
public TextfieldViewsModel::Delegate {
public:
@@ -47,57 +45,56 @@ class TextfieldViewsModelTest : public ViewsTestBase,
#define EXPECT_STR_EQ(ascii, utf16) \
EXPECT_EQ(ASCIIToWide(ascii), UTF16ToWide(utf16))
-
TEST_F(TextfieldViewsModelTest, EditString) {
TextfieldViewsModel model(NULL);
// append two strings
model.Append(ASCIIToUTF16("HILL"));
- EXPECT_STR_EQ("HILL", model.text());
+ EXPECT_STR_EQ("HILL", model.GetText());
model.Append(ASCIIToUTF16("WORLD"));
- EXPECT_STR_EQ("HILLWORLD", model.text());
+ EXPECT_STR_EQ("HILLWORLD", model.GetText());
// Insert "E" to make hello
- model.MoveCursorRight(false);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
model.InsertChar('E');
- EXPECT_STR_EQ("HEILLWORLD", model.text());
+ EXPECT_STR_EQ("HEILLWORLD", model.GetText());
// Replace "I" with "L"
model.ReplaceChar('L');
- EXPECT_STR_EQ("HELLLWORLD", model.text());
+ EXPECT_STR_EQ("HELLLWORLD", model.GetText());
model.ReplaceChar('L');
model.ReplaceChar('O');
- EXPECT_STR_EQ("HELLOWORLD", model.text());
+ EXPECT_STR_EQ("HELLOWORLD", model.GetText());
// Delete 6th char "W", then delete 5th char O"
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_TRUE(model.Delete());
- EXPECT_STR_EQ("HELLOORLD", model.text());
+ EXPECT_STR_EQ("HELLOORLD", model.GetText());
EXPECT_TRUE(model.Backspace());
- EXPECT_EQ(4U, model.cursor_pos());
- EXPECT_STR_EQ("HELLORLD", model.text());
+ EXPECT_EQ(4U, model.GetCursorPosition());
+ EXPECT_STR_EQ("HELLORLD", model.GetText());
// Move the cursor to start. backspace should fail.
- model.MoveCursorToHome(false);
+ model.MoveCursorLeft(gfx::LINE_BREAK, false);
EXPECT_FALSE(model.Backspace());
- EXPECT_STR_EQ("HELLORLD", model.text());
+ EXPECT_STR_EQ("HELLORLD", model.GetText());
// Move the cursor to the end. delete should fail.
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
EXPECT_FALSE(model.Delete());
- EXPECT_STR_EQ("HELLORLD", model.text());
+ EXPECT_STR_EQ("HELLORLD", model.GetText());
// but backspace should work.
EXPECT_TRUE(model.Backspace());
- EXPECT_STR_EQ("HELLORL", model.text());
+ EXPECT_STR_EQ("HELLORL", model.GetText());
}
TEST_F(TextfieldViewsModelTest, EmptyString) {
TextfieldViewsModel model(NULL);
- EXPECT_EQ(string16(), model.text());
+ EXPECT_EQ(string16(), model.GetText());
EXPECT_EQ(string16(), model.GetSelectedText());
EXPECT_EQ(string16(), model.GetVisibleText());
- model.MoveCursorLeft(true);
- EXPECT_EQ(0U, model.cursor_pos());
- model.MoveCursorRight(true);
- EXPECT_EQ(0U, model.cursor_pos());
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ EXPECT_EQ(0U, model.GetCursorPosition());
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_EQ(string16(), model.GetSelectedText());
@@ -108,15 +105,15 @@ TEST_F(TextfieldViewsModelTest, EmptyString) {
TEST_F(TextfieldViewsModelTest, Selection) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO"));
- model.MoveCursorRight(false);
- model.MoveCursorRight(true);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
EXPECT_STR_EQ("E", model.GetSelectedText());
- model.MoveCursorRight(true);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
EXPECT_STR_EQ("EL", model.GetSelectedText());
- model.MoveCursorToHome(true);
+ model.MoveCursorLeft(gfx::LINE_BREAK, true);
EXPECT_STR_EQ("H", model.GetSelectedText());
- model.MoveCursorToEnd(true);
+ model.MoveCursorRight(gfx::LINE_BREAK, true);
EXPECT_STR_EQ("ELLO", model.GetSelectedText());
model.ClearSelection();
EXPECT_EQ(string16(), model.GetSelectedText());
@@ -132,49 +129,49 @@ TEST_F(TextfieldViewsModelTest, Selection) {
model.MoveCursorTo(1U, false);
model.MoveCursorTo(3U, true);
EXPECT_STR_EQ("EL", model.GetSelectedText());
- model.MoveCursorLeft(false);
- EXPECT_EQ(1U, model.cursor_pos());
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
+ EXPECT_EQ(1U, model.GetCursorPosition());
model.MoveCursorTo(1U, false);
model.MoveCursorTo(3U, true);
- model.MoveCursorRight(false);
- EXPECT_EQ(3U, model.cursor_pos());
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ EXPECT_EQ(3U, model.GetCursorPosition());
// Select all and move cursor
model.SelectAll();
- model.MoveCursorLeft(false);
- EXPECT_EQ(0U, model.cursor_pos());
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
+ EXPECT_EQ(0U, model.GetCursorPosition());
model.SelectAll();
- model.MoveCursorRight(false);
- EXPECT_EQ(5U, model.cursor_pos());
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ EXPECT_EQ(5U, model.GetCursorPosition());
}
TEST_F(TextfieldViewsModelTest, SelectionAndEdit) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO"));
- model.MoveCursorRight(false);
- model.MoveCursorRight(true);
- model.MoveCursorRight(true); // select "EL"
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "EL"
EXPECT_TRUE(model.Backspace());
- EXPECT_STR_EQ("HLO", model.text());
+ EXPECT_STR_EQ("HLO", model.GetText());
model.Append(ASCIIToUTF16("ILL"));
- model.MoveCursorRight(true);
- model.MoveCursorRight(true); // select "LO"
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "LO"
EXPECT_TRUE(model.Delete());
- EXPECT_STR_EQ("HILL", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
- model.MoveCursorRight(true); // select "I"
+ EXPECT_STR_EQ("HILL", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "I"
model.InsertChar('E');
- EXPECT_STR_EQ("HELL", model.text());
- model.MoveCursorToHome(false);
- model.MoveCursorRight(true); // select "H"
+ EXPECT_STR_EQ("HELL", model.GetText());
+ model.MoveCursorLeft(gfx::LINE_BREAK, false);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true); // select "H"
model.ReplaceChar('B');
- EXPECT_STR_EQ("BELL", model.text());
- model.MoveCursorToEnd(false);
- model.MoveCursorLeft(true);
- model.MoveCursorLeft(true); // select ">LL"
+ EXPECT_STR_EQ("BELL", model.GetText());
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, true); // select "ELL"
model.ReplaceChar('E');
- EXPECT_STR_EQ("BEE", model.text());
+ EXPECT_STR_EQ("BEE", model.GetText());
}
TEST_F(TextfieldViewsModelTest, Password) {
@@ -182,110 +179,88 @@ TEST_F(TextfieldViewsModelTest, Password) {
model.set_is_password(true);
model.Append(ASCIIToUTF16("HELLO"));
EXPECT_STR_EQ("*****", model.GetVisibleText());
- EXPECT_STR_EQ("HELLO", model.text());
+ EXPECT_STR_EQ("HELLO", model.GetText());
EXPECT_TRUE(model.Delete());
EXPECT_STR_EQ("****", model.GetVisibleText());
- EXPECT_STR_EQ("ELLO", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("ELLO", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
model.SelectAll();
EXPECT_STR_EQ("ELLO", model.GetSelectedText());
- EXPECT_EQ(4U, model.cursor_pos());
+ EXPECT_EQ(4U, model.GetCursorPosition());
model.InsertChar('X');
EXPECT_STR_EQ("*", model.GetVisibleText());
- EXPECT_STR_EQ("X", model.text());
+ EXPECT_STR_EQ("X", model.GetText());
}
TEST_F(TextfieldViewsModelTest, Word) {
TextfieldViewsModel model(NULL);
model.Append(
ASCIIToUTF16("The answer to Life, the Universe, and Everything"));
- model.MoveCursorToNextWord(false);
- EXPECT_EQ(3U, model.cursor_pos());
- model.MoveCursorToNextWord(false);
- EXPECT_EQ(10U, model.cursor_pos());
- model.MoveCursorToNextWord(false);
- model.MoveCursorToNextWord(false);
- EXPECT_EQ(18U, model.cursor_pos());
+ model.MoveCursorRight(gfx::WORD_BREAK, false);
+ EXPECT_EQ(3U, model.GetCursorPosition());
+ model.MoveCursorRight(gfx::WORD_BREAK, false);
+ EXPECT_EQ(10U, model.GetCursorPosition());
+ model.MoveCursorRight(gfx::WORD_BREAK, false);
+ model.MoveCursorRight(gfx::WORD_BREAK, false);
+ EXPECT_EQ(18U, model.GetCursorPosition());
// Should passes the non word char ','
- model.MoveCursorToNextWord(true);
- EXPECT_EQ(23U, model.cursor_pos());
+ model.MoveCursorRight(gfx::WORD_BREAK, true);
+ EXPECT_EQ(23U, model.GetCursorPosition());
EXPECT_STR_EQ(", the", model.GetSelectedText());
// Move to the end.
- model.MoveCursorToNextWord(true);
- model.MoveCursorToNextWord(true);
- model.MoveCursorToNextWord(true);
+ model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursorRight(gfx::WORD_BREAK, true);
+ model.MoveCursorRight(gfx::WORD_BREAK, true);
EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
// Should be safe to go next word at the end.
- model.MoveCursorToNextWord(true);
+ model.MoveCursorRight(gfx::WORD_BREAK, true);
EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
model.InsertChar('2');
- EXPECT_EQ(19U, model.cursor_pos());
+ EXPECT_EQ(19U, model.GetCursorPosition());
// Now backwards.
- model.MoveCursorLeft(false); // leave 2.
- model.MoveCursorToPreviousWord(true);
- EXPECT_EQ(14U, model.cursor_pos());
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, false); // leave 2.
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ EXPECT_EQ(14U, model.GetCursorPosition());
EXPECT_STR_EQ("Life", model.GetSelectedText());
- model.MoveCursorToPreviousWord(true);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
EXPECT_STR_EQ("to Life", model.GetSelectedText());
- model.MoveCursorToPreviousWord(true);
- model.MoveCursorToPreviousWord(true);
- model.MoveCursorToPreviousWord(true); // Select to the begining.
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true); // Select to the begining.
EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
// Should be safe to go pervious word at the begining.
- model.MoveCursorToPreviousWord(true);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
model.ReplaceChar('4');
EXPECT_EQ(string16(), model.GetSelectedText());
EXPECT_STR_EQ("42", model.GetVisibleText());
}
-TEST_F(TextfieldViewsModelTest, TextFragment) {
- TextfieldViewsModel model(NULL);
- TextfieldViewsModel::TextFragments fragments;
- // Empty string has no fragment.
- model.GetFragments(&fragments);
- EXPECT_EQ(0U, fragments.size());
-
- // Some string
- model.Append(ASCIIToUTF16("Hello world"));
- model.GetFragments(&fragments);
- EXPECT_EQ(1U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(11U, fragments[0].range.end());
-
- // Selection won't change fragment.
- model.MoveCursorToNextWord(true);
- model.GetFragments(&fragments);
- EXPECT_EQ(1U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(11U, fragments[0].range.end());
-}
-
TEST_F(TextfieldViewsModelTest, SetText) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO"));
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
model.SetText(ASCIIToUTF16("GOODBYE"));
- EXPECT_STR_EQ("GOODBYE", model.text());
+ EXPECT_STR_EQ("GOODBYE", model.GetText());
// SetText won't reset the cursor posistion.
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_EQ(5U, model.GetCursorPosition());
model.SelectAll();
EXPECT_STR_EQ("GOODBYE", model.GetSelectedText());
- model.MoveCursorToEnd(false);
- EXPECT_EQ(7U, model.cursor_pos());
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
+ EXPECT_EQ(7U, model.GetCursorPosition());
model.SetText(ASCIIToUTF16("BYE"));
// Setting shorter string moves the cursor to the end of the new string.
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_EQ(string16(), model.GetSelectedText());
model.SetText(ASCIIToUTF16(""));
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_EQ(0U, model.GetCursorPosition());
}
TEST_F(TextfieldViewsModelTest, Clipboard) {
@@ -296,29 +271,29 @@ TEST_F(TextfieldViewsModelTest, Clipboard) {
string16 clipboard_text;
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO WORLD"));
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
// Test for cut: Empty selection.
EXPECT_FALSE(model.Cut());
clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
EXPECT_STR_EQ(UTF16ToUTF8(initial_clipboard_text), clipboard_text);
- EXPECT_STR_EQ("HELLO WORLD", model.text());
- EXPECT_EQ(11U, model.cursor_pos());
+ EXPECT_STR_EQ("HELLO WORLD", model.GetText());
+ EXPECT_EQ(11U, model.GetCursorPosition());
// Test for cut: Non-empty selection.
- model.MoveCursorToPreviousWord(true);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
EXPECT_TRUE(model.Cut());
clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
EXPECT_STR_EQ("WORLD", clipboard_text);
- EXPECT_STR_EQ("HELLO ", model.text());
- EXPECT_EQ(6U, model.cursor_pos());
+ EXPECT_STR_EQ("HELLO ", model.GetText());
+ EXPECT_EQ(6U, model.GetCursorPosition());
// Test for copy: Empty selection.
model.Copy();
clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
EXPECT_STR_EQ("WORLD", clipboard_text);
- EXPECT_STR_EQ("HELLO ", model.text());
- EXPECT_EQ(6U, model.cursor_pos());
+ EXPECT_STR_EQ("HELLO ", model.GetText());
+ EXPECT_EQ(6U, model.GetCursorPosition());
// Test for copy: Non-empty selection.
model.Append(ASCIIToUTF16("HELLO WORLD"));
@@ -326,24 +301,24 @@ TEST_F(TextfieldViewsModelTest, Clipboard) {
model.Copy();
clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
- EXPECT_STR_EQ("HELLO HELLO WORLD", model.text());
- EXPECT_EQ(17U, model.cursor_pos());
+ EXPECT_STR_EQ("HELLO HELLO WORLD", model.GetText());
+ EXPECT_EQ(17U, model.GetCursorPosition());
// Test for paste.
model.ClearSelection();
- model.MoveCursorToEnd(false);
- model.MoveCursorToPreviousWord(true);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
EXPECT_TRUE(model.Paste());
clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
- EXPECT_STR_EQ("HELLO HELLO HELLO HELLO WORLD", model.text());
- EXPECT_EQ(29U, model.cursor_pos());
+ EXPECT_STR_EQ("HELLO HELLO HELLO HELLO WORLD", model.GetText());
+ EXPECT_EQ(29U, model.GetCursorPosition());
}
void SelectWordTestVerifier(TextfieldViewsModel &model,
const std::string &expected_selected_string, size_t expected_cursor_pos) {
EXPECT_STR_EQ(expected_selected_string, model.GetSelectedText());
- EXPECT_EQ(expected_cursor_pos, model.cursor_pos());
+ EXPECT_EQ(expected_cursor_pos, model.GetCursorPosition());
}
TEST_F(TextfieldViewsModelTest, SelectWordTest) {
@@ -351,7 +326,7 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) {
model.Append(ASCIIToUTF16(" HELLO !! WO RLD "));
// Test when cursor is at the beginning.
- model.MoveCursorToHome(false);
+ model.MoveCursorLeft(gfx::LINE_BREAK, false);
model.SelectWord();
SelectWordTestVerifier(model, " ", 2U);
@@ -378,7 +353,7 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) {
SelectWordTestVerifier(model, " ", 20U);
// Test when cursor is at the end.
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
model.SelectWord();
SelectWordTestVerifier(model, " ", 24U);
}
@@ -386,61 +361,61 @@ TEST_F(TextfieldViewsModelTest, SelectWordTest) {
TEST_F(TextfieldViewsModelTest, RangeTest) {
TextfieldViewsModel model(NULL);
model.Append(ASCIIToUTF16("HELLO WORLD"));
- model.MoveCursorToHome(false);
+ model.MoveCursorLeft(gfx::LINE_BREAK, false);
ui::Range range;
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(0U, range.end());
- model.MoveCursorToNextWord(true);
+ model.MoveCursorRight(gfx::WORD_BREAK, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_FALSE(range.is_reversed());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(5U, range.end());
- model.MoveCursorLeft(true);
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(4U, range.end());
- model.MoveCursorToPreviousWord(true);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(0U, range.start());
EXPECT_EQ(0U, range.end());
// now from the end.
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(11U, range.end());
- model.MoveCursorToPreviousWord(true);
+ model.MoveCursorLeft(gfx::WORD_BREAK, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_TRUE(range.is_reversed());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(6U, range.end());
- model.MoveCursorRight(true);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_TRUE(range.is_reversed());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(7U, range.end());
- model.MoveCursorToNextWord(true);
+ model.MoveCursorRight(gfx::WORD_BREAK, true);
model.GetSelectedRange(&range);
EXPECT_TRUE(range.is_empty());
EXPECT_EQ(11U, range.start());
EXPECT_EQ(11U, range.end());
// Select All
- model.MoveCursorToHome(true);
+ model.MoveCursorLeft(gfx::LINE_BREAK, true);
model.GetSelectedRange(&range);
EXPECT_FALSE(range.is_empty());
EXPECT_TRUE(range.is_reversed());
@@ -497,7 +472,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
model.Append(ASCIIToUTF16("1234590"));
model.SelectRange(ui::Range(5, 5));
EXPECT_FALSE(model.HasSelection());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_EQ(5U, model.GetCursorPosition());
ui::Range range;
model.GetTextRange(&range);
@@ -514,7 +489,7 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
model.GetTextRange(&range);
EXPECT_EQ(10U, range.end());
- EXPECT_STR_EQ("1234567890", model.text());
+ EXPECT_STR_EQ("1234567890", model.GetText());
model.GetCompositionTextRange(&range);
EXPECT_EQ(5U, range.start());
@@ -527,55 +502,40 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
EXPECT_EQ(8U, range.end());
EXPECT_STR_EQ("8", model.GetSelectedText());
- TextfieldViewsModel::TextFragments fragments;
- model.GetFragments(&fragments);
- EXPECT_EQ(3U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(5U, fragments[0].range.end());
- EXPECT_FALSE(fragments[0].style->underline());
-
- EXPECT_EQ(5U, fragments[1].range.start());
- EXPECT_EQ(8U, fragments[1].range.end());
- EXPECT_TRUE(fragments[1].style->underline());
-
- EXPECT_EQ(8U, fragments[2].range.start());
- EXPECT_EQ(10U, fragments[2].range.end());
- EXPECT_FALSE(fragments[2].style->underline());
-
EXPECT_FALSE(composition_text_confirmed_or_cleared_);
model.CancelCompositionText();
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
EXPECT_FALSE(model.HasCompositionText());
EXPECT_FALSE(model.HasSelection());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_EQ(5U, model.GetCursorPosition());
model.SetCompositionText(composition);
- EXPECT_STR_EQ("1234567890", model.text());
+ EXPECT_STR_EQ("1234567890", model.GetText());
EXPECT_TRUE(model.SetText(ASCIIToUTF16("1234567890")));
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
model.SetCompositionText(composition);
- EXPECT_STR_EQ("1234567890678", model.text());
+ EXPECT_STR_EQ("1234567890678", model.GetText());
model.InsertText(UTF8ToUTF16("-"));
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("1234567890-", model.text());
+ EXPECT_STR_EQ("1234567890-", model.GetText());
EXPECT_FALSE(model.HasCompositionText());
EXPECT_FALSE(model.HasSelection());
- model.MoveCursorLeft(true);
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
EXPECT_STR_EQ("-", model.GetSelectedText());
model.SetCompositionText(composition);
- EXPECT_STR_EQ("1234567890678", model.text());
+ EXPECT_STR_EQ("1234567890678", model.GetText());
model.ReplaceText(UTF8ToUTF16("-"));
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("1234567890-", model.text());
+ EXPECT_STR_EQ("1234567890-", model.GetText());
EXPECT_FALSE(model.HasCompositionText());
EXPECT_FALSE(model.HasSelection());
@@ -583,82 +543,82 @@ TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
model.Append(UTF8ToUTF16("-"));
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("1234567890-678-", model.text());
+ EXPECT_STR_EQ("1234567890-678-", model.GetText());
model.SetCompositionText(composition);
model.Delete();
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("1234567890-678-", model.text());
+ EXPECT_STR_EQ("1234567890-678-", model.GetText());
model.SetCompositionText(composition);
model.Backspace();
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("1234567890-678-", model.text());
+ EXPECT_STR_EQ("1234567890-678-", model.GetText());
model.SetText(string16());
model.SetCompositionText(composition);
- model.MoveCursorLeft(false);
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("678", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("678", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
model.SetCompositionText(composition);
- model.MoveCursorRight(false);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("676788", model.text());
- EXPECT_EQ(6U, model.cursor_pos());
+ EXPECT_STR_EQ("676788", model.GetText());
+ EXPECT_EQ(6U, model.GetCursorPosition());
model.SetCompositionText(composition);
- model.MoveCursorToPreviousWord(false);
+ model.MoveCursorLeft(gfx::WORD_BREAK, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("676788678", model.text());
+ EXPECT_STR_EQ("676788678", model.GetText());
model.SetText(string16());
model.SetCompositionText(composition);
- model.MoveCursorToNextWord(false);
+ model.MoveCursorRight(gfx::WORD_BREAK, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
model.SetCompositionText(composition);
- model.MoveCursorToHome(true);
+ model.MoveCursorLeft(gfx::LINE_BREAK, true);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("678678", model.text());
+ EXPECT_STR_EQ("678678", model.GetText());
model.SetCompositionText(composition);
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("678", model.text());
+ EXPECT_STR_EQ("678", model.GetText());
model.SetCompositionText(composition);
model.MoveCursorTo(0, true);
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("678678", model.text());
+ EXPECT_STR_EQ("678678", model.GetText());
model.SetCompositionText(composition);
model.SelectRange(ui::Range(0, 3));
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("678", model.text());
+ EXPECT_STR_EQ("678", model.GetText());
model.SetCompositionText(composition);
model.SelectAll();
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("678", model.text());
+ EXPECT_STR_EQ("678", model.GetText());
model.SetCompositionText(composition);
model.SelectWord();
EXPECT_TRUE(composition_text_confirmed_or_cleared_);
composition_text_confirmed_or_cleared_ = false;
- EXPECT_STR_EQ("678", model.text());
+ EXPECT_STR_EQ("678", model.GetText());
model.SetCompositionText(composition);
model.ClearSelection();
@@ -675,157 +635,157 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_BasicTest) {
model.InsertChar('a');
EXPECT_FALSE(model.Redo()); // nothing to redo
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("a", model.text());
+ EXPECT_STR_EQ("a", model.GetText());
// Continuous inserts are treated as one edit.
model.InsertChar('b');
model.InsertChar('c');
- EXPECT_STR_EQ("abc", model.text());
+ EXPECT_STR_EQ("abc", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("a", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("a", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
// Undoing further shouldn't change the text.
EXPECT_FALSE(model.Undo());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_FALSE(model.Undo());
- EXPECT_STR_EQ("", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
// Redoing to the latest text.
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("a", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("a", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("abc", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("abc", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
// Backspace ===============================
EXPECT_TRUE(model.Backspace());
- EXPECT_STR_EQ("ab", model.text());
+ EXPECT_STR_EQ("ab", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("abc", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("abc", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ab", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("ab", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
// Continous backspaces are treated as one edit.
EXPECT_TRUE(model.Backspace());
EXPECT_TRUE(model.Backspace());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
// Extra backspace shouldn't affect the history.
EXPECT_FALSE(model.Backspace());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ab", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("ab", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("abc", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("abc", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("a", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("a", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
// Clear history
model.ClearEditHistory();
EXPECT_FALSE(model.Undo());
EXPECT_FALSE(model.Redo());
- EXPECT_STR_EQ("a", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("a", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
// Delete ===============================
model.SetText(ASCIIToUTF16("ABCDE"));
model.ClearEditHistory();
model.MoveCursorTo(2, false);
EXPECT_TRUE(model.Delete());
- EXPECT_STR_EQ("ABDE", model.text());
- model.MoveCursorToHome(false);
+ EXPECT_STR_EQ("ABDE", model.GetText());
+ model.MoveCursorLeft(gfx::LINE_BREAK, false);
EXPECT_TRUE(model.Delete());
- EXPECT_STR_EQ("BDE", model.text());
+ EXPECT_STR_EQ("BDE", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABDE", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("ABDE", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABDE", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("ABDE", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
// Continous deletes are treated as one edit.
EXPECT_TRUE(model.Delete());
EXPECT_TRUE(model.Delete());
- EXPECT_STR_EQ("AB", model.text());
+ EXPECT_STR_EQ("AB", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABDE", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("ABDE", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("AB", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("AB", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
}
TEST_F(TextfieldViewsModelTest, UndoRedo_SetText) {
// This is to test the undo/redo behavior of omnibox.
TextfieldViewsModel model(NULL);
model.InsertChar('w');
- EXPECT_STR_EQ("w", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("w", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
model.SetText(ASCIIToUTF16("www.google.com"));
- EXPECT_EQ(1U, model.cursor_pos());
- EXPECT_STR_EQ("www.google.com", model.text());
+ EXPECT_EQ(1U, model.GetCursorPosition());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
model.SelectRange(ui::Range(14, 1));
model.InsertChar('w');
- EXPECT_STR_EQ("ww", model.text());
+ EXPECT_STR_EQ("ww", model.GetText());
model.SetText(ASCIIToUTF16("www.google.com"));
model.SelectRange(ui::Range(14, 2));
model.InsertChar('w');
- EXPECT_STR_EQ("www", model.text());
+ EXPECT_STR_EQ("www", model.GetText());
model.SetText(ASCIIToUTF16("www.google.com"));
model.SelectRange(ui::Range(14, 3));
model.InsertChar('.');
- EXPECT_STR_EQ("www.", model.text());
+ EXPECT_STR_EQ("www.", model.GetText());
model.SetText(ASCIIToUTF16("www.google.com"));
model.SelectRange(ui::Range(14, 4));
model.InsertChar('y');
- EXPECT_STR_EQ("www.y", model.text());
+ EXPECT_STR_EQ("www.y", model.GetText());
model.SetText(ASCIIToUTF16("www.youtube.com"));
- EXPECT_STR_EQ("www.youtube.com", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("www.youtube.com", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(4U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(4U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_FALSE(model.Undo());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("www.google.com", model.text());
- EXPECT_EQ(4U, model.cursor_pos());
+ EXPECT_STR_EQ("www.google.com", model.GetText());
+ EXPECT_EQ(4U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("www.youtube.com", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("www.youtube.com", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_FALSE(model.Redo());
}
@@ -837,153 +797,153 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CutCopyPasteTest) {
model.MoveCursorTo(1, false);
model.MoveCursorTo(3, true);
model.Cut();
- EXPECT_STR_EQ("ADE", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("ADE", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_FALSE(model.Undo()); // no more undo
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ADE", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("ADE", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_FALSE(model.Redo()); // no more redo
- EXPECT_STR_EQ("ADE", model.text());
+ EXPECT_STR_EQ("ADE", model.GetText());
model.Paste();
model.Paste();
model.Paste();
- EXPECT_STR_EQ("ABCBCBCDE", model.text());
- EXPECT_EQ(7U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCBCDE", model.GetText());
+ EXPECT_EQ(7U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCBCDE", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCDE", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ADE", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("ADE", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_FALSE(model.Undo());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCDE", model.text()); // Redoing SetText
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCDE", model.GetText()); // Redoing SetText
+ EXPECT_EQ(0U, model.GetCursorPosition());
// Redo
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ADE", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("ADE", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCDE", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCBCDE", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCDE", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCBCBCDE", model.text());
- EXPECT_EQ(7U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCBCDE", model.GetText());
+ EXPECT_EQ(7U, model.GetCursorPosition());
EXPECT_FALSE(model.Redo());
// with SelectRange
model.SelectRange(ui::Range(1, 3));
EXPECT_TRUE(model.Cut());
- EXPECT_STR_EQ("ABCBCDE", model.text());
- EXPECT_EQ(1U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCDE", model.GetText());
+ EXPECT_EQ(1U, model.GetCursorPosition());
model.SelectRange(ui::Range(1, 1));
EXPECT_FALSE(model.Cut());
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
EXPECT_TRUE(model.Paste());
- EXPECT_STR_EQ("ABCBCDEBC", model.text());
- EXPECT_EQ(9U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCDEBC", model.GetText());
+ EXPECT_EQ(9U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCBCDE", model.text());
- EXPECT_EQ(7U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCDE", model.GetText());
+ EXPECT_EQ(7U, model.GetCursorPosition());
// empty cut shouldn't create an edit.
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCBCBCDE", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("ABCBCBCDE", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
// Copy
ResetModel(&model);
model.SetText(ASCIIToUTF16("12345"));
- EXPECT_STR_EQ("12345", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("12345", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
model.MoveCursorTo(1, false);
model.MoveCursorTo(3, true);
model.Copy(); // Copy "23"
- EXPECT_STR_EQ("12345", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("12345", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
model.Paste(); // Paste "23" into "23".
- EXPECT_STR_EQ("12345", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("12345", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
model.Paste();
- EXPECT_STR_EQ("1232345", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("1232345", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("12345", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("12345", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
// TODO(oshima): We need to change the return type from bool to enum.
EXPECT_FALSE(model.Undo()); // No text change.
- EXPECT_STR_EQ("12345", model.text());
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("12345", model.GetText());
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_FALSE(model.Undo());
// Redo
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("12345", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("12345", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("12345", model.text()); // For 1st paste
- EXPECT_EQ(3U, model.cursor_pos());
+ EXPECT_STR_EQ("12345", model.GetText()); // For 1st paste
+ EXPECT_EQ(3U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("1232345", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("1232345", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_FALSE(model.Redo());
- EXPECT_STR_EQ("1232345", model.text());
+ EXPECT_STR_EQ("1232345", model.GetText());
- // with SelectRange
+ // Test using SelectRange
model.SelectRange(ui::Range(1, 3));
model.Copy();
- EXPECT_STR_EQ("1232345", model.text());
- model.MoveCursorToEnd(false);
+ EXPECT_STR_EQ("1232345", model.GetText());
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
EXPECT_TRUE(model.Paste());
- EXPECT_STR_EQ("123234523", model.text());
- EXPECT_EQ(9U, model.cursor_pos());
+ EXPECT_STR_EQ("123234523", model.GetText());
+ EXPECT_EQ(9U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("1232345", model.text());
- EXPECT_EQ(7U, model.cursor_pos());
+ EXPECT_STR_EQ("1232345", model.GetText());
+ EXPECT_EQ(7U, model.GetCursorPosition());
}
TEST_F(TextfieldViewsModelTest, UndoRedo_CursorTest) {
TextfieldViewsModel model(NULL);
model.InsertChar('a');
- model.MoveCursorLeft(false);
- model.MoveCursorRight(false);
+ model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
+ model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
model.InsertChar('b');
- // Moving cursor shoudln't create a new edit.
- EXPECT_STR_EQ("ab", model.text());
+ // Moving the cursor shouldn't create a new edit.
+ EXPECT_STR_EQ("ab", model.GetText());
EXPECT_FALSE(model.Redo());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_FALSE(model.Undo());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ab", model.text());
- EXPECT_EQ(2U, model.cursor_pos());
+ EXPECT_STR_EQ("ab", model.GetText());
+ EXPECT_EQ(2U, model.GetCursorPosition());
EXPECT_FALSE(model.Redo());
}
@@ -995,21 +955,21 @@ void RunInsertReplaceTest(TextfieldViewsModel& model) {
model.InsertChar('1');
model.InsertChar('2');
model.InsertChar('3');
- EXPECT_STR_EQ("a123d", model.text());
- EXPECT_EQ(4U, model.cursor_pos());
+ EXPECT_STR_EQ("a123d", model.GetText());
+ EXPECT_EQ(4U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("abcd", model.text());
- EXPECT_EQ(reverse ? 1U : 3U, model.cursor_pos());
+ EXPECT_STR_EQ("abcd", model.GetText());
+ EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_FALSE(model.Undo());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("abcd", model.text());
- EXPECT_EQ(0U, model.cursor_pos()); // By SetText
+ EXPECT_STR_EQ("abcd", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition()); // By SetText
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("a123d", model.text());
- EXPECT_EQ(4U, model.cursor_pos());
+ EXPECT_STR_EQ("a123d", model.GetText());
+ EXPECT_EQ(4U, model.GetCursorPosition());
EXPECT_FALSE(model.Redo());
}
@@ -1022,21 +982,21 @@ void RunOverwriteReplaceTest(TextfieldViewsModel& model) {
model.ReplaceChar('2');
model.ReplaceChar('3');
model.ReplaceChar('4');
- EXPECT_STR_EQ("a1234", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("a1234", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("abcd", model.text());
- EXPECT_EQ(reverse ? 1U : 3U, model.cursor_pos());
+ EXPECT_STR_EQ("abcd", model.GetText());
+ EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_FALSE(model.Undo());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("abcd", model.text());
- EXPECT_EQ(0U, model.cursor_pos());
+ EXPECT_STR_EQ("abcd", model.GetText());
+ EXPECT_EQ(0U, model.GetCursorPosition());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("a1234", model.text());
- EXPECT_EQ(5U, model.cursor_pos());
+ EXPECT_STR_EQ("a1234", model.GetText());
+ EXPECT_EQ(5U, model.GetCursorPosition());
EXPECT_FALSE(model.Redo());
}
@@ -1114,282 +1074,72 @@ TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) {
composition.selection = ui::Range(2, 3);
model.SetText(ASCIIToUTF16("ABCDE"));
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
model.InsertChar('x');
- EXPECT_STR_EQ("ABCDEx", model.text());
+ EXPECT_STR_EQ("ABCDEx", model.GetText());
EXPECT_TRUE(model.Undo()); // set composition should forget undone edit.
model.SetCompositionText(composition);
EXPECT_TRUE(model.HasCompositionText());
EXPECT_TRUE(model.HasSelection());
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
// Accepting composition
model.ConfirmCompositionText();
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("", model.text());
+ EXPECT_STR_EQ("", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCDE", model.text());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
EXPECT_FALSE(model.Redo());
// Canceling composition
- model.MoveCursorToHome(false);
+ model.MoveCursorLeft(gfx::LINE_BREAK, false);
model.SetCompositionText(composition);
- EXPECT_STR_EQ("abcABCDEabc", model.text());
+ EXPECT_STR_EQ("abcABCDEabc", model.GetText());
model.CancelCompositionText();
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
EXPECT_FALSE(model.Redo());
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
EXPECT_FALSE(model.Redo());
// SetText with the same text as the result.
ResetModel(&model);
model.SetText(ASCIIToUTF16("ABCDE"));
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
model.SetCompositionText(composition);
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
model.SetText(ASCIIToUTF16("ABCDEabc"));
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
EXPECT_FALSE(model.Redo());
// SetText with the different text than the result should not
// remember composition text.
ResetModel(&model);
model.SetText(ASCIIToUTF16("ABCDE"));
- model.MoveCursorToEnd(false);
+ model.MoveCursorRight(gfx::LINE_BREAK, false);
model.SetCompositionText(composition);
- EXPECT_STR_EQ("ABCDEabc", model.text());
+ EXPECT_STR_EQ("ABCDEabc", model.GetText());
model.SetText(ASCIIToUTF16("1234"));
- EXPECT_STR_EQ("1234", model.text());
+ EXPECT_STR_EQ("1234", model.GetText());
EXPECT_TRUE(model.Undo());
- EXPECT_STR_EQ("ABCDE", model.text());
+ EXPECT_STR_EQ("ABCDE", model.GetText());
EXPECT_TRUE(model.Redo());
- EXPECT_STR_EQ("1234", model.text());
+ EXPECT_STR_EQ("1234", model.GetText());
EXPECT_FALSE(model.Redo());
// TODO(oshima): We need MockInputMethod to test the behavior with IME.
}
-TEST_F(TextfieldViewsModelTest, TextStyleTest) {
- const SkColor black = 0xFF000000; // black is default text color.
- const SkColor white = 0xFFFFFFFF;
- TextfieldViewsModel model(NULL);
- TextStyle* color = model.CreateTextStyle();
- color->set_foreground(white);
- TextStyle* underline = model.CreateTextStyle();
- underline->set_underline(true);
- underline->set_foreground(white);
- TextStyle* strike = model.CreateTextStyle();
- strike->set_strike(true);
- strike->set_foreground(white);
-
- // Case 1: No overlaps
- model.ApplyTextStyle(color, ui::Range(1, 3));
- model.ApplyTextStyle(underline, ui::Range(5, 6));
-
- TextfieldViewsModel::TextFragments fragments;
- model.GetFragments(&fragments);
- // Styles with empty string simply returns an empty fragments.
- EXPECT_EQ(0U, fragments.size());
-
- // 1st style only.
- model.SetText(ASCIIToUTF16("01234")); // SetText doesn't change styles.
- model.GetFragments(&fragments);
- EXPECT_EQ(3U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(1U, fragments[0].range.end());
- EXPECT_EQ(black, fragments[0].style->foreground());
-
- EXPECT_EQ(1U, fragments[1].range.start());
- EXPECT_EQ(3U, fragments[1].range.end());
- EXPECT_EQ(color, fragments[1].style);
-
- EXPECT_EQ(3U, fragments[2].range.start());
- EXPECT_EQ(5U, fragments[2].range.end());
- EXPECT_EQ(black, fragments[2].style->foreground());
-
- // Clear styles
- model.ClearAllTextStyles();
- model.GetFragments(&fragments);
- EXPECT_EQ(1U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(5U, fragments[0].range.end());
- EXPECT_EQ(black, fragments[0].style->foreground());
-
- // Case 2: Overlaps on left and on right
- model.ApplyTextStyle(color, ui::Range(1, 3));
- model.ApplyTextStyle(strike, ui::Range(6, 8));
- model.ApplyTextStyle(underline, ui::Range(2, 7));
-
- // With short string
- model.SetText(ASCIIToUTF16("0"));
- model.GetFragments(&fragments);
- EXPECT_EQ(1U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(1U, fragments[0].range.end());
- EXPECT_EQ(black, fragments[0].style->foreground());
-
- // With mid-length string
- model.SetText(ASCIIToUTF16("0123"));
- model.GetFragments(&fragments);
- EXPECT_EQ(3U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(1U, fragments[0].range.end());
- EXPECT_EQ(black, fragments[0].style->foreground());
-
- EXPECT_EQ(1U, fragments[1].range.start());
- EXPECT_EQ(2U, fragments[1].range.end());
- EXPECT_EQ(color, fragments[1].style);
-
- EXPECT_EQ(2U, fragments[2].range.start());
- EXPECT_EQ(4U, fragments[2].range.end());
- EXPECT_EQ(underline, fragments[2].style);
-
- // With long (longer than styles) string
- model.SetText(ASCIIToUTF16("0123456789"));
- model.GetFragments(&fragments);
- EXPECT_EQ(5U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(1U, fragments[0].range.end());
- EXPECT_EQ(black, fragments[0].style->foreground());
-
- EXPECT_EQ(1U, fragments[1].range.start());
- EXPECT_EQ(2U, fragments[1].range.end());
- EXPECT_EQ(color, fragments[1].style);
-
- EXPECT_EQ(2U, fragments[2].range.start());
- EXPECT_EQ(7U, fragments[2].range.end());
- EXPECT_EQ(underline, fragments[2].style);
-
- EXPECT_EQ(7U, fragments[3].range.start());
- EXPECT_EQ(8U, fragments[3].range.end());
- EXPECT_EQ(strike, fragments[3].style);
-
- EXPECT_EQ(8U, fragments[4].range.start());
- EXPECT_EQ(10U, fragments[4].range.end());
- EXPECT_EQ(black, fragments[4].style->foreground());
-
- model.ClearAllTextStyles();
-
- // Case 3: The underline style splits the color style underneath.
- model.ApplyTextStyle(color, ui::Range(0, 15));
- model.ApplyTextStyle(underline, ui::Range(5, 6));
- model.GetFragments(&fragments);
- EXPECT_EQ(3U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(5U, fragments[0].range.end());
- EXPECT_EQ(color, fragments[0].style);
-
- EXPECT_EQ(5U, fragments[1].range.start());
- EXPECT_EQ(6U, fragments[1].range.end());
- EXPECT_EQ(underline, fragments[1].style);
-
- EXPECT_EQ(6U, fragments[2].range.start());
- EXPECT_EQ(10U, fragments[2].range.end());
- EXPECT_EQ(color, fragments[2].style);
-
- model.ClearAllTextStyles();
-
- // Case 4: The underline style moves the color style underneath.
- model.ApplyTextStyle(color, ui::Range(0, 15));
- model.ApplyTextStyle(underline, ui::Range(0, 6));
- model.GetFragments(&fragments);
- EXPECT_EQ(2U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(6U, fragments[0].range.end());
- EXPECT_EQ(underline, fragments[0].style);
-
- EXPECT_EQ(6U, fragments[1].range.start());
- EXPECT_EQ(10U, fragments[1].range.end());
- EXPECT_EQ(color, fragments[1].style);
-
- model.ClearAllTextStyles();
-
- model.ApplyTextStyle(color, ui::Range(0, 10));
- model.ApplyTextStyle(underline, ui::Range(6, 10));
- model.GetFragments(&fragments);
- EXPECT_EQ(2U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(6U, fragments[0].range.end());
- EXPECT_EQ(color, fragments[0].style);
-
- EXPECT_EQ(6U, fragments[1].range.start());
- EXPECT_EQ(10U, fragments[1].range.end());
- EXPECT_EQ(underline, fragments[1].style);
-
- model.ClearAllTextStyles();
- // Case 5: The strike style hides the unerline style underneath.
- model.ApplyTextStyle(color, ui::Range(0, 15));
- model.ApplyTextStyle(underline, ui::Range(0, 6));
- model.ApplyTextStyle(strike, ui::Range(4, 7));
- model.GetFragments(&fragments);
- EXPECT_EQ(3U, fragments.size());
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(4U, fragments[0].range.end());
- EXPECT_EQ(underline, fragments[0].style);
-
- EXPECT_EQ(4U, fragments[1].range.start());
- EXPECT_EQ(7U, fragments[1].range.end());
- EXPECT_EQ(strike, fragments[1].style);
-
- EXPECT_EQ(7U, fragments[2].range.start());
- EXPECT_EQ(10U, fragments[2].range.end());
- EXPECT_EQ(color, fragments[2].style);
-
- // Case 6: Reversed range.
- model.ClearAllTextStyles();
- model.ApplyTextStyle(color, ui::Range(3, 1));
- model.ApplyTextStyle(underline, ui::Range(6, 4));
- model.ApplyTextStyle(strike, ui::Range(5, 2));
- model.GetFragments(&fragments);
- EXPECT_EQ(5U, fragments.size());
-
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(1U, fragments[0].range.end());
- EXPECT_EQ(black, fragments[0].style->foreground());
-
- EXPECT_EQ(1U, fragments[1].range.start());
- EXPECT_EQ(2U, fragments[1].range.end());
- EXPECT_EQ(color, fragments[1].style);
-
- EXPECT_EQ(2U, fragments[2].range.start());
- EXPECT_EQ(5U, fragments[2].range.end());
- EXPECT_EQ(strike, fragments[2].style);
-
- EXPECT_EQ(5U, fragments[3].range.start());
- EXPECT_EQ(6U, fragments[3].range.end());
- EXPECT_EQ(underline, fragments[3].style);
-
- EXPECT_EQ(6U, fragments[4].range.start());
- EXPECT_EQ(10U, fragments[4].range.end());
- EXPECT_EQ(black, fragments[4].style->foreground());
-
- // Case 7: empty / invald range
- model.ClearAllTextStyles();
- model.ApplyTextStyle(color, ui::Range(0, 0));
- model.ApplyTextStyle(underline, ui::Range(4, 4));
- ui::Range invalid = ui::Range(0, 2).Intersect(ui::Range(3, 4));
- ASSERT_FALSE(invalid.IsValid());
-
- model.ApplyTextStyle(strike, invalid);
- model.GetFragments(&fragments);
- EXPECT_EQ(1U, fragments.size());
-
- EXPECT_EQ(0U, fragments[0].range.start());
- EXPECT_EQ(10U, fragments[0].range.end());
- EXPECT_EQ(black, fragments[0].style->foreground());
-}
-
} // namespace views
diff --git a/views/examples/textfield_example.cc b/views/examples/textfield_example.cc
index f3553fe..bb1ab24 100644
--- a/views/examples/textfield_example.cc
+++ b/views/examples/textfield_example.cc
@@ -6,8 +6,8 @@
#include "base/utf_string_conversions.h"
#include "ui/base/range/range.h"
+#include "ui/gfx/render_text.h"
#include "views/controls/label.h"
-#include "views/controls/textfield/text_style.h"
#include "views/controls/textfield/textfield.h"
#include "views/layout/grid_layout.h"
#include "views/view.h"
@@ -15,10 +15,7 @@
namespace examples {
TextfieldExample::TextfieldExample(ExamplesMain* main)
- : ExampleBase(main),
- underline_(NULL),
- strike_(NULL),
- color_(NULL) {
+ : ExampleBase(main) {
}
TextfieldExample::~TextfieldExample() {
@@ -93,19 +90,22 @@ void TextfieldExample::ButtonPressed(views::Button* sender,
} else if (sender == set_) {
name_->SetText(WideToUTF16(L"[set]"));
} else if (sender == set_style_) {
- if (!underline_) {
- color_ = name_->CreateTextStyle();
- color_->set_foreground(SK_ColorYELLOW);
- underline_ = name_->CreateTextStyle();
- underline_->set_underline(true);
- underline_->set_foreground(SK_ColorBLUE);
- strike_ = name_->CreateTextStyle();
- strike_->set_strike(true);
- strike_->set_foreground(SK_ColorRED);
- name_->ApplyTextStyle(color_, ui::Range(0, 11));
- name_->ApplyTextStyle(underline_, ui::Range(1, 7));
- name_->ApplyTextStyle(strike_, ui::Range(6, 9));
- }
+ gfx::StyleRange color;
+ color.foreground = SK_ColorYELLOW;
+ color.range = ui::Range(0, 11);
+ name_->ApplyStyleRange(color);
+
+ gfx::StyleRange underline;
+ underline.underline = true;
+ underline.foreground = SK_ColorBLUE;
+ underline.range = ui::Range(1, 7);
+ name_->ApplyStyleRange(underline);
+
+ gfx::StyleRange strike;
+ strike.strike = true;
+ strike.foreground = SK_ColorRED;
+ strike.range = ui::Range(6, 9);
+ name_->ApplyStyleRange(strike);
}
}
diff --git a/views/examples/textfield_example.h b/views/examples/textfield_example.h
index faa9f46..608a3e7 100644
--- a/views/examples/textfield_example.h
+++ b/views/examples/textfield_example.h
@@ -15,10 +15,6 @@
#include "views/controls/textfield/textfield_controller.h"
#include "views/examples/example_base.h"
-namespace views {
-class TextStyle;
-}
-
namespace examples {
// TextfieldExample mimics login screen.
@@ -55,11 +51,6 @@ class TextfieldExample : public ExampleBase,
views::TextButton* set_;
views::TextButton* set_style_;
- // Text Styles
- views::TextStyle* underline_;
- views::TextStyle* strike_;
- views::TextStyle* color_;
-
DISALLOW_COPY_AND_ASSIGN(TextfieldExample);
};
diff --git a/views/views.gyp b/views/views.gyp
index 39593fc..1771c85 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -206,8 +206,6 @@
'controls/textfield/gtk_views_entry.h',
'controls/textfield/gtk_views_textview.cc',
'controls/textfield/gtk_views_textview.h',
- 'controls/textfield/text_style.cc',
- 'controls/textfield/text_style.h',
'controls/textfield/textfield.cc',
'controls/textfield/textfield.h',
'controls/textfield/textfield_controller.h',