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 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 selection_start_ = start; | 128 selection_start_ = start; |
129 selection_end_ = end; | 129 selection_end_ = end; |
130 caret_pos_ = pos; | 130 caret_pos_ = pos; |
131 caret_placement_ = placement; | 131 caret_placement_ = placement; |
132 } | 132 } |
133 | 133 |
134 RenderText::~RenderText() { | 134 RenderText::~RenderText() { |
135 } | 135 } |
136 | 136 |
137 void RenderText::SetText(const string16& text) { | 137 void RenderText::SetText(const string16& text) { |
| 138 DCHECK(!composition_range_.IsValid()); |
138 size_t old_text_length = text_.length(); | 139 size_t old_text_length = text_.length(); |
139 text_ = text; | 140 text_ = text; |
140 | 141 |
141 // Update the style ranges as needed. | 142 // Update the style ranges as needed. |
142 if (text_.empty()) { | 143 if (text_.empty()) { |
143 style_ranges_.clear(); | 144 style_ranges_.clear(); |
144 } else if (style_ranges_.empty()) { | 145 } else if (style_ranges_.empty()) { |
145 ApplyDefaultStyle(); | 146 ApplyDefaultStyle(); |
146 } else if (text_.length() > old_text_length) { | 147 } else if (text_.length() > old_text_length) { |
147 style_ranges_.back().range.set_end(text_.length()); | 148 style_ranges_.back().range.set_end(text_.length()); |
148 } else if (text_.length() < old_text_length) { | 149 } else if (text_.length() < old_text_length) { |
149 StyleRanges::iterator i; | 150 StyleRanges::iterator i; |
150 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) { | 151 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) { |
151 if (i->range.start() >= text_.length()) { | 152 if (i->range.start() >= text_.length()) { |
152 i = style_ranges_.erase(i); | 153 i = style_ranges_.erase(i); |
153 if (i == style_ranges_.end()) | 154 if (i == style_ranges_.end()) |
154 break; | 155 break; |
155 } else if (i->range.end() > text_.length()) { | 156 } else if (i->range.end() > text_.length()) { |
156 i->range.set_end(text_.length()); | 157 i->range.set_end(text_.length()); |
157 } | 158 } |
158 } | 159 } |
159 style_ranges_.back().range.set_end(text_.length()); | 160 style_ranges_.back().range.set_end(text_.length()); |
160 } | 161 } |
161 #ifndef NDEBUG | 162 #ifndef NDEBUG |
162 CheckStyleRanges(style_ranges_, text_.length()); | 163 CheckStyleRanges(style_ranges_, text_.length()); |
163 #endif | 164 #endif |
164 cached_bounds_and_offset_valid_ = false; | 165 cached_bounds_and_offset_valid_ = false; |
| 166 |
| 167 // Reset selection model. SetText should always followed by SetSelectionModel |
| 168 // or SetCursorPosition in upper layer. |
| 169 SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING)); |
165 } | 170 } |
166 | 171 |
167 void RenderText::ToggleInsertMode() { | 172 void RenderText::ToggleInsertMode() { |
168 insert_mode_ = !insert_mode_; | 173 insert_mode_ = !insert_mode_; |
169 cached_bounds_and_offset_valid_ = false; | 174 cached_bounds_and_offset_valid_ = false; |
170 } | 175 } |
171 | 176 |
172 void RenderText::SetDisplayRect(const Rect& r) { | 177 void RenderText::SetDisplayRect(const Rect& r) { |
173 display_rect_ = r; | 178 display_rect_ = r; |
174 cached_bounds_and_offset_valid_ = false; | 179 cached_bounds_and_offset_valid_ = false; |
175 } | 180 } |
176 | 181 |
177 size_t RenderText::GetCursorPosition() const { | 182 size_t RenderText::GetCursorPosition() const { |
178 return selection_model_.selection_end(); | 183 return selection_model_.selection_end(); |
179 } | 184 } |
180 | 185 |
181 void RenderText::SetCursorPosition(size_t position) { | 186 void RenderText::SetCursorPosition(size_t position) { |
182 MoveCursorTo(position, false); | 187 MoveCursorTo(position, false); |
183 } | 188 } |
184 | 189 |
185 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { | 190 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { |
186 SelectionModel position(selection_model()); | 191 SelectionModel position(selection_model()); |
187 position.set_selection_start(GetCursorPosition()); | 192 position.set_selection_start(GetCursorPosition()); |
188 // Cancelling a selection moves to the edge of the selection. | 193 // Cancelling a selection moves to the edge of the selection. |
189 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 194 if (break_type != LINE_BREAK && !EmptySelection() && !select) { |
190 // Use the selection start if it is left of the selection end. | 195 // Use the selection start if it is left of the selection end. |
191 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 196 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
192 SelectionModel::LEADING); | 197 if (GetCursorBounds(selection_start, true).x() < |
193 if (GetCursorBounds(selection_start, false).x() < | 198 GetCursorBounds(position, true).x()) |
194 GetCursorBounds(position, false).x()) | |
195 position = selection_start; | 199 position = selection_start; |
196 // For word breaks, use the nearest word boundary left of the selection. | 200 // For word breaks, use the nearest word boundary left of the selection. |
197 if (break_type == WORD_BREAK) | 201 if (break_type == WORD_BREAK) |
198 position = GetLeftSelectionModel(position, break_type); | 202 position = GetLeftSelectionModel(position, break_type); |
199 } else { | 203 } else { |
200 position = GetLeftSelectionModel(position, break_type); | 204 position = GetLeftSelectionModel(position, break_type); |
201 } | 205 } |
202 if (select) | 206 if (select) |
203 position.set_selection_start(GetSelectionStart()); | 207 position.set_selection_start(GetSelectionStart()); |
204 MoveCursorTo(position); | 208 MoveCursorTo(position); |
205 } | 209 } |
206 | 210 |
207 void RenderText::MoveCursorRight(BreakType break_type, bool select) { | 211 void RenderText::MoveCursorRight(BreakType break_type, bool select) { |
208 SelectionModel position(selection_model()); | 212 SelectionModel position(selection_model()); |
209 position.set_selection_start(GetCursorPosition()); | 213 position.set_selection_start(GetCursorPosition()); |
210 // Cancelling a selection moves to the edge of the selection. | 214 // Cancelling a selection moves to the edge of the selection. |
211 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 215 if (break_type != LINE_BREAK && !EmptySelection() && !select) { |
212 // Use the selection start if it is right of the selection end. | 216 // Use the selection start if it is right of the selection end. |
213 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 217 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
214 SelectionModel::LEADING); | 218 if (GetCursorBounds(selection_start, true).x() > |
215 if (GetCursorBounds(selection_start, false).x() > | 219 GetCursorBounds(position, true).x()) |
216 GetCursorBounds(position, false).x()) | |
217 position = selection_start; | 220 position = selection_start; |
218 // For word breaks, use the nearest word boundary right of the selection. | 221 // For word breaks, use the nearest word boundary right of the selection. |
219 if (break_type == WORD_BREAK) | 222 if (break_type == WORD_BREAK) |
220 position = GetRightSelectionModel(position, break_type); | 223 position = GetRightSelectionModel(position, break_type); |
221 } else { | 224 } else { |
222 position = GetRightSelectionModel(position, break_type); | 225 position = GetRightSelectionModel(position, break_type); |
223 } | 226 } |
224 if (select) | 227 if (select) |
225 position.set_selection_start(GetSelectionStart()); | 228 position.set_selection_start(GetSelectionStart()); |
226 MoveCursorTo(position); | 229 MoveCursorTo(position); |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
346 } | 349 } |
347 | 350 |
348 void RenderText::ApplyDefaultStyle() { | 351 void RenderText::ApplyDefaultStyle() { |
349 style_ranges_.clear(); | 352 style_ranges_.clear(); |
350 StyleRange style = StyleRange(default_style_); | 353 StyleRange style = StyleRange(default_style_); |
351 style.range.set_end(text_.length()); | 354 style.range.set_end(text_.length()); |
352 style_ranges_.push_back(style); | 355 style_ranges_.push_back(style); |
353 cached_bounds_and_offset_valid_ = false; | 356 cached_bounds_and_offset_valid_ = false; |
354 } | 357 } |
355 | 358 |
356 base::i18n::TextDirection RenderText::GetTextDirection() const { | 359 base::i18n::TextDirection RenderText::GetTextDirection() { |
357 if (base::i18n::IsRTL()) | 360 if (base::i18n::IsRTL()) |
358 return base::i18n::RIGHT_TO_LEFT; | 361 return base::i18n::RIGHT_TO_LEFT; |
359 return base::i18n::LEFT_TO_RIGHT; | 362 return base::i18n::LEFT_TO_RIGHT; |
360 } | 363 } |
361 | 364 |
362 int RenderText::GetStringWidth() { | 365 int RenderText::GetStringWidth() { |
363 return default_style_.font.GetStringWidth(text()); | 366 return default_style_.font.GetStringWidth(text()); |
364 } | 367 } |
365 | 368 |
366 void RenderText::Draw(Canvas* canvas) { | 369 void RenderText::Draw(Canvas* canvas) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
423 int left = 0; | 426 int left = 0; |
424 int left_pos = 0; | 427 int left_pos = 0; |
425 int right = font.GetStringWidth(text()); | 428 int right = font.GetStringWidth(text()); |
426 int right_pos = text().length(); | 429 int right_pos = text().length(); |
427 | 430 |
428 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); | 431 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); |
429 if (x <= left) return SelectionModel(left_pos); | 432 if (x <= left) return SelectionModel(left_pos); |
430 if (x >= right) return SelectionModel(right_pos); | 433 if (x >= right) return SelectionModel(right_pos); |
431 // binary searching the cursor position. | 434 // binary searching the cursor position. |
432 // TODO(oshima): use the center of character instead of edge. | 435 // TODO(oshima): use the center of character instead of edge. |
433 // Binary search may not work for language like arabic. | 436 // Binary search may not work for language like Arabic. |
434 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { | 437 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { |
435 int pivot_pos = left_pos + (right_pos - left_pos) / 2; | 438 int pivot_pos = left_pos + (right_pos - left_pos) / 2; |
436 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); | 439 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); |
437 if (pivot < x) { | 440 if (pivot < x) { |
438 left = pivot; | 441 left = pivot; |
439 left_pos = pivot_pos; | 442 left_pos = pivot_pos; |
440 } else if (pivot == x) { | 443 } else if (pivot == x) { |
441 return SelectionModel(pivot_pos); | 444 return SelectionModel(pivot_pos); |
442 } else { | 445 } else { |
443 right = pivot; | 446 right = pivot; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
480 | 483 |
481 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, | 484 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, |
482 BreakType break_type) { | 485 BreakType break_type) { |
483 if (break_type == LINE_BREAK) | 486 if (break_type == LINE_BREAK) |
484 return SelectionModel(0, 0, SelectionModel::LEADING); | 487 return SelectionModel(0, 0, SelectionModel::LEADING); |
485 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), | 488 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), |
486 static_cast<long>(0)); | 489 static_cast<long>(0)); |
487 if (break_type == CHARACTER_BREAK) | 490 if (break_type == CHARACTER_BREAK) |
488 return SelectionModel(pos, pos, SelectionModel::LEADING); | 491 return SelectionModel(pos, pos, SelectionModel::LEADING); |
489 | 492 |
490 // Notes: We always iterate words from the begining. | 493 // Notes: We always iterate words from the beginning. |
491 // This is probably fast enough for our usage, but we may | 494 // This is probably fast enough for our usage, but we may |
492 // want to modify WordIterator so that it can start from the | 495 // want to modify WordIterator so that it can start from the |
493 // middle of string and advance backwards. | 496 // middle of string and advance backwards. |
494 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 497 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
495 bool success = iter.Init(); | 498 bool success = iter.Init(); |
496 DCHECK(success); | 499 DCHECK(success); |
497 if (!success) | 500 if (!success) |
498 return current; | 501 return current; |
499 while (iter.Advance()) { | 502 while (iter.Advance()) { |
500 if (iter.IsWord()) { | 503 if (iter.IsWord()) { |
(...skipping 11 matching lines...) Expand all Loading... |
512 pos = iter.pos() - iter.GetString().length(); | 515 pos = iter.pos() - iter.GetString().length(); |
513 } | 516 } |
514 } | 517 } |
515 } | 518 } |
516 | 519 |
517 return SelectionModel(pos, pos, SelectionModel::LEADING); | 520 return SelectionModel(pos, pos, SelectionModel::LEADING); |
518 } | 521 } |
519 | 522 |
520 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current, | 523 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current, |
521 BreakType break_type) { | 524 BreakType break_type) { |
| 525 if (text_.empty()) |
| 526 return SelectionModel(0, 0, SelectionModel::LEADING); |
522 if (break_type == LINE_BREAK) | 527 if (break_type == LINE_BREAK) |
523 return SelectionModel(text().length(), | 528 return SelectionModel(text().length(), |
524 GetIndexOfPreviousGrapheme(text().length()), SelectionModel::TRAILING); | 529 GetIndexOfPreviousGrapheme(text().length()), SelectionModel::TRAILING); |
525 size_t pos = std::min(current.selection_end() + 1, text().length()); | 530 size_t pos = std::min(current.selection_end() + 1, text().length()); |
526 if (break_type == CHARACTER_BREAK) | 531 if (break_type == CHARACTER_BREAK) |
527 return SelectionModel(pos, pos, SelectionModel::LEADING); | 532 return SelectionModel(pos, pos, SelectionModel::LEADING); |
528 | 533 |
529 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 534 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
530 bool success = iter.Init(); | 535 bool success = iter.Init(); |
531 DCHECK(success); | 536 DCHECK(success); |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 cached_bounds_and_offset_valid_ = true; | 650 cached_bounds_and_offset_valid_ = true; |
646 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 651 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
647 // Update |display_offset_| to ensure the current cursor is visible. | 652 // Update |display_offset_| to ensure the current cursor is visible. |
648 int display_width = display_rect_.width(); | 653 int display_width = display_rect_.width(); |
649 int string_width = GetStringWidth(); | 654 int string_width = GetStringWidth(); |
650 int delta_offset = 0; | 655 int delta_offset = 0; |
651 if (string_width < display_width) { | 656 if (string_width < display_width) { |
652 // Show all text whenever the text fits to the size. | 657 // Show all text whenever the text fits to the size. |
653 delta_offset = -display_offset_.x(); | 658 delta_offset = -display_offset_.x(); |
654 } else if (cursor_bounds_.right() > display_rect_.right()) { | 659 } else if (cursor_bounds_.right() > display_rect_.right()) { |
| 660 // TODO(xji): when the character overflow is a RTL character, currently, if |
| 661 // we pan cursor at the rightmost position, the entered RTL character is not |
| 662 // displayed. Should pan cursor to show the last logical characters. |
| 663 // |
655 // Pan to show the cursor when it overflows to the right, | 664 // Pan to show the cursor when it overflows to the right, |
656 delta_offset = display_rect_.right() - cursor_bounds_.right(); | 665 delta_offset = display_rect_.right() - cursor_bounds_.right(); |
657 } else if (cursor_bounds_.x() < display_rect_.x()) { | 666 } else if (cursor_bounds_.x() < display_rect_.x()) { |
| 667 // TODO(xji): have similar problem as above when overflow character is a |
| 668 // LTR character. |
| 669 // |
658 // Pan to show the cursor when it overflows to the left. | 670 // Pan to show the cursor when it overflows to the left. |
659 delta_offset = display_rect_.x() - cursor_bounds_.x(); | 671 delta_offset = display_rect_.x() - cursor_bounds_.x(); |
660 } | 672 } |
661 display_offset_.Offset(delta_offset, 0); | 673 display_offset_.Offset(delta_offset, 0); |
662 cursor_bounds_.Offset(delta_offset, 0); | 674 cursor_bounds_.Offset(delta_offset, 0); |
663 } | 675 } |
664 | 676 |
| 677 SelectionModel RenderText::GetSelectionModelForSelectionStart() { |
| 678 size_t selection_start = GetSelectionStart(); |
| 679 size_t selection_end = GetCursorPosition(); |
| 680 if (selection_start < selection_end) |
| 681 return SelectionModel(selection_start, |
| 682 selection_start, |
| 683 SelectionModel::LEADING); |
| 684 else if (selection_start > selection_end) |
| 685 return SelectionModel(selection_start, |
| 686 GetIndexOfPreviousGrapheme(selection_start), |
| 687 SelectionModel::TRAILING); |
| 688 return selection_model_; |
| 689 } |
| 690 |
665 } // namespace gfx | 691 } // namespace gfx |
OLD | NEW |