summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ui/gfx/render_text.cc142
-rw-r--r--ui/gfx/render_text.h52
-rw-r--r--ui/gfx/render_text_win.cc628
-rw-r--r--ui/gfx/render_text_win.h104
-rw-r--r--views/controls/textfield/native_textfield_views.cc11
-rw-r--r--views/controls/textfield/textfield_views_model.cc13
-rw-r--r--views/controls/textfield/textfield_views_model.h3
-rw-r--r--views/examples/textfield_example.cc33
8 files changed, 892 insertions, 94 deletions
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index 498bb3b..26518d1 100644
--- a/ui/gfx/render_text.cc
+++ b/ui/gfx/render_text.cc
@@ -164,14 +164,8 @@ void RenderText::SetText(const string16& text) {
cached_bounds_and_offset_valid_ = false;
}
-void RenderText::SetSelectionModel(const SelectionModel& sel) {
- size_t start = sel.selection_start();
- size_t end = sel.selection_end();
- selection_model_.set_selection_start(std::min(start, text().length()));
- selection_model_.set_selection_end(std::min(end, text().length()));
- selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length()));
- selection_model_.set_caret_placement(sel.caret_placement());
-
+void RenderText::ToggleInsertMode() {
+ insert_mode_ = !insert_mode_;
cached_bounds_and_offset_valid_ = false;
}
@@ -184,13 +178,8 @@ size_t RenderText::GetCursorPosition() const {
return selection_model_.selection_end();
}
-void RenderText::SetCursorPosition(const size_t position) {
- SelectionModel sel(selection_model());
- sel.set_selection_start(position);
- sel.set_selection_end(position);
- sel.set_caret_pos(GetIndexOfPreviousGrapheme(position));
- sel.set_caret_placement(SelectionModel::TRAILING);
- SetSelectionModel(sel);
+void RenderText::SetCursorPosition(size_t position) {
+ MoveCursorTo(position, false);
}
void RenderText::MoveCursorLeft(BreakType break_type, bool select) {
@@ -237,9 +226,26 @@ void RenderText::MoveCursorRight(BreakType break_type, bool select) {
MoveCursorTo(position);
}
-bool RenderText::MoveCursorTo(const SelectionModel& selection) {
- bool changed = !selection.Equals(selection_model_);
- SetSelectionModel(selection);
+bool RenderText::MoveCursorTo(const SelectionModel& selection_model) {
+ SelectionModel sel(selection_model);
+ size_t text_length = text().length();
+ // Enforce valid selection model components.
+ if (sel.selection_start() > text_length)
+ sel.set_selection_start(text_length);
+ if (sel.selection_end() > text_length)
+ sel.set_selection_end(text_length);
+ // The current model only supports caret positions at valid character indices.
+ if (text_length == 0) {
+ sel.set_caret_pos(0);
+ sel.set_caret_placement(SelectionModel::LEADING);
+ } else if (sel.caret_pos() >= text_length) {
+ SelectionModel end = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ?
+ LeftEndSelectionModel() : RightEndSelectionModel();
+ sel.set_caret_pos(end.caret_pos());
+ sel.set_caret_placement(end.caret_placement());
+ }
+ bool changed = !sel.Equals(selection_model_);
+ SetSelectionModel(sel);
return changed;
}
@@ -251,6 +257,8 @@ bool RenderText::MoveCursorTo(const Point& point, bool select) {
}
bool RenderText::IsPointInSelection(const Point& point) {
+ if (EmptySelection())
+ return false;
// TODO(xji): should this check whether the point is inside the visual
// selection bounds? In case of "abcFED", if "ED" is selected, |point| points
// to the right half of 'c', is the point in selection?
@@ -265,8 +273,8 @@ void RenderText::ClearSelection() {
}
void RenderText::SelectAll() {
- SelectionModel sel(0, text().length(),
- text().length(), SelectionModel::LEADING);
+ SelectionModel sel(RightEndSelectionModel());
+ sel.set_selection_start(LeftEndSelectionModel().selection_start());
SetSelectionModel(sel);
}
@@ -308,12 +316,8 @@ void RenderText::SelectWord() {
break;
}
- SelectionModel sel(selection_model());
- sel.set_selection_start(selection_start);
- sel.set_selection_end(cursor_position);
- sel.set_caret_pos(GetIndexOfPreviousGrapheme(cursor_position));
- sel.set_caret_placement(SelectionModel::TRAILING);
- SetSelectionModel(sel);
+ MoveCursorTo(selection_start, false);
+ MoveCursorTo(cursor_position, true);
}
const ui::Range& RenderText::GetCompositionRange() const {
@@ -350,8 +354,8 @@ void RenderText::ApplyDefaultStyle() {
}
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).
+ if (base::i18n::IsRTL())
+ return base::i18n::RIGHT_TO_LEFT;
return base::i18n::LEFT_TO_RIGHT;
}
@@ -443,20 +447,6 @@ SelectionModel RenderText::FindCursorPosition(const Point& point) {
return SelectionModel(left_pos);
}
-std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) {
- size_t start = std::min(from, to);
- size_t end = std::max(from, to);
- const Font& font = default_style_.font;
- int start_x = font.GetStringWidth(text().substr(0, start));
- int end_x = font.GetStringWidth(text().substr(0, end));
- Rect rect(start_x, 0, end_x - start_x, font.GetHeight());
- rect.Offset(display_rect_.origin());
- rect.Offset(GetUpdatedDisplayOffset());
- // Center the rect vertically in |display_rect_|.
- rect.Offset(Point(0, (display_rect_.height() - rect.height()) / 2));
- return std::vector<Rect>(1, rect);
-}
-
Rect RenderText::GetCursorBounds(const SelectionModel& selection,
bool insert_mode) {
size_t from = selection.selection_end();
@@ -549,9 +539,35 @@ SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current,
return SelectionModel(pos, pos, SelectionModel::LEADING);
}
+SelectionModel RenderText::LeftEndSelectionModel() {
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+}
+
+SelectionModel RenderText::RightEndSelectionModel() {
+ size_t cursor = text().length();
+ size_t caret_pos = GetIndexOfPreviousGrapheme(cursor);
+ SelectionModel::CaretPlacement placement = (caret_pos == cursor) ?
+ SelectionModel::LEADING : SelectionModel::TRAILING;
+ return SelectionModel(cursor, caret_pos, placement);
+}
+
size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) {
// TODO(msw): Handle complex script.
- return std::max(static_cast<int>(position - 1), static_cast<int>(0));
+ return std::max(static_cast<long>(position - 1), static_cast<long>(0));
+}
+
+std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) {
+ size_t start = std::min(from, to);
+ size_t end = std::max(from, to);
+ const Font& font = default_style_.font;
+ int start_x = font.GetStringWidth(text().substr(0, start));
+ int end_x = font.GetStringWidth(text().substr(0, end));
+ Rect rect(start_x, 0, end_x - start_x, font.GetHeight());
+ rect.Offset(display_rect_.origin());
+ rect.Offset(GetUpdatedDisplayOffset());
+ // Center the rect vertically in |display_rect_|.
+ rect.Offset(Point(0, (display_rect_.height() - rect.height()) / 2));
+ return std::vector<Rect>(1, rect);
}
void RenderText::ApplyCompositionAndSelectionStyles(
@@ -576,6 +592,45 @@ void RenderText::ApplyCompositionAndSelectionStyles(
}
}
+Point RenderText::ToTextPoint(const Point& point) {
+ Point p(point.Subtract(display_rect().origin()));
+ p = p.Subtract(GetUpdatedDisplayOffset());
+ if (base::i18n::IsRTL())
+ p.Offset(GetStringWidth() - display_rect().width() + 1, 0);
+ return p;
+}
+
+Point RenderText::ToViewPoint(const Point& point) {
+ Point p(point.Add(display_rect().origin()));
+ p = p.Add(GetUpdatedDisplayOffset());
+ if (base::i18n::IsRTL())
+ p.Offset(display_rect().width() - GetStringWidth() - 1, 0);
+ return p;
+}
+
+void RenderText::SetSelectionModel(const SelectionModel& selection_model) {
+ DCHECK_LE(selection_model.selection_start(), text().length());
+ selection_model_.set_selection_start(selection_model.selection_start());
+ DCHECK_LE(selection_model.selection_end(), text().length());
+ selection_model_.set_selection_end(selection_model.selection_end());
+ DCHECK_LT(selection_model.caret_pos(),
+ std::max(text().length(), static_cast<size_t>(1)));
+ selection_model_.set_caret_pos(selection_model.caret_pos());
+ selection_model_.set_caret_placement(selection_model.caret_placement());
+
+ cached_bounds_and_offset_valid_ = false;
+}
+
+void RenderText::MoveCursorTo(size_t position, bool select) {
+ size_t cursor = std::min(position, text().length());
+ size_t caret_pos = GetIndexOfPreviousGrapheme(cursor);
+ SelectionModel::CaretPlacement placement = (caret_pos == cursor) ?
+ SelectionModel::LEADING : SelectionModel::TRAILING;
+ size_t selection_start = select ? GetSelectionStart() : cursor;
+ SelectionModel sel(selection_start, cursor, caret_pos, placement);
+ SetSelectionModel(sel);
+}
+
bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) {
return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) ||
(!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos]));
@@ -589,7 +644,6 @@ void RenderText::UpdateCachedBoundsAndOffset() {
// function will set |cursor_bounds_| and |display_offset_| to correct values.
cached_bounds_and_offset_valid_ = true;
cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
- cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1));
// Update |display_offset_| to ensure the current cursor is visible.
int display_width = display_rect_.width();
int string_width = GetStringWidth();
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index cc4f4e5..e13d8e4 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -154,13 +154,12 @@ class UI_EXPORT RenderText {
virtual void SetText(const string16& text);
const SelectionModel& selection_model() const { return selection_model_; }
- void SetSelectionModel(const SelectionModel& sel);
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_; }
+ void ToggleInsertMode();
bool focused() const { return focused_; }
void set_focused(bool focused) { focused_ = focused; }
@@ -176,7 +175,7 @@ class UI_EXPORT RenderText {
// edits take place, and doesn't necessarily correspond to
// SelectionModel::caret_pos.
size_t GetCursorPosition() const;
- void SetCursorPosition(const size_t position);
+ void SetCursorPosition(size_t position);
void SetCaretPlacement(SelectionModel::CaretPlacement placement) {
selection_model_.set_caret_placement(placement);
@@ -184,16 +183,18 @@ class UI_EXPORT RenderText {
// 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 |select| is false, the selection start is moved to the same position.
void MoveCursorLeft(BreakType break_type, bool select);
void MoveCursorRight(BreakType break_type, bool select);
// Set the selection_model_ to the value of |selection|.
+ // The selection model components are modified if invalid.
// Returns true if the cursor position or selection range changed.
- bool MoveCursorTo(const SelectionModel& selection);
+ bool MoveCursorTo(const SelectionModel& selection_model);
// Move the cursor to the position associated with the clicked point.
- // If |select| is false, the selection range is emptied at the new position.
+ // If |select| is false, the selection start is moved to the same position.
+ // Returns true if the cursor position or selection range changed.
bool MoveCursorTo(const Point& point, bool select);
size_t GetSelectionStart() const {
@@ -236,13 +237,6 @@ class UI_EXPORT RenderText {
// Gets the SelectionModel from a visual point in local coordinates.
virtual SelectionModel FindCursorPosition(const Point& point);
- // Get the visual bounds containing the logical substring within |from| to
- // |to|. These bounds could be visually discontinuous if the substring is
- // split by a LTR/RTL level change. These bounds are in local coordinates, but
- // may be outside the visible region if the text is longer than the textfield.
- // Subsequent text, cursor, or bounds changes may invalidate returned values.
- virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to);
-
// Get the visual bounds of a cursor at |selection|. These bounds typically
// represent a vertical line, but if |insert_mode| is true they contain the
// bounds of the associated glyph. These bounds are in local coordinates, but
@@ -274,13 +268,31 @@ class UI_EXPORT RenderText {
virtual SelectionModel GetRightSelectionModel(const SelectionModel& current,
BreakType break_type);
+ // Get the SelectionModels corresponding to visual text ends.
+ // The returned value represents a cursor/caret position without a selection.
+ virtual SelectionModel LeftEndSelectionModel();
+ virtual SelectionModel RightEndSelectionModel();
+
// Get the logical index of the grapheme preceeding the argument |position|.
virtual size_t GetIndexOfPreviousGrapheme(size_t position);
+ // Get the visual bounds containing the logical substring within |from| to
+ // |to|. These bounds could be visually discontinuous if the substring is
+ // split by a LTR/RTL level change. These bounds are in local coordinates, but
+ // may be outside the visible region if the text is longer than the textfield.
+ // Subsequent text, cursor, or bounds changes may invalidate returned values.
+ // TODO(msw) Re-evaluate this function's necessity and signature.
+ virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to);
+
// Apply composition style (underline) to composition range and selection
// style (foreground) to selection range.
void ApplyCompositionAndSelectionStyles(StyleRanges* style_ranges) const;
+ // Convert points from the text space to the view space and back.
+ // Handles the display area, display offset, and the application LTR/RTL mode.
+ Point ToTextPoint(const Point& point);
+ Point ToViewPoint(const Point& point);
+
private:
friend class RenderTextTest;
@@ -289,8 +301,13 @@ class UI_EXPORT RenderText {
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust);
- // Clear out |style_ranges_|.
- void ClearStyleRanges();
+ // Sets the selection model, the argument is assumed to be valid.
+ void SetSelectionModel(const SelectionModel& selection_model);
+
+ // Set the cursor to |position|, with the caret trailing the previous
+ // grapheme, or if there is no previous grapheme, leading the cursor position.
+ // If |select| is false, the selection start is moved to the same position.
+ void MoveCursorTo(size_t position, bool select);
bool IsPositionAtWordSelectionBoundary(size_t pos);
@@ -328,9 +345,8 @@ class UI_EXPORT RenderText {
// Get this point with GetUpdatedDisplayOffset (or risk using a stale value).
Point display_offset_;
- // The cached bounds and offset are invalidated by operations such as
- // SetCursorPosition, SetSelectionModel, Font related style change, and other
- // operations that adjust the visible text bounds.
+ // The cached bounds and offset are invalidated by changes to the cursor,
+ // selection, font, and other operations that adjust the visible text bounds.
bool cached_bounds_and_offset_valid_;
DISALLOW_COPY_AND_ASSIGN(RenderText);
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index 9f47946..3195093 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -4,13 +4,639 @@
#include "ui/gfx/render_text_win.h"
+#include "base/i18n/break_iterator.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "skia/ext/skia_utils_win.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/canvas_skia.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace {
+
+// The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
+// TODO(msw): Review memory use/failure? Max string length? Alternate approach?
+const int kGuessItems = 100;
+const int kMaxItems = 10000;
+
+// The maximum supported number of Uniscribe glyphs; a glyph is 1 word.
+// TODO(msw): Review memory use/failure? Max string length? Alternate approach?
+const int kMaxGlyphs = 100000;
+
+// TODO(msw): Solve gfx/Uniscribe/Skia text size unit conversion issues.
+const float kSkiaFontScale = 1.375;
+
+} // namespace
+
namespace gfx {
+namespace internal {
+
+TextRun::TextRun()
+ : strike(false),
+ width(0),
+ preceding_run_widths(0),
+ glyph_count(0) {
+}
+
+} // namespace internal
+
RenderTextWin::RenderTextWin()
- : RenderText() {
+ : RenderText(),
+ script_control_(),
+ script_state_(),
+ script_cache_(NULL),
+ string_width_(0) {
+ // Omitting default constructors for script_* would leave POD uninitialized.
+ HRESULT hr = 0;
+
+ // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message.
+ // TODO(msw): Use Chrome/profile locale/language settings?
+ hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_);
+ DCHECK(SUCCEEDED(hr));
+
+ hr = ScriptApplyDigitSubstitution(&digit_substitute_,
+ &script_control_,
+ &script_state_);
+ DCHECK(SUCCEEDED(hr));
+ script_control_.fMergeNeutralItems = true;
+
+ MoveCursorTo(LeftEndSelectionModel());
}
RenderTextWin::~RenderTextWin() {
+ ScriptFreeCache(&script_cache_);
+ STLDeleteContainerPointers(runs_.begin(), runs_.end());
+}
+
+void RenderTextWin::SetText(const string16& text) {
+ // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
+ RenderText::SetText(text);
+ ItemizeLogicalText();
+ LayoutVisualText(CreateCompatibleDC(NULL));
+}
+
+void RenderTextWin::SetDisplayRect(const Rect& r) {
+ RenderText::SetDisplayRect(r);
+ ItemizeLogicalText();
+ LayoutVisualText(CreateCompatibleDC(NULL));
+}
+
+void RenderTextWin::ApplyStyleRange(StyleRange style_range) {
+ RenderText::ApplyStyleRange(style_range);
+ ItemizeLogicalText();
+ LayoutVisualText(CreateCompatibleDC(NULL));
+}
+
+void RenderTextWin::ApplyDefaultStyle() {
+ RenderText::ApplyDefaultStyle();
+ ItemizeLogicalText();
+ LayoutVisualText(CreateCompatibleDC(NULL));
+}
+
+int RenderTextWin::GetStringWidth() {
+ return string_width_;
+}
+
+void RenderTextWin::Draw(Canvas* canvas) {
+ skia::ScopedPlatformPaint scoped_platform_paint(canvas->AsCanvasSkia());
+ HDC hdc = scoped_platform_paint.GetPlatformSurface();
+ int saved_dc = SaveDC(hdc);
+ DrawSelection(canvas);
+ DrawVisualText(canvas);
+ DrawCursor(canvas);
+ RestoreDC(hdc, saved_dc);
+}
+
+SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
+ if (text().empty())
+ return SelectionModel();
+
+ // Find the run that contains the point and adjust the argument location.
+ Point p(ToTextPoint(point));
+ size_t run_index = GetRunContainingPoint(p);
+ if (run_index == runs_.size())
+ return (p.x() < 0) ? LeftEndSelectionModel() : RightEndSelectionModel();
+ internal::TextRun* run = runs_[run_index];
+
+ int position = 0, trailing = 0;
+ HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &position,
+ &trailing);
+ DCHECK(SUCCEEDED(hr));
+ position += run->range.start();
+
+ size_t cursor = position + trailing;
+ DCHECK_GE(cursor, 0U);
+ DCHECK_LE(cursor, text().length());
+ return SelectionModel(cursor, position,
+ (trailing > 0) ? SelectionModel::TRAILING : SelectionModel::LEADING);
+}
+
+Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection,
+ bool insert_mode) {
+ // Highlight the logical cursor (selection end) when not in insert mode.
+ size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end();
+ size_t run_index = GetRunContainingPosition(pos);
+ internal::TextRun* run = run_index == runs_.size() ? NULL : runs_[run_index];
+
+ int start_x = 0, end_x = 0;
+ if (run) {
+ HRESULT hr = 0;
+ hr = ScriptCPtoX(pos - run->range.start(),
+ false,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &start_x);
+ DCHECK(SUCCEEDED(hr));
+ hr = ScriptCPtoX(pos - run->range.start(),
+ true,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &end_x);
+ DCHECK(SUCCEEDED(hr));
+ }
+ // TODO(msw): Use the last visual run's font instead of the default font?
+ int height = run ? run->font.GetHeight() : default_style().font.GetHeight();
+ Rect rect(std::min(start_x, end_x), 0, std::abs(end_x - start_x), height);
+ // Offset to the run start or the right/left end for an out of bounds index.
+ // Also center the rect vertically in the display area.
+ int text_end_offset = base::i18n::IsRTL() ? 0 : GetStringWidth();
+ rect.Offset((run ? run->preceding_run_widths : text_end_offset),
+ (display_rect().height() - rect.height()) / 2);
+ // Adjust for leading/trailing in insert mode.
+ if (insert_mode && run) {
+ bool leading = selection.caret_placement() == SelectionModel::LEADING;
+ // Adjust the x value for right-side placement.
+ if (run->script_analysis.fRTL == leading)
+ rect.set_x(rect.right());
+ rect.set_width(0);
+ }
+ rect.set_origin(ToViewPoint(rect.origin()));
+ return rect;
+}
+
+SelectionModel RenderTextWin::GetLeftSelectionModel(
+ const SelectionModel& selection,
+ BreakType break_type) {
+ if (break_type == LINE_BREAK || text().empty())
+ return LeftEndSelectionModel();
+ if (break_type == CHARACTER_BREAK)
+ return LeftSelectionModel(selection);
+ // TODO(msw): Implement word breaking.
+ return RenderText::GetLeftSelectionModel(selection, break_type);
+}
+
+SelectionModel RenderTextWin::GetRightSelectionModel(
+ const SelectionModel& selection,
+ BreakType break_type) {
+ if (break_type == LINE_BREAK || text().empty())
+ return RightEndSelectionModel();
+ if (break_type == CHARACTER_BREAK)
+ return RightSelectionModel(selection);
+ // TODO(msw): Implement word breaking.
+ return RenderText::GetRightSelectionModel(selection, break_type);
+}
+
+SelectionModel RenderTextWin::LeftEndSelectionModel() {
+ if (text().empty())
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+ size_t cursor = base::i18n::IsRTL() ? text().length() : 0;
+ internal::TextRun* run = runs_[visual_to_logical_[0]];
+ bool rtl = run->script_analysis.fRTL;
+ size_t caret = rtl ? run->range.end() - 1 : run->range.start();
+ SelectionModel::CaretPlacement placement =
+ rtl ? SelectionModel::TRAILING : SelectionModel::LEADING;
+ return SelectionModel(cursor, caret, placement);
+}
+
+SelectionModel RenderTextWin::RightEndSelectionModel() {
+ if (text().empty())
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+ size_t cursor = base::i18n::IsRTL() ? 0 : text().length();
+ internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]];
+ bool rtl = run->script_analysis.fRTL;
+ size_t caret = rtl ? run->range.start() : run->range.end() - 1;
+ SelectionModel::CaretPlacement placement =
+ rtl ? SelectionModel::LEADING : SelectionModel::TRAILING;
+ return SelectionModel(cursor, caret, placement);
+}
+
+size_t RenderTextWin::GetIndexOfPreviousGrapheme(size_t position) {
+ return IndexOfAdjacentGrapheme(position, false);
+}
+
+std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
+ ui::Range range(from, to);
+ DCHECK(ui::Range(0, text().length()).Contains(range));
+ Point display_offset(GetUpdatedDisplayOffset());
+ std::vector<Rect> bounds;
+ HRESULT hr = 0;
+
+ // Add a Rect for each run/selection intersection.
+ // TODO(msw): The bounds should probably not always be leading the range ends.
+ for (size_t i = 0; i < runs_.size(); ++i) {
+ internal::TextRun* run = runs_[visual_to_logical_[i]];
+ ui::Range intersection = run->range.Intersect(range);
+ if (intersection.IsValid()) {
+ DCHECK(!intersection.is_reversed());
+ int start_offset = 0;
+ hr = ScriptCPtoX(intersection.start() - run->range.start(),
+ false,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &start_offset);
+ DCHECK(SUCCEEDED(hr));
+ int end_offset = 0;
+ hr = ScriptCPtoX(intersection.end() - run->range.start(),
+ false,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &(run->script_analysis),
+ &end_offset);
+ DCHECK(SUCCEEDED(hr));
+ if (start_offset > end_offset)
+ std::swap(start_offset, end_offset);
+ Rect rect(run->preceding_run_widths + start_offset, 0,
+ end_offset - start_offset, run->font.GetHeight());
+ // Center the rect vertically in the display area.
+ rect.Offset(0, (display_rect().height() - rect.height()) / 2);
+ rect.set_origin(ToViewPoint(rect.origin()));
+ // Union this with the last rect if they're adjacent.
+ if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
+ rect = rect.Union(bounds.back());
+ bounds.pop_back();
+ }
+ bounds.push_back(rect);
+ }
+ }
+ return bounds;
+}
+
+void RenderTextWin::ItemizeLogicalText() {
+ text_is_dirty_ = false;
+ STLDeleteContainerPointers(runs_.begin(), runs_.end());
+ runs_.clear();
+ if (text().empty())
+ return;
+
+ const wchar_t* raw_text = text().c_str();
+ const int text_length = text().length();
+
+ HRESULT hr = E_OUTOFMEMORY;
+ int script_items_count = 0;
+ scoped_array<SCRIPT_ITEM> script_items;
+ for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
+ // Derive the array of Uniscribe script items from the logical text.
+ // ScriptItemize always adds a terminal array item so that the length of the
+ // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
+ script_items.reset(new SCRIPT_ITEM[n]);
+ hr = ScriptItemize(raw_text,
+ text_length,
+ n - 1,
+ &script_control_,
+ &script_state_,
+ script_items.get(),
+ &script_items_count);
+ }
+ DCHECK(SUCCEEDED(hr));
+
+ if (script_items_count <= 0)
+ return;
+
+ // Build the list of runs, merge font/underline styles.
+ // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
+ // TODO(msw): Apply the overriding selection and composition styles.
+ StyleRanges::const_iterator style = style_ranges().begin();
+ SCRIPT_ITEM* script_item = script_items.get();
+ for (int run_break = 0; run_break < text_length;) {
+ internal::TextRun* run = new internal::TextRun();
+ run->range.set_start(run_break);
+ run->font = !style->underline ? style->font :
+ style->font.DeriveFont(0, style->font.GetStyle() | Font::UNDERLINED);
+ run->foreground = style->foreground;
+ run->strike = style->strike;
+ run->script_analysis = script_item->a;
+
+ // Find the range end and advance the structures as needed.
+ int script_item_end = (script_item + 1)->iCharPos;
+ int style_range_end = style->range.end();
+ run_break = std::min(script_item_end, style_range_end);
+ if (script_item_end <= style_range_end)
+ script_item++;
+ if (script_item_end >= style_range_end)
+ style++;
+ run->range.set_end(run_break);
+ runs_.push_back(run);
+ }
+}
+
+void RenderTextWin::LayoutVisualText(HDC hdc) {
+ HRESULT hr = 0;
+ std::vector<internal::TextRun*>::const_iterator run_iter;
+ for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) {
+ internal::TextRun* run = *run_iter;
+ int run_length = run->range.length();
+ string16 run_string(text().substr(run->range.start(), run_length));
+ const wchar_t* run_text = run_string.c_str();
+ // Select the font desired for glyph generation
+ SelectObject(hdc, run->font.GetNativeFont());
+
+ run->logical_clusters.reset(new WORD[run_length]);
+ run->glyph_count = 0;
+ hr = E_OUTOFMEMORY;
+ // kGuess comes from: http://msdn.microsoft.com/en-us/library/dd368564.aspx
+ const int kGuess = static_cast<int>(1.5 * run_length + 16);
+ for (size_t n = kGuess; hr == E_OUTOFMEMORY && n < kMaxGlyphs; n *= 2) {
+ run->glyphs.reset(new WORD[n]);
+ run->visible_attributes.reset(new SCRIPT_VISATTR[n]);
+ hr = ScriptShape(hdc,
+ &script_cache_,
+ run_text,
+ run_length,
+ n,
+ &(run->script_analysis),
+ run->glyphs.get(),
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ &(run->glyph_count));
+ }
+ DCHECK(SUCCEEDED(hr));
+
+ if (run->glyph_count > 0) {
+ run->advance_widths.reset(new int[run->glyph_count]);
+ run->offsets.reset(new GOFFSET[run->glyph_count]);
+ hr = ScriptPlace(hdc,
+ &script_cache_,
+ run->glyphs.get(),
+ run->glyph_count,
+ run->visible_attributes.get(),
+ &(run->script_analysis),
+ run->advance_widths.get(),
+ run->offsets.get(),
+ &(run->abc_widths));
+ DCHECK(SUCCEEDED(hr));
+ }
+ }
+
+ if (runs_.size() > 0) {
+ // Build the array of bidirectional embedding levels.
+ scoped_array<BYTE> levels(new BYTE[runs_.size()]);
+ for (size_t i = 0; i < runs_.size(); ++i)
+ levels[i] = runs_[i]->script_analysis.s.uBidiLevel;
+
+ // Get the maps between visual and logical run indices.
+ visual_to_logical_.reset(new int[runs_.size()]);
+ logical_to_visual_.reset(new int[runs_.size()]);
+ hr = ScriptLayout(runs_.size(),
+ levels.get(),
+ visual_to_logical_.get(),
+ logical_to_visual_.get());
+ DCHECK(SUCCEEDED(hr));
+ }
+
+ // Precalculate run width information.
+ size_t preceding_run_widths = 0;
+ for (size_t i = 0; i < runs_.size(); ++i) {
+ internal::TextRun* run = runs_[visual_to_logical_[i]];
+ run->preceding_run_widths = preceding_run_widths;
+ const ABC& abc = run->abc_widths;
+ run->width = abc.abcA + abc.abcB + abc.abcC;
+ preceding_run_widths += run->width;
+ }
+ string_width_ = preceding_run_widths;
+}
+
+size_t RenderTextWin::GetRunContainingPosition(size_t position) const {
+ // Find the text run containing the argument position.
+ size_t run = 0;
+ for (; run < runs_.size(); ++run)
+ if (runs_[run]->range.start() <= position &&
+ runs_[run]->range.end() > position)
+ break;
+ return run;
+}
+
+size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
+ // Find the text run containing the argument point (assumed already offset).
+ size_t run = 0;
+ for (; run < runs_.size(); ++run)
+ if (runs_[run]->preceding_run_widths <= point.x() &&
+ runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
+ break;
+ return run;
+}
+
+size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) const {
+ size_t run_index = GetRunContainingPosition(index);
+ internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL;
+ long start = run ? run->range.start() : 0;
+ long length = run ? run->range.length() : text().length();
+ long ch = index - start;
+ WORD cluster = run ? run->logical_clusters[ch] : 0;
+
+ if (!next) {
+ do {
+ ch--;
+ } while (ch >= 0 && run && run->logical_clusters[ch] == cluster);
+ } else {
+ while (ch < length && run && run->logical_clusters[ch] == cluster)
+ ch++;
+ }
+ return std::max(static_cast<long>(std::min(ch, length) + start), 0L);
+}
+
+SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
+ internal::TextRun* run) const {
+ size_t caret = run->range.start();
+ size_t cursor = IndexOfAdjacentGrapheme(caret, true);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+}
+
+SelectionModel RenderTextWin::LastSelectionModelInsideRun(
+ internal::TextRun* run) const {
+ size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false);
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+}
+
+SelectionModel RenderTextWin::LeftSelectionModel(
+ const SelectionModel& selection) {
+ size_t caret = selection.caret_pos();
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ size_t run_index = GetRunContainingPosition(caret);
+ DCHECK(run_index < runs_.size());
+ internal::TextRun* run = runs_[run_index];
+
+ // If the caret's associated character is in a LTR run.
+ if (!run->script_analysis.fRTL) {
+ if (caret_placement == SelectionModel::TRAILING)
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ else if (caret > run->range.start()) {
+ caret = IndexOfAdjacentGrapheme(caret, false);
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ }
+ } else { // The caret's associated character is in a RTL run.
+ if (caret_placement == SelectionModel::LEADING) {
+ size_t cursor = IndexOfAdjacentGrapheme(caret, true);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ } else if (selection.selection_end() < run->range.end()) {
+ caret = IndexOfAdjacentGrapheme(caret, true);
+ size_t cursor = IndexOfAdjacentGrapheme(caret, true);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ }
+ }
+
+ // The character is at the begin of its run; go to the previous visual run.
+ size_t visual_index = logical_to_visual_[run_index];
+ if (visual_index == 0)
+ return LeftEndSelectionModel();
+ internal::TextRun* prev = runs_[visual_to_logical_[visual_index - 1]];
+ return prev->script_analysis.fRTL ? FirstSelectionModelInsideRun(prev) :
+ LastSelectionModelInsideRun(prev);
+}
+
+SelectionModel RenderTextWin::RightSelectionModel(
+ const SelectionModel& selection) {
+ size_t caret = selection.caret_pos();
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ size_t run_index = GetRunContainingPosition(caret);
+ DCHECK(run_index < runs_.size());
+ internal::TextRun* run = runs_[run_index];
+
+ // If the caret's associated character is in a LTR run.
+ if (!run->script_analysis.fRTL) {
+ if (caret_placement == SelectionModel::LEADING) {
+ size_t cursor = IndexOfAdjacentGrapheme(caret, true);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ } else if (selection.selection_end() < run->range.end()) {
+ caret = IndexOfAdjacentGrapheme(caret, true);
+ size_t cursor = IndexOfAdjacentGrapheme(caret, true);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ }
+ } else { // The caret's associated character is in a RTL run.
+ if (caret_placement == SelectionModel::TRAILING)
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ else if (caret > run->range.start()) {
+ caret = IndexOfAdjacentGrapheme(caret, false);
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ }
+ }
+
+ // The character is at the end of its run; go to the next visual run.
+ size_t visual_index = logical_to_visual_[run_index];
+ if (visual_index == runs_.size() - 1)
+ return RightEndSelectionModel();
+ internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]];
+ return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) :
+ FirstSelectionModelInsideRun(next);
+}
+
+void RenderTextWin::DrawSelection(Canvas* canvas) {
+ std::vector<Rect> sel(
+ GetSubstringBounds(GetSelectionStart(), GetCursorPosition()));
+ SkColor color = focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
+ for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
+ canvas->FillRectInt(color, i->x(), i->y(), i->width(), i->height());
+}
+
+void RenderTextWin::DrawVisualText(Canvas* canvas) {
+ if (text().empty())
+ return;
+
+ CanvasSkia* canvas_skia = canvas->AsCanvasSkia();
+ skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia);
+
+ Point offset(ToViewPoint(Point()));
+ // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
+ size_t height = default_style().font.GetHeight();
+ // Center the text vertically in the display area.
+ offset.Offset(0, (display_rect().height() - height) / 2);
+
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setAntiAlias(true);
+ paint.setSubpixelText(true);
+ paint.setLCDRenderText(true);
+ SkPoint point(SkPoint::Make(SkIntToScalar(offset.x()),
+ SkIntToScalar(display_rect().height() - offset.y())));
+ RECT rect = display_rect().ToRECT();
+ scoped_array<SkPoint> pos;
+ for (size_t i = 0; i < runs_.size(); ++i) {
+ // Get the run specified by the visual-to-logical map.
+ internal::TextRun* run = runs_[visual_to_logical_[i]];
+
+ // TODO(msw): Font default/fallback and style integration.
+ std::string font(UTF16ToASCII(run->font.GetFontName()));
+ SkTypeface::Style style = SkTypeface::kNormal;
+ SkTypeface* typeface = SkTypeface::CreateFromName(font.c_str(), style);
+ if (typeface) {
+ paint.setTypeface(typeface);
+ // |paint| adds its own ref. Release the ref from CreateFromName.
+ typeface->unref();
+ }
+ // TODO(msw): Skia font size units? Set OmniboxViewViews gfx::Font size?
+ int font_size = run->font.GetFontSize();
+ paint.setTextSize(SkFloatToScalar(font_size * kSkiaFontScale));
+ paint.setColor(run->foreground);
+
+ // Based on WebCore::skiaDrawText.
+ pos.reset(new SkPoint[run->glyph_count]);
+ for (int glyph = 0; glyph < run->glyph_count; glyph++) {
+ pos[glyph].set(point.x() + run->offsets[glyph].du,
+ point.y() + run->offsets[glyph].dv);
+ point.offset(SkIntToScalar(run->advance_widths[glyph]), 0);
+ }
+ size_t byte_length = run->glyph_count * sizeof(WORD);
+ canvas_skia->drawPosText(run->glyphs.get(), byte_length, pos.get(), paint);
+
+ // Draw the strikethrough.
+ if (run->strike) {
+ Rect bounds(offset, Size(run->width, run->font.GetHeight()));
+ SkPaint strike;
+ strike.setAntiAlias(true);
+ strike.setStyle(SkPaint::kFill_Style);
+ strike.setColor(run->foreground);
+ strike.setStrokeWidth(kStrikeWidth);
+ canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()),
+ SkIntToScalar(bounds.bottom()),
+ SkIntToScalar(bounds.right()),
+ SkIntToScalar(bounds.y()),
+ strike);
+ }
+ offset.Offset(run->width, 0);
+ }
+}
+
+void RenderTextWin::DrawCursor(Canvas* canvas) {
+ // Paint cursor. Replace cursor is drawn as rectangle for now.
+ // TODO(msw): Draw a better cursor with a better indication of association.
+ if (cursor_visible() && focused()) {
+ Rect r(GetUpdatedCursorBounds());
+ canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height());
+ }
}
RenderText* RenderText::CreateRenderText() {
diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h
index 829888b..d812f50 100644
--- a/ui/gfx/render_text_win.h
+++ b/ui/gfx/render_text_win.h
@@ -6,17 +6,121 @@
#define UI_GFX_RENDER_TEXT_WIN_H_
#pragma once
+#include <usp10.h>
+
+#include "base/memory/scoped_ptr.h"
#include "ui/gfx/render_text.h"
namespace gfx {
+namespace internal {
+
+struct TextRun {
+ TextRun();
+
+ ui::Range range;
+ Font font;
+ // TODO(msw): Disambiguate color, strike, etc. from TextRuns.
+ // Otherwise, this breaks the glyph shaping process.
+ // See the example at: http://www.catch22.net/tuts/neatpad/12.
+ SkColor foreground;
+ bool strike;
+
+ int width;
+ // The cumulative widths of preceding runs.
+ int preceding_run_widths;
+
+ SCRIPT_ANALYSIS script_analysis;
+
+ scoped_array<WORD> glyphs;
+ scoped_array<WORD> logical_clusters;
+ scoped_array<SCRIPT_VISATTR> visible_attributes;
+ int glyph_count;
+
+ scoped_array<int> advance_widths;
+ scoped_array<GOFFSET> offsets;
+ ABC abc_widths;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TextRun);
+};
+
+} // namespace internal
+
// RenderTextWin is the Windows implementation of RenderText using Uniscribe.
class RenderTextWin : public RenderText {
public:
RenderTextWin();
virtual ~RenderTextWin();
+ // Overridden from RenderText:
+ virtual void SetText(const string16& text) OVERRIDE;
+ virtual void SetDisplayRect(const Rect& r) OVERRIDE;
+ virtual void ApplyStyleRange(StyleRange style_range) OVERRIDE;
+ virtual void ApplyDefaultStyle() OVERRIDE;
+ virtual int GetStringWidth() OVERRIDE;
+ virtual void Draw(Canvas* canvas) OVERRIDE;
+ virtual SelectionModel FindCursorPosition(const Point& point) OVERRIDE;
+ virtual Rect GetCursorBounds(const SelectionModel& selection,
+ bool insert_mode) OVERRIDE;
+
+ protected:
+ // Overridden from RenderText:
+ virtual SelectionModel GetLeftSelectionModel(const SelectionModel& current,
+ BreakType break_type) OVERRIDE;
+ virtual SelectionModel GetRightSelectionModel(const SelectionModel& current,
+ BreakType break_type) OVERRIDE;
+ virtual SelectionModel LeftEndSelectionModel() OVERRIDE;
+ virtual SelectionModel RightEndSelectionModel() OVERRIDE;
+ virtual size_t GetIndexOfPreviousGrapheme(size_t position) OVERRIDE;
+ virtual std::vector<Rect> GetSubstringBounds(size_t from, size_t to) OVERRIDE;
+
private:
+ void ItemizeLogicalText();
+ void LayoutVisualText(HDC hdc);
+
+ // Return the run index that contains the argument; or the length of the
+ // |runs_| vector if argument exceeds the text length or width.
+ size_t GetRunContainingPosition(size_t position) const;
+ size_t GetRunContainingPoint(const Point& point) const;
+
+ // Return an index belonging to the |next| or previous logical grapheme.
+ // The return value is bounded by 0 and the text length, inclusive.
+ size_t IndexOfAdjacentGrapheme(size_t index, bool next) const;
+
+ // Given a |run|, returns the SelectionModel that contains the logical first
+ // or last caret position inside (not at a boundary of) the run.
+ // The returned value represents a cursor/caret position without a selection.
+ SelectionModel FirstSelectionModelInsideRun(internal::TextRun*) const;
+ SelectionModel LastSelectionModelInsideRun(internal::TextRun*) const;
+
+ // Get the selection model visually left/right of |selection| by one grapheme.
+ // The returned value represents a cursor/caret position without a selection.
+ SelectionModel LeftSelectionModel(const SelectionModel& selection);
+ SelectionModel RightSelectionModel(const SelectionModel& selection);
+
+ // Draw the text, cursor, and selection.
+ void DrawSelection(Canvas* canvas);
+ void DrawVisualText(Canvas* canvas);
+ void DrawCursor(Canvas* canvas);
+
+ bool text_is_dirty_;
+ bool style_is_dirty_;
+
+ // National Language Support native digit and digit substitution settings.
+ SCRIPT_DIGITSUBSTITUTE digit_substitute_;
+
+ SCRIPT_CONTROL script_control_;
+ SCRIPT_STATE script_state_;
+
+ SCRIPT_CACHE script_cache_;
+
+ std::vector<internal::TextRun*> runs_;
+ int string_width_;
+
+ scoped_array<int> visual_to_logical_;
+ scoped_array<int> logical_to_visual_;
+
DISALLOW_COPY_AND_ASSIGN(RenderTextWin);
};
diff --git a/views/controls/textfield/native_textfield_views.cc b/views/controls/textfield/native_textfield_views.cc
index 5b43215..e8fd03d 100644
--- a/views/controls/textfield/native_textfield_views.cc
+++ b/views/controls/textfield/native_textfield_views.cc
@@ -874,11 +874,12 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
cursor_changed = true;
break;
case ui::VKEY_END:
- model_->MoveCursorRight(gfx::LINE_BREAK, selection);
- cursor_changed = true;
- break;
case ui::VKEY_HOME:
- model_->MoveCursorLeft(gfx::LINE_BREAK, selection);
+ if ((key_code == ui::VKEY_HOME) ==
+ (GetRenderText()->GetTextDirection() == base::i18n::RIGHT_TO_LEFT))
+ model_->MoveCursorRight(gfx::LINE_BREAK, selection);
+ else
+ model_->MoveCursorLeft(gfx::LINE_BREAK, selection);
cursor_changed = true;
break;
case ui::VKEY_BACK:
@@ -921,7 +922,7 @@ bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
cursor_changed = text_changed = model_->Delete();
break;
case ui::VKEY_INSERT:
- GetRenderText()->toggle_insert_mode();
+ GetRenderText()->ToggleInsertMode();
cursor_changed = true;
break;
default:
diff --git a/views/controls/textfield/textfield_views_model.cc b/views/controls/textfield/textfield_views_model.cc
index 3f927dd..85e2179 100644
--- a/views/controls/textfield/textfield_views_model.cc
+++ b/views/controls/textfield/textfield_views_model.cc
@@ -398,11 +398,6 @@ bool TextfieldViewsModel::MoveCursorTo(const gfx::Point& point, bool select) {
return render_text_->MoveCursorTo(point, select);
}
-std::vector<gfx::Rect> TextfieldViewsModel::GetSelectionBounds() const {
- return render_text_->GetSubstringBounds(render_text_->GetSelectionStart(),
- render_text_->GetCursorPosition());
-}
-
string16 TextfieldViewsModel::GetSelectedText() const {
return GetText().substr(render_text_->MinOfSelection(),
(render_text_->MaxOfSelection() - render_text_->MinOfSelection()));
@@ -421,7 +416,7 @@ void TextfieldViewsModel::SelectRange(const ui::Range& range) {
void TextfieldViewsModel::SelectSelectionModel(const gfx::SelectionModel& sel) {
if (HasCompositionText())
ConfirmCompositionText();
- render_text_->SetSelectionModel(sel);
+ render_text_->MoveCursorTo(sel);
}
void TextfieldViewsModel::SelectAll() {
@@ -509,7 +504,7 @@ bool TextfieldViewsModel::Cut() {
render_text_->GetSelectionStart(),
render_text_->GetSelectionStart(),
gfx::SelectionModel::LEADING);
- render_text_->SetSelectionModel(sel);
+ render_text_->MoveCursorTo(sel);
DeleteSelection();
return true;
}
@@ -589,7 +584,7 @@ void TextfieldViewsModel::SetCompositionText(
size_t end =
std::min(range.start() + composition.selection.end(), range.end());
gfx::SelectionModel sel(start, end);
- render_text_->SetSelectionModel(sel);
+ render_text_->MoveCursorTo(sel);
} else {
render_text_->SetCursorPosition(range.end());
}
@@ -662,7 +657,7 @@ void TextfieldViewsModel::ReplaceTextInternal(const string16& text,
size_t cursor = GetCursorPosition();
gfx::SelectionModel sel(render_text_->selection_model());
sel.set_selection_start(cursor + text.length());
- render_text_->SetSelectionModel(sel);
+ render_text_->MoveCursorTo(sel);
}
// Edit history is recorded in InsertText.
InsertTextInternal(text, mergeable);
diff --git a/views/controls/textfield/textfield_views_model.h b/views/controls/textfield/textfield_views_model.h
index 616255f..abd1e13 100644
--- a/views/controls/textfield/textfield_views_model.h
+++ b/views/controls/textfield/textfield_views_model.h
@@ -143,9 +143,6 @@ class VIEWS_EXPORT TextfieldViewsModel {
// Helper function to call MoveCursorTo on the TextfieldViewsModel.
bool MoveCursorTo(const gfx::Point& point, bool select);
- // Returns the bounds of selected text.
- std::vector<gfx::Rect> GetSelectionBounds() const;
-
// Selection related method
// Returns the selected text.
diff --git a/views/examples/textfield_example.cc b/views/examples/textfield_example.cc
index bb1ab24..af0f2b9 100644
--- a/views/examples/textfield_example.cc
+++ b/views/examples/textfield_example.cc
@@ -90,22 +90,27 @@ void TextfieldExample::ButtonPressed(views::Button* sender,
} else if (sender == set_) {
name_->SetText(WideToUTF16(L"[set]"));
} else if (sender == set_style_) {
- gfx::StyleRange color;
- color.foreground = SK_ColorYELLOW;
- color.range = ui::Range(0, 11);
- name_->ApplyStyleRange(color);
+ if (!name_->text().empty()) {
+ gfx::StyleRange color;
+ color.foreground = SK_ColorYELLOW;
+ color.range = ui::Range(0, name_->text().length());
+ name_->ApplyStyleRange(color);
- gfx::StyleRange underline;
- underline.underline = true;
- underline.foreground = SK_ColorBLUE;
- underline.range = ui::Range(1, 7);
- name_->ApplyStyleRange(underline);
+ if (name_->text().length() >= 5) {
+ size_t fifth = name_->text().length() / 5;
+ gfx::StyleRange underline;
+ underline.underline = true;
+ underline.foreground = SK_ColorBLUE;
+ underline.range = ui::Range(1 * fifth, 4 * fifth);
+ name_->ApplyStyleRange(underline);
- gfx::StyleRange strike;
- strike.strike = true;
- strike.foreground = SK_ColorRED;
- strike.range = ui::Range(6, 9);
- name_->ApplyStyleRange(strike);
+ gfx::StyleRange strike;
+ strike.strike = true;
+ strike.foreground = SK_ColorRED;
+ strike.range = ui::Range(2 * fifth, 3 * fifth);
+ name_->ApplyStyleRange(strike);
+ }
+ }
}
}