| 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 14 matching lines...) Expand all Loading... |
| 458 UpdateCachedBoundsAndOffset(); | 461 UpdateCachedBoundsAndOffset(); |
| 459 return cursor_bounds_; | 462 return cursor_bounds_; |
| 460 } | 463 } |
| 461 | 464 |
| 462 RenderText::RenderText() | 465 RenderText::RenderText() |
| 463 : text_(), | 466 : text_(), |
| 464 selection_model_(), | 467 selection_model_(), |
| 465 cursor_bounds_(), | 468 cursor_bounds_(), |
| 466 cursor_visible_(false), | 469 cursor_visible_(false), |
| 467 insert_mode_(true), | 470 insert_mode_(true), |
| 468 composition_range_(), | 471 composition_range_(ui::Range::InvalidRange()), |
| 469 style_ranges_(), | 472 style_ranges_(), |
| 470 default_style_(), | 473 default_style_(), |
| 471 display_rect_(), | 474 display_rect_(), |
| 472 display_offset_(), | 475 display_offset_(), |
| 473 cached_bounds_and_offset_valid_(false) { | 476 cached_bounds_and_offset_valid_(false) { |
| 474 } | 477 } |
| 475 | 478 |
| 476 const Point& RenderText::GetUpdatedDisplayOffset() { | 479 const Point& RenderText::GetUpdatedDisplayOffset() { |
| 477 UpdateCachedBoundsAndOffset(); | 480 UpdateCachedBoundsAndOffset(); |
| 478 return display_offset_; | 481 return display_offset_; |
| 479 } | 482 } |
| 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 |