Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/render_text.h" | 5 #include "ui/gfx/render_text.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/i18n/break_iterator.h" | 9 #include "base/i18n/break_iterator.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 split_style.range.set_end(new_range.start()); | 60 split_style.range.set_end(new_range.start()); |
| 61 i = style_ranges->insert(i, split_style) + 1; | 61 i = style_ranges->insert(i, split_style) + 1; |
| 62 i->range.set_start(new_range.end()); | 62 i->range.set_start(new_range.end()); |
| 63 break; | 63 break; |
| 64 } else if (i->range.start() < new_range.start()) { | 64 } else if (i->range.start() < new_range.start()) { |
| 65 i->range.set_end(new_range.start()); | 65 i->range.set_end(new_range.start()); |
| 66 i++; | 66 i++; |
| 67 } else if (i->range.end() > new_range.end()) { | 67 } else if (i->range.end() > new_range.end()) { |
| 68 i->range.set_start(new_range.end()); | 68 i->range.set_start(new_range.end()); |
| 69 break; | 69 break; |
| 70 } else | 70 } else { |
| 71 NOTREACHED(); | 71 NOTREACHED(); |
| 72 } | |
| 72 } | 73 } |
| 73 // Add the new range in its sorted location. | 74 // Add the new range in its sorted location. |
| 74 style_ranges->insert(i, style_range); | 75 style_ranges->insert(i, style_range); |
| 75 } | 76 } |
| 76 | 77 |
| 77 } // namespace | 78 } // namespace |
| 78 | 79 |
| 79 namespace gfx { | 80 namespace gfx { |
| 80 | 81 |
| 81 StyleRange::StyleRange() | 82 StyleRange::StyleRange() |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 if (break_type == WORD_BREAK) | 180 if (break_type == WORD_BREAK) |
| 180 position = GetRightSelectionModel(position, break_type); | 181 position = GetRightSelectionModel(position, break_type); |
| 181 } else { | 182 } else { |
| 182 position = GetRightSelectionModel(position, break_type); | 183 position = GetRightSelectionModel(position, break_type); |
| 183 } | 184 } |
| 184 if (select) | 185 if (select) |
| 185 position.set_selection_start(GetSelectionStart()); | 186 position.set_selection_start(GetSelectionStart()); |
| 186 MoveCursorTo(position); | 187 MoveCursorTo(position); |
| 187 } | 188 } |
| 188 | 189 |
| 189 bool RenderText::MoveCursorTo(const SelectionModel& selection_model) { | 190 bool RenderText::MoveCursorTo(const SelectionModel& model) { |
| 190 SelectionModel sel(selection_model); | 191 SelectionModel sel(model); |
| 191 size_t text_length = text().length(); | 192 size_t text_length = text().length(); |
| 192 // Enforce valid selection model components. | 193 // Enforce valid selection model components. |
| 193 if (sel.selection_start() > text_length) | 194 if (sel.selection_start() > text_length) |
| 194 sel.set_selection_start(text_length); | 195 sel.set_selection_start(text_length); |
| 195 if (sel.selection_end() > text_length) | 196 if (sel.selection_end() > text_length) |
| 196 sel.set_selection_end(text_length); | 197 sel.set_selection_end(text_length); |
| 197 // The current model only supports caret positions at valid character indices. | 198 // The current model only supports caret positions at valid character indices. |
| 198 if (text_length == 0) { | 199 if (text_length == 0) { |
| 199 sel.set_caret_pos(0); | 200 sel.set_caret_pos(0); |
| 200 sel.set_caret_placement(SelectionModel::LEADING); | 201 sel.set_caret_placement(SelectionModel::LEADING); |
| 201 } else if (sel.caret_pos() >= text_length) { | 202 } else if (sel.caret_pos() >= text_length) { |
| 202 SelectionModel end = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? | 203 SelectionModel end = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? |
| 203 LeftEndSelectionModel() : RightEndSelectionModel(); | 204 LeftEndSelectionModel() : RightEndSelectionModel(); |
| 204 sel.set_caret_pos(end.caret_pos()); | 205 sel.set_caret_pos(end.caret_pos()); |
| 205 sel.set_caret_placement(end.caret_placement()); | 206 sel.set_caret_placement(end.caret_placement()); |
| 206 } | 207 } |
| 207 | 208 |
| 208 if (!IsCursorablePosition(sel.selection_start()) || | 209 if (!IsCursorablePosition(sel.selection_start(), false) || |
| 209 !IsCursorablePosition(sel.selection_end()) || | 210 !IsCursorablePosition(sel.selection_end(), false) || |
| 210 !IsCursorablePosition(sel.caret_pos())) | 211 !IsCursorablePosition(sel.caret_pos(), |
| 212 (sel.caret_placement() == SelectionModel::TRAILING))) { | |
|
xji
2011/11/29 21:15:48
I did not quite get the reason of introducing "boo
| |
| 211 return false; | 213 return false; |
| 214 } | |
| 212 | 215 |
| 213 bool changed = !sel.Equals(selection_model_); | 216 bool changed = !sel.Equals(selection_model_); |
| 214 SetSelectionModel(sel); | 217 SetSelectionModel(sel); |
| 215 return changed; | 218 return changed; |
| 216 } | 219 } |
| 217 | 220 |
| 218 bool RenderText::MoveCursorTo(const Point& point, bool select) { | 221 bool RenderText::MoveCursorTo(const Point& point, bool select) { |
| 219 SelectionModel selection = FindCursorPosition(point); | 222 SelectionModel selection = FindCursorPosition(point); |
| 220 if (select) | 223 if (select) |
| 221 selection.set_selection_start(GetSelectionStart()); | 224 selection.set_selection_start(GetSelectionStart()); |
| 222 return MoveCursorTo(selection); | 225 return MoveCursorTo(selection); |
| 223 } | 226 } |
| 224 | 227 |
| 225 bool RenderText::SelectRange(const ui::Range& range) { | 228 bool RenderText::SelectRange(const ui::Range& range) { |
| 226 size_t text_length = text().length(); | 229 size_t text_length = text().length(); |
| 227 size_t start = std::min(range.start(), text_length); | 230 size_t start = std::min(range.start(), text_length); |
| 228 size_t end = std::min(range.end(), text_length); | 231 size_t end = std::min(range.end(), text_length); |
| 229 | 232 |
| 230 if (!IsCursorablePosition(start) || !IsCursorablePosition(end)) | 233 if (!IsCursorablePosition(start, false) || !IsCursorablePosition(end, false)) |
| 231 return false; | 234 return false; |
| 232 | 235 |
| 233 size_t pos = end; | 236 size_t pos = end; |
| 234 SelectionModel::CaretPlacement placement = SelectionModel::LEADING; | 237 SelectionModel::CaretPlacement placement = SelectionModel::LEADING; |
| 235 if (start < end) { | 238 if (start < end) { |
| 236 pos = GetIndexOfPreviousGrapheme(end); | 239 pos = GetIndexOfPreviousGrapheme(end); |
| 237 DCHECK_LT(pos, end); | 240 DCHECK_LT(pos, end); |
| 238 placement = SelectionModel::TRAILING; | 241 placement = SelectionModel::TRAILING; |
| 239 } else if (end == text_length) { | 242 } else if (end == text_length) { |
| 240 SelectionModel boundary = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? | 243 SelectionModel boundary = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 403 int left_pos = 0; | 406 int left_pos = 0; |
| 404 int right = font.GetStringWidth(text()); | 407 int right = font.GetStringWidth(text()); |
| 405 int right_pos = text().length(); | 408 int right_pos = text().length(); |
| 406 | 409 |
| 407 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); | 410 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); |
| 408 if (x <= left) return SelectionModel(left_pos); | 411 if (x <= left) return SelectionModel(left_pos); |
| 409 if (x >= right) return SelectionModel(right_pos); | 412 if (x >= right) return SelectionModel(right_pos); |
| 410 // binary searching the cursor position. | 413 // binary searching the cursor position. |
| 411 // TODO(oshima): use the center of character instead of edge. | 414 // TODO(oshima): use the center of character instead of edge. |
| 412 // Binary search may not work for language like Arabic. | 415 // Binary search may not work for language like Arabic. |
| 413 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { | 416 while (std::abs(right_pos - left_pos) > 1) { |
| 414 int pivot_pos = left_pos + (right_pos - left_pos) / 2; | 417 int pivot_pos = left_pos + (right_pos - left_pos) / 2; |
| 415 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); | 418 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); |
| 416 if (pivot < x) { | 419 if (pivot < x) { |
| 417 left = pivot; | 420 left = pivot; |
| 418 left_pos = pivot_pos; | 421 left_pos = pivot_pos; |
| 419 } else if (pivot == x) { | 422 } else if (pivot == x) { |
| 420 return SelectionModel(pivot_pos); | 423 return SelectionModel(pivot_pos); |
| 421 } else { | 424 } else { |
| 422 right = pivot; | 425 right = pivot; |
| 423 right_pos = pivot_pos; | 426 right_pos = pivot_pos; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 472 | 475 |
| 473 const Point& RenderText::GetUpdatedDisplayOffset() { | 476 const Point& RenderText::GetUpdatedDisplayOffset() { |
| 474 UpdateCachedBoundsAndOffset(); | 477 UpdateCachedBoundsAndOffset(); |
| 475 return display_offset_; | 478 return display_offset_; |
| 476 } | 479 } |
| 477 | 480 |
| 478 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, | 481 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, |
| 479 BreakType break_type) { | 482 BreakType break_type) { |
| 480 if (break_type == LINE_BREAK) | 483 if (break_type == LINE_BREAK) |
| 481 return LeftEndSelectionModel(); | 484 return LeftEndSelectionModel(); |
| 482 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), | 485 size_t pos = std::max<int>(current.selection_end() - 1, 0); |
| 483 static_cast<long>(0)); | |
| 484 if (break_type == CHARACTER_BREAK) | 486 if (break_type == CHARACTER_BREAK) |
| 485 return SelectionModel(pos, pos, SelectionModel::LEADING); | 487 return SelectionModel(pos, pos, SelectionModel::LEADING); |
| 486 | 488 |
| 487 // Notes: We always iterate words from the beginning. | 489 // Notes: We always iterate words from the beginning. |
| 488 // This is probably fast enough for our usage, but we may | 490 // This is probably fast enough for our usage, but we may |
| 489 // want to modify WordIterator so that it can start from the | 491 // want to modify WordIterator so that it can start from the |
| 490 // middle of string and advance backwards. | 492 // middle of string and advance backwards. |
| 491 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 493 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 492 bool success = iter.Init(); | 494 bool success = iter.Init(); |
| 493 DCHECK(success); | 495 DCHECK(success); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 603 if (base::i18n::IsRTL()) | 605 if (base::i18n::IsRTL()) |
| 604 p.Offset(display_rect().width() - GetStringWidth() - 1, 0); | 606 p.Offset(display_rect().width() - GetStringWidth() - 1, 0); |
| 605 return p; | 607 return p; |
| 606 } | 608 } |
| 607 | 609 |
| 608 void RenderText::SetSelectionModel(const SelectionModel& selection_model) { | 610 void RenderText::SetSelectionModel(const SelectionModel& selection_model) { |
| 609 DCHECK_LE(selection_model.selection_start(), text().length()); | 611 DCHECK_LE(selection_model.selection_start(), text().length()); |
| 610 selection_model_.set_selection_start(selection_model.selection_start()); | 612 selection_model_.set_selection_start(selection_model.selection_start()); |
| 611 DCHECK_LE(selection_model.selection_end(), text().length()); | 613 DCHECK_LE(selection_model.selection_end(), text().length()); |
| 612 selection_model_.set_selection_end(selection_model.selection_end()); | 614 selection_model_.set_selection_end(selection_model.selection_end()); |
| 613 DCHECK_LT(selection_model.caret_pos(), | 615 DCHECK_LT(selection_model.caret_pos(), std::max<size_t>(text().length(), 1)); |
| 614 std::max(text().length(), static_cast<size_t>(1))); | |
| 615 selection_model_.set_caret_pos(selection_model.caret_pos()); | 616 selection_model_.set_caret_pos(selection_model.caret_pos()); |
| 616 selection_model_.set_caret_placement(selection_model.caret_placement()); | 617 selection_model_.set_caret_placement(selection_model.caret_placement()); |
| 617 | 618 |
| 618 cached_bounds_and_offset_valid_ = false; | 619 cached_bounds_and_offset_valid_ = false; |
| 619 UpdateLayout(); | 620 UpdateLayout(); |
| 620 } | 621 } |
| 621 | 622 |
| 622 void RenderText::MoveCursorTo(size_t position, bool select) { | 623 void RenderText::MoveCursorTo(size_t position, bool select) { |
| 623 size_t cursor = std::min(position, text().length()); | 624 size_t cursor = std::min(position, text().length()); |
| 624 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); | 625 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); |
| 625 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? | 626 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? |
| 626 SelectionModel::LEADING : SelectionModel::TRAILING; | 627 SelectionModel::LEADING : SelectionModel::TRAILING; |
| 627 size_t selection_start = select ? GetSelectionStart() : cursor; | 628 size_t selection_start = select ? GetSelectionStart() : cursor; |
| 628 if (IsCursorablePosition(cursor)) { | 629 if (IsCursorablePosition(cursor, false)) { |
| 629 SelectionModel sel(selection_start, cursor, caret_pos, placement); | 630 SelectionModel sel(selection_start, cursor, caret_pos, placement); |
| 630 SetSelectionModel(sel); | 631 SetSelectionModel(sel); |
| 631 } | 632 } |
| 632 } | 633 } |
| 633 | 634 |
| 634 void RenderText::UpdateCachedBoundsAndOffset() { | 635 void RenderText::UpdateCachedBoundsAndOffset() { |
| 635 if (cached_bounds_and_offset_valid_) | 636 if (cached_bounds_and_offset_valid_) |
| 636 return; | 637 return; |
| 637 // First, set the valid flag true to calculate the current cursor bounds using | 638 // First, set the valid flag true to calculate the current cursor bounds using |
| 638 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 639 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 658 // LTR character. | 659 // LTR character. |
| 659 // | 660 // |
| 660 // Pan to show the cursor when it overflows to the left. | 661 // Pan to show the cursor when it overflows to the left. |
| 661 delta_offset = display_rect_.x() - cursor_bounds_.x(); | 662 delta_offset = display_rect_.x() - cursor_bounds_.x(); |
| 662 } | 663 } |
| 663 display_offset_.Offset(delta_offset, 0); | 664 display_offset_.Offset(delta_offset, 0); |
| 664 cursor_bounds_.Offset(delta_offset, 0); | 665 cursor_bounds_.Offset(delta_offset, 0); |
| 665 } | 666 } |
| 666 | 667 |
| 667 } // namespace gfx | 668 } // namespace gfx |
| OLD | NEW |