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

Unified Diff: ui/gfx/render_text_win.cc

Issue 9390022: Simplify handling of BiDi cursor movement (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: address comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/render_text_win.cc
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index 05ed0abba862c16fabcae41ca5dacf08031809b1..67e29ede00c800ee58b85ce0a1c25e6d6b5628b1 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -138,6 +138,26 @@ TextRun::~TextRun() {
ScriptFreeCache(&script_cache);
}
+// Returns the X coordinate of the leading or |trailing| edge of the glyph
+// starting at |index|, relative to the left of the text (not the view).
+int GetGlyphXBoundary(internal::TextRun* run, size_t index, bool trailing) {
+ DCHECK_GE(index, run->range.start())
+ DCHECK_LT(index, run->range.end())
+ int x = 0;
+ HRESULT hr = ScriptCPtoX(
+ index - run->range.start(),
+ trailing,
+ run->range.length(),
+ run->glyph_count,
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ run->advance_widths.get(),
+ &run->script_analysis,
+ &x);
+ DCHECK(SUCCEEDED(hr));
+ return run->preceding_run_widths + x;
+}
+
} // namespace internal
// static
@@ -169,9 +189,10 @@ base::i18n::TextDirection RenderTextWin::GetTextDirection() {
return base::i18n::LEFT_TO_RIGHT;
}
-int RenderTextWin::GetStringWidth() {
+Size RenderTextWin::GetStringSize() {
EnsureLayout();
- return string_width_;
+ // TODO(msw): Use the largest font instead of the default font?
+ return Size(string_width_, GetFont().GetHeight());
}
SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
@@ -197,106 +218,54 @@ SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
&position,
&trailing);
DCHECK(SUCCEEDED(hr));
+ DCHECK_GE(trailing, 0);
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) {
- EnsureLayout();
-
- // 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() : GetFont().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;
+ return SelectionModel(cursor, trailing ? CURSOR_BACKWARD : CURSOR_FORWARD);
}
SelectionModel RenderTextWin::AdjacentCharSelectionModel(
- const SelectionModel& selection, VisualCursorDirection direction) {
+ const SelectionModel& selection,
+ VisualCursorDirection direction) {
DCHECK(!needs_layout_);
- 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];
-
- bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT);
- if (forward_motion) {
- if (caret_placement == SelectionModel::LEADING) {
- size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- } else if (selection.selection_end() < run->range.end()) {
- caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
- size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
- }
+ internal::TextRun* run;
+ size_t run_index = GetRunContainingCaret(selection);
+ if (run_index == runs_.size()) {
+ // The cursor is not in any run: we're at the visual and logical edge.
+ SelectionModel edge = EdgeSelectionModel(direction);
+ if (edge.caret_pos() == selection.caret_pos())
+ return edge;
+ else
+ run = direction == CURSOR_RIGHT ? runs_.front() : runs_.back();
} else {
- if (caret_placement == SelectionModel::TRAILING)
- return SelectionModel(caret, caret, SelectionModel::LEADING);
- else if (caret > run->range.start()) {
- caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
- return SelectionModel(caret, caret, SelectionModel::LEADING);
+ // If the cursor is moving within the current run, just move it by one
+ // grapheme in the appropriate direction.
+ run = runs_[run_index];
+ size_t caret = selection.caret_pos();
+ bool forward_motion =
+ run->script_analysis.fRTL == (direction == CURSOR_LEFT);
+ if (forward_motion) {
+ if (caret < run->range.end()) {
+ caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
+ return SelectionModel(caret, CURSOR_BACKWARD);
+ }
+ } else {
+ if (caret > run->range.start()) {
+ caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
+ return SelectionModel(caret, CURSOR_FORWARD);
+ }
}
+ // The cursor is at the edge of a run; move to the visually adjacent run.
+ int visual_index = logical_to_visual_[run_index];
+ visual_index += (direction == CURSOR_LEFT) ? -1 : 1;
+ if (visual_index < 0 || visual_index >= runs_.size())
+ return EdgeSelectionModel(direction);
+ run = runs_[visual_to_logical_[visual_index]];
}
-
- // The character is at the beginning/end of its run; go to the previous/next
- // visual run.
- size_t visual_index = logical_to_visual_[run_index];
- if (visual_index == (direction == CURSOR_LEFT ? 0 : runs_.size() - 1))
- return EdgeSelectionModel(direction);
- internal::TextRun* adjacent = runs_[visual_to_logical_[
- direction == CURSOR_LEFT ? visual_index - 1 : visual_index + 1]];
- forward_motion = adjacent->script_analysis.fRTL == (direction == CURSOR_LEFT);
- return forward_motion ? FirstSelectionModelInsideRun(adjacent) :
- LastSelectionModelInsideRun(adjacent);
+ bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT);
+ return forward_motion ? FirstSelectionModelInsideRun(run) :
+ LastSelectionModelInsideRun(run);
}
// TODO(msw): Implement word breaking for Windows.
@@ -311,10 +280,10 @@ SelectionModel RenderTextWin::AdjacentWordSelectionModel(
size_t pos;
if (direction == CURSOR_RIGHT) {
- pos = std::min(selection.selection_end() + 1, text().length());
+ pos = std::min(selection.caret_pos() + 1, text().length());
while (iter.Advance()) {
pos = iter.pos();
- if (iter.IsWord() && pos > selection.selection_end())
+ if (iter.IsWord() && pos > selection.caret_pos())
break;
}
} else { // direction == CURSOR_LEFT
@@ -322,15 +291,15 @@ SelectionModel RenderTextWin::AdjacentWordSelectionModel(
// 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.
- pos = std::max<int>(selection.selection_end() - 1, 0);
+ pos = std::max<int>(selection.caret_pos() - 1, 0);
while (iter.Advance()) {
if (iter.IsWord()) {
size_t begin = iter.pos() - iter.GetString().length();
- if (begin == selection.selection_end()) {
+ if (begin == selection.caret_pos()) {
// The cursor is at the beginning of a word.
// Move to previous word.
break;
- } else if (iter.pos() >= selection.selection_end()) {
+ } else if (iter.pos() >= selection.caret_pos()) {
// The cursor is in the middle or at the end of a word.
// Move to the top of current word.
pos = begin;
@@ -341,40 +310,29 @@ SelectionModel RenderTextWin::AdjacentWordSelectionModel(
}
}
}
- return SelectionModel(pos, pos, SelectionModel::LEADING);
+ return SelectionModel(pos, CURSOR_FORWARD);
}
-SelectionModel RenderTextWin::EdgeSelectionModel(
- VisualCursorDirection direction) {
- if (text().empty())
- return SelectionModel(0, 0, SelectionModel::LEADING);
-
- EnsureLayout();
- size_t cursor = (direction == GetVisualDirectionOfLogicalEnd()) ?
- text().length() : 0;
- internal::TextRun* run = runs_[
- visual_to_logical_[direction == CURSOR_RIGHT ? runs_.size() - 1 : 0]];
- size_t caret;
- SelectionModel::CaretPlacement placement;
- if (run->script_analysis.fRTL == (direction == CURSOR_RIGHT)) {
- caret = run->range.start();
- placement = SelectionModel::LEADING;
- } else {
- caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
- placement = SelectionModel::TRAILING;
- }
- return SelectionModel(cursor, caret, placement);
+void RenderTextWin::GetGlyphBounds(size_t index,
+ ui::Range* xspan,
+ int* height) {
+ size_t run_index =
+ GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
+ DCHECK_LT(run_index, runs_.size());
+ internal::TextRun* run = runs_[run_index];
+ xspan->set_start(GetGlyphXBoundary(run, index, false));
+ xspan->set_end(GetGlyphXBoundary(run, index, true));
+ *height = run->font.GetHeight();
}
-std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
+std::vector<Rect> RenderTextWin::GetSubstringBounds(ui::Range range) {
DCHECK(!needs_layout_);
- ui::Range range(from, to);
DCHECK(ui::Range(0, text().length()).Contains(range));
Point display_offset(GetUpdatedDisplayOffset());
HRESULT hr = 0;
std::vector<Rect> bounds;
- if (from == to)
+ if (range.is_empty())
return bounds;
// Add a Rect for each run/selection intersection.
@@ -384,32 +342,9 @@ std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) {
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());
+ ui::Range range(GetGlyphXBoundary(run, intersection.start(), false),
+ GetGlyphXBoundary(run, intersection.end(), false));
+ Rect rect(range.GetMin(), 0, range.length(), 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()));
@@ -439,7 +374,8 @@ bool RenderTextWin::IsCursorablePosition(size_t position) {
return true;
EnsureLayout();
- size_t run_index = GetRunContainingPosition(position);
+ size_t run_index =
+ GetRunContainingCaret(SelectionModel(position, CURSOR_FORWARD));
if (run_index >= runs_.size())
return false;
@@ -539,7 +475,8 @@ size_t RenderTextWin::IndexOfAdjacentGrapheme(
}
}
- size_t run_index = GetRunContainingPosition(index);
+ size_t run_index =
+ GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
DCHECK(run_index < runs_.size());
internal::TextRun* run = runs_[run_index];
size_t start = run->range.start();
@@ -788,13 +725,13 @@ const std::vector<Font>* RenderTextWin::GetLinkedFonts(const Font& font) const {
return linked_fonts;
}
-size_t RenderTextWin::GetRunContainingPosition(size_t position) const {
+size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const {
DCHECK(!needs_layout_);
- // Find the text run containing the argument position.
+ size_t position = caret.caret_pos();
+ LogicalCursorDirection affinity = caret.caret_affinity();
size_t run = 0;
for (; run < runs_.size(); ++run)
- if (runs_[run]->range.start() <= position &&
- runs_[run]->range.end() > position)
+ if (RangeContainsCaret(runs_[run]->range, position, affinity))
break;
return run;
}
@@ -812,15 +749,14 @@ size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
internal::TextRun* run) {
- size_t caret = run->range.start();
- size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
- return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ size_t cursor = IndexOfAdjacentGrapheme(run->range.start(), CURSOR_FORWARD);
+ return SelectionModel(cursor, CURSOR_BACKWARD);
}
SelectionModel RenderTextWin::LastSelectionModelInsideRun(
internal::TextRun* run) {
size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
- return SelectionModel(caret, caret, SelectionModel::LEADING);
+ return SelectionModel(caret, CURSOR_FORWARD);
}
RenderText* RenderText::CreateRenderText() {

Powered by Google App Engine
This is Rietveld 408576698