Index: ui/gfx/render_text_linux.cc |
diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc |
index 133bd4eb63d435ff7daf451b8f08f9fb4a889b0d..a28acc30158c296cde7fca80a80ea4b6a9e5bad1 100644 |
--- a/ui/gfx/render_text_linux.cc |
+++ b/ui/gfx/render_text_linux.cc |
@@ -78,130 +78,93 @@ base::i18n::TextDirection RenderTextLinux::GetTextDirection() { |
return base::i18n::LEFT_TO_RIGHT; |
} |
-int RenderTextLinux::GetStringWidth() { |
+Size RenderTextLinux::GetStringSize() { |
EnsureLayout(); |
- int width; |
- pango_layout_get_pixel_size(layout_, &width, NULL); |
- return width; |
+ int width, height; |
+ pango_layout_get_pixel_size(layout_, &width, &height); |
+ return Size(width, height); |
+} |
+ |
+void RenderTextLinux::GetGlyphBounds(size_t index, |
+ ui::Range* xspan, |
+ int* height) { |
+ PangoRectangle pos; |
+ pango_layout_index_to_pos(layout_, Utf16IndexToUtf8Index(index), &pos); |
+ *xspan = ui::Range(PANGO_PIXELS(pos.x), PANGO_PIXELS(pos.x + pos.width)); |
+ *height = PANGO_PIXELS(pos.height); |
} |
SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { |
EnsureLayout(); |
if (text().empty()) |
- return SelectionModel(0, 0, SelectionModel::LEADING); |
+ return SelectionModel(0, CURSOR_FORWARD); |
Point p(ToTextPoint(point)); |
// When the point is outside of text, return HOME/END position. |
if (p.x() < 0) |
return EdgeSelectionModel(CURSOR_LEFT); |
- else if (p.x() > GetStringWidth()) |
+ else if (p.x() > GetStringSize().width()) |
return EdgeSelectionModel(CURSOR_RIGHT); |
int caret_pos, trailing; |
pango_layout_xy_to_index(layout_, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, |
&caret_pos, &trailing); |
- size_t selection_end = caret_pos; |
- if (trailing > 0) { |
- const char* ch = g_utf8_offset_to_pointer(layout_text_ + caret_pos, |
- trailing); |
- DCHECK_GE(ch, layout_text_); |
- DCHECK_LE(ch, layout_text_ + layout_text_len_); |
- selection_end = ch - layout_text_; |
+ DCHECK(trailing >= 0); |
+ if (trailing) { |
+ caret_pos = g_utf8_offset_to_pointer(layout_text_ + caret_pos, |
+ trailing) - layout_text_; |
+ DCHECK_LE(static_cast<size_t>(caret_pos), layout_text_len_); |
} |
return SelectionModel( |
- Utf8IndexToUtf16Index(selection_end), |
Utf8IndexToUtf16Index(caret_pos), |
- trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING); |
-} |
- |
-Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection, |
- bool insert_mode) { |
- EnsureLayout(); |
- |
- size_t caret_pos = insert_mode ? selection.caret_pos() : |
- selection.selection_end(); |
- PangoRectangle pos; |
- pango_layout_index_to_pos(layout_, Utf16IndexToUtf8Index(caret_pos), &pos); |
- |
- SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); |
- int x = pos.x; |
- if ((insert_mode && caret_placement == SelectionModel::TRAILING) || |
- (!insert_mode && pos.width < 0)) |
- x += pos.width; |
- x = PANGO_PIXELS(x); |
- |
- int h = std::min(display_rect().height(), PANGO_PIXELS(pos.height)); |
- Rect bounds(x, (display_rect().height() - h) / 2, 0, h); |
- bounds.set_origin(ToViewPoint(bounds.origin())); |
- |
- if (!insert_mode) |
- bounds.set_width(PANGO_PIXELS(std::abs(pos.width))); |
- |
- return bounds; |
+ trailing ? CURSOR_BACKWARD : CURSOR_FORWARD); |
} |
-// Assume caret_pos in |current| is n, 'l' represents leading in |
-// caret_placement and 't' represents trailing in caret_placement. Following |
-// is the calculation from (caret_pos, caret_placement) in |current| to |
-// (selection_end, caret_pos, caret_placement) when moving cursor left/right by |
-// one grapheme (for simplicity, assume each grapheme is one character). |
-// If n is in LTR (if moving left) or RTL (if moving right) run, |
-// (n, t) --> (n, n, l). |
-// (n, l) --> (n-1, n-1, l) if n is inside run (not at boundary). |
-// (n, l) --> goto across run case if n is at run boundary. |
-// Otherwise, |
-// (n, l) --> (n+1, n, t). |
-// (n, t) --> (n+2, n+1, t) if n is inside run. |
-// (n, t) --> goto across run case if n is at run boundary. |
-// If n is at run boundary, get its visually left/right run, |
-// If left/right run is LTR/RTL run, |
-// (n, t) --> (left/right run's end, left/right run's end, l). |
-// Otherwise, |
-// (n, t) --> (left/right run's begin + 1, left/right run's begin, t). |
SelectionModel RenderTextLinux::AdjacentCharSelectionModel( |
const SelectionModel& selection, |
VisualCursorDirection direction) { |
size_t caret = selection.caret_pos(); |
- SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); |
- GSList* run = GetRunContainingPosition(caret); |
- DCHECK(run); |
- |
- PangoLayoutRun* layout_run = reinterpret_cast<PangoLayoutRun*>(run->data); |
- PangoItem* item = layout_run->item; |
- size_t run_start = Utf8IndexToUtf16Index(item->offset); |
- size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length); |
- |
- if (!IsForwardMotion(direction, item)) { |
- if (caret_placement == SelectionModel::TRAILING) |
- return SelectionModel(caret, caret, SelectionModel::LEADING); |
- else if (caret > run_start) { |
- caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); |
- return SelectionModel(caret, caret, SelectionModel::LEADING); |
- } |
+ |
+ GSList* run = GetRunContainingCaret(selection); |
+ if (!run) { |
+ // Cursor not in any run: we're at the visual & logical edge. |
+ SelectionModel edge = EdgeSelectionModel(direction); |
+ if (text().empty() || |
+ edge == SelectionModel(caret, selection.caret_affinity())) |
+ return edge; |
+ else if (direction == CURSOR_RIGHT) |
+ run = current_line_->runs; |
+ else |
+ run = g_slist_last(current_line_->runs); |
} else { |
- if (caret_placement == SelectionModel::LEADING) { |
- size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); |
- return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
- } else if (selection.selection_end() < run_end) { |
- caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); |
- size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); |
- return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
+ // If the cursor is moving within the current run, just move it by one |
+ // grapheme in the appropriate direction. |
+ PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
+ if (IsForwardMotion(direction, item)) { |
+ size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length); |
+ if (caret < run_end) { |
+ caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); |
+ return SelectionModel(caret, CURSOR_BACKWARD); |
+ } |
+ } else { |
+ size_t run_start = Utf8IndexToUtf16Index(item->offset); |
+ if (caret > run_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. |
+ // TODO(xji): Keep a vector of runs to avoid using a singly-linked list. |
+ run = (direction == CURSOR_RIGHT) ? |
+ run->next : GSListPrevious(current_line_->runs, run); |
+ if (!run) |
+ return EdgeSelectionModel(direction); |
} |
- |
- // The character is at the edge of its run; advance to the adjacent visual |
- // run. |
- // TODO(xji): Keep a vector of runs to avoid using a singly-linked list. |
- GSList* adjacent_run = (direction == CURSOR_RIGHT) ? |
- run->next : GSListPrevious(current_line_->runs, run); |
- if (!adjacent_run) |
- return EdgeSelectionModel(direction); |
- |
- item = reinterpret_cast<PangoLayoutRun*>(adjacent_run->data)->item; |
+ PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
return IsForwardMotion(direction, item) ? |
FirstSelectionModelInsideRun(item) : LastSelectionModelInsideRun(item); |
} |
@@ -215,69 +178,41 @@ SelectionModel RenderTextLinux::AdjacentWordSelectionModel( |
if (!success) |
return selection; |
- SelectionModel end = EdgeSelectionModel(direction); |
SelectionModel cur(selection); |
- while (!cur.Equals(end)) { |
+ for (;;) { |
cur = AdjacentCharSelectionModel(cur, direction); |
- size_t caret = cur.caret_pos(); |
- GSList* run = GetRunContainingPosition(caret); |
- DCHECK(run); |
+ GSList* run = GetRunContainingCaret(cur); |
+ if (!run) |
+ break; |
PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
- size_t cursor = cur.selection_end(); |
+ size_t cursor = cur.caret_pos(); |
if (IsForwardMotion(direction, item) ? |
iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor)) |
- return cur; |
+ break; |
} |
- return end; |
-} |
- |
-SelectionModel RenderTextLinux::EdgeSelectionModel( |
- VisualCursorDirection direction) { |
- if (direction == GetVisualDirectionOfLogicalEnd()) { |
- // Advance to the logical end of the text. |
- GSList* run = current_line_->runs; |
- if (direction == CURSOR_RIGHT) |
- run = g_slist_last(run); |
- if (run) { |
- PangoLayoutRun* end_run = reinterpret_cast<PangoLayoutRun*>(run->data); |
- PangoItem* item = end_run->item; |
- if (IsForwardMotion(direction, item)) { |
- size_t caret = Utf8IndexToUtf16Index( |
- Utf8IndexOfAdjacentGrapheme(item->offset + item->length, |
- CURSOR_BACKWARD)); |
- return SelectionModel(text().length(), caret, SelectionModel::TRAILING); |
- } else { |
- size_t caret = Utf8IndexToUtf16Index(item->offset); |
- return SelectionModel(text().length(), caret, SelectionModel::LEADING); |
- } |
- } |
- } |
- return SelectionModel(0, 0, SelectionModel::LEADING); |
+ return cur; |
} |
void RenderTextLinux::SetSelectionModel(const SelectionModel& model) { |
- if (GetSelectionStart() != model.selection_start() || |
- GetCursorPosition() != model.selection_end()) { |
+ if (selection() != model.selection()) |
selection_visual_bounds_.clear(); |
- } |
RenderText::SetSelectionModel(model); |
} |
-std::vector<Rect> RenderTextLinux::GetSubstringBounds(size_t from, size_t to) { |
- DCHECK(from <= text().length()); |
- DCHECK(to <= text().length()); |
+std::vector<Rect> RenderTextLinux::GetSubstringBounds(ui::Range range) { |
+ DCHECK(range.GetMax() <= text().length()); |
- if (from == to) |
+ if (range.is_empty()) |
return std::vector<Rect>(); |
EnsureLayout(); |
- if (from == GetSelectionStart() && to == GetCursorPosition()) |
+ if (range == selection()) |
return GetSelectionBounds(); |
else |
- return CalculateSubstringBounds(from, to); |
+ return CalculateSubstringBounds(range); |
} |
bool RenderTextLinux::IsCursorablePosition(size_t position) { |
@@ -473,14 +408,16 @@ size_t RenderTextLinux::IndexOfAdjacentGrapheme( |
Utf8IndexOfAdjacentGrapheme(Utf16IndexToUtf8Index(index), direction)); |
} |
-GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const { |
+GSList* RenderTextLinux::GetRunContainingCaret( |
+ const SelectionModel& caret) const { |
+ size_t position = Utf16IndexToUtf8Index(caret.caret_pos()); |
+ // NB: exploits unsigned wraparound (0 - 1) |
Alexei Svitkine (slow)
2012/02/14 20:16:31
This is undefined behavior per the C++ standard, s
benrg
2012/02/14 23:39:02
Only signed arithmetic overflow is undefined. Unsi
|
+ ui::Range pos(position, caret.caret_affinity() == CURSOR_BACKWARD ? |
+ position - 1 : position + 1); |
GSList* run = current_line_->runs; |
while (run) { |
PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
- size_t run_start = Utf8IndexToUtf16Index(item->offset); |
- size_t run_end = Utf8IndexToUtf16Index(item->offset + item->length); |
- |
- if (position >= run_start && position < run_end) |
+ if (ui::Range(item->offset, item->offset + item->length).Contains(pos)) |
return run; |
run = run->next; |
} |
@@ -515,17 +452,16 @@ size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme( |
SelectionModel RenderTextLinux::FirstSelectionModelInsideRun( |
const PangoItem* item) const { |
- size_t caret = Utf8IndexToUtf16Index(item->offset); |
size_t cursor = Utf8IndexToUtf16Index( |
Utf8IndexOfAdjacentGrapheme(item->offset, CURSOR_FORWARD)); |
- return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
+ return SelectionModel(cursor, CURSOR_BACKWARD); |
} |
SelectionModel RenderTextLinux::LastSelectionModelInsideRun( |
const PangoItem* item) const { |
size_t caret = Utf8IndexToUtf16Index(Utf8IndexOfAdjacentGrapheme( |
item->offset + item->length, CURSOR_BACKWARD)); |
- return SelectionModel(caret, caret, SelectionModel::LEADING); |
+ return SelectionModel(caret, CURSOR_FORWARD); |
} |
void RenderTextLinux::ResetLayout() { |
@@ -575,16 +511,13 @@ size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const { |
return utf16_index; |
} |
-std::vector<Rect> RenderTextLinux::CalculateSubstringBounds(size_t from, |
- size_t to) { |
+std::vector<Rect> RenderTextLinux::CalculateSubstringBounds(ui::Range range) { |
int* ranges; |
int n_ranges; |
- size_t from_in_utf8 = Utf16IndexToUtf8Index(from); |
- size_t to_in_utf8 = Utf16IndexToUtf8Index(to); |
pango_layout_line_get_x_ranges( |
current_line_, |
- std::min(from_in_utf8, to_in_utf8), |
- std::max(from_in_utf8, to_in_utf8), |
+ Utf16IndexToUtf8Index(range.GetMin()), |
+ Utf16IndexToUtf8Index(range.GetMax()), |
&ranges, |
&n_ranges); |
@@ -607,8 +540,7 @@ std::vector<Rect> RenderTextLinux::CalculateSubstringBounds(size_t from, |
std::vector<Rect> RenderTextLinux::GetSelectionBounds() { |
if (selection_visual_bounds_.empty()) |
- selection_visual_bounds_ = |
- CalculateSubstringBounds(GetSelectionStart(), GetCursorPosition()); |
+ selection_visual_bounds_ = CalculateSubstringBounds(selection()); |
return selection_visual_bounds_; |
} |