| 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 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 | 177 |
| 178 void RenderText::SetDisplayRect(const Rect& r) { | 178 void RenderText::SetDisplayRect(const Rect& r) { |
| 179 display_rect_ = r; | 179 display_rect_ = r; |
| 180 cached_bounds_and_offset_valid_ = false; | 180 cached_bounds_and_offset_valid_ = false; |
| 181 } | 181 } |
| 182 | 182 |
| 183 size_t RenderText::GetCursorPosition() const { | 183 size_t RenderText::GetCursorPosition() const { |
| 184 return selection_model_.selection_end(); | 184 return selection_model_.selection_end(); |
| 185 } | 185 } |
| 186 | 186 |
| 187 void RenderText::SetCursorPosition(const size_t position) { | 187 void RenderText::SetCursorPosition(size_t position) { |
| 188 SelectionModel sel(selection_model()); | 188 MoveCursorTo(position, false); |
| 189 sel.set_selection_start(position); | |
| 190 sel.set_selection_end(position); | |
| 191 sel.set_caret_pos(GetIndexOfPreviousGrapheme(position)); | |
| 192 sel.set_caret_placement(SelectionModel::TRAILING); | |
| 193 SetSelectionModel(sel); | |
| 194 } | 189 } |
| 195 | 190 |
| 196 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { | 191 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { |
| 197 SelectionModel position(selection_model()); | 192 SelectionModel position(selection_model()); |
| 198 position.set_selection_start(GetCursorPosition()); | 193 position.set_selection_start(GetCursorPosition()); |
| 199 // Cancelling a selection moves to the edge of the selection. | 194 // Cancelling a selection moves to the edge of the selection. |
| 200 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 195 if (break_type != LINE_BREAK && !EmptySelection() && !select) { |
| 201 // Use the selection start if it is left of the selection end. | 196 // Use the selection start if it is left of the selection end. |
| 202 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 197 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
| 203 SelectionModel::LEADING); | |
| 204 if (GetCursorBounds(selection_start, false).x() < | 198 if (GetCursorBounds(selection_start, false).x() < |
| 205 GetCursorBounds(position, false).x()) | 199 GetCursorBounds(position, false).x()) |
| 206 position = selection_start; | 200 position = selection_start; |
| 207 // For word breaks, use the nearest word boundary left of the selection. | 201 // For word breaks, use the nearest word boundary left of the selection. |
| 208 if (break_type == WORD_BREAK) | 202 if (break_type == WORD_BREAK) |
| 209 position = GetLeftSelectionModel(position, break_type); | 203 position = GetLeftSelectionModel(position, break_type); |
| 210 } else { | 204 } else { |
| 211 position = GetLeftSelectionModel(position, break_type); | 205 position = GetLeftSelectionModel(position, break_type); |
| 212 } | 206 } |
| 213 if (select) | 207 if (select) |
| 214 position.set_selection_start(GetSelectionStart()); | 208 position.set_selection_start(GetSelectionStart()); |
| 215 MoveCursorTo(position); | 209 MoveCursorTo(position); |
| 216 } | 210 } |
| 217 | 211 |
| 218 void RenderText::MoveCursorRight(BreakType break_type, bool select) { | 212 void RenderText::MoveCursorRight(BreakType break_type, bool select) { |
| 219 SelectionModel position(selection_model()); | 213 SelectionModel position(selection_model()); |
| 220 position.set_selection_start(GetCursorPosition()); | 214 position.set_selection_start(GetCursorPosition()); |
| 221 // Cancelling a selection moves to the edge of the selection. | 215 // Cancelling a selection moves to the edge of the selection. |
| 222 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 216 if (break_type != LINE_BREAK && !EmptySelection() && !select) { |
| 223 // Use the selection start if it is right of the selection end. | 217 // Use the selection start if it is right of the selection end. |
| 224 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 218 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
| 225 SelectionModel::LEADING); | |
| 226 if (GetCursorBounds(selection_start, false).x() > | 219 if (GetCursorBounds(selection_start, false).x() > |
| 227 GetCursorBounds(position, false).x()) | 220 GetCursorBounds(position, false).x()) |
| 228 position = selection_start; | 221 position = selection_start; |
| 229 // For word breaks, use the nearest word boundary right of the selection. | 222 // For word breaks, use the nearest word boundary right of the selection. |
| 230 if (break_type == WORD_BREAK) | 223 if (break_type == WORD_BREAK) |
| 231 position = GetRightSelectionModel(position, break_type); | 224 position = GetRightSelectionModel(position, break_type); |
| 232 } else { | 225 } else { |
| 233 position = GetRightSelectionModel(position, break_type); | 226 position = GetRightSelectionModel(position, break_type); |
| 234 } | 227 } |
| 235 if (select) | 228 if (select) |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 } | 335 } |
| 343 | 336 |
| 344 void RenderText::ApplyDefaultStyle() { | 337 void RenderText::ApplyDefaultStyle() { |
| 345 style_ranges_.clear(); | 338 style_ranges_.clear(); |
| 346 StyleRange style = StyleRange(default_style_); | 339 StyleRange style = StyleRange(default_style_); |
| 347 style.range.set_end(text_.length()); | 340 style.range.set_end(text_.length()); |
| 348 style_ranges_.push_back(style); | 341 style_ranges_.push_back(style); |
| 349 cached_bounds_and_offset_valid_ = false; | 342 cached_bounds_and_offset_valid_ = false; |
| 350 } | 343 } |
| 351 | 344 |
| 352 base::i18n::TextDirection RenderText::GetTextDirection() const { | 345 base::i18n::TextDirection RenderText::GetTextDirection() { |
| 353 // TODO(msw): Bidi implementation, intended to replace the functionality added | 346 if (base::i18n::IsRTL()) |
| 354 // in crrev.com/91881 (discussed in codereview.chromium.org/7324011). | 347 return base::i18n::RIGHT_TO_LEFT; |
| 355 return base::i18n::LEFT_TO_RIGHT; | 348 return base::i18n::LEFT_TO_RIGHT; |
| 356 } | 349 } |
| 357 | 350 |
| 358 int RenderText::GetStringWidth() { | 351 int RenderText::GetStringWidth() { |
| 359 return default_style_.font.GetStringWidth(text()); | 352 return default_style_.font.GetStringWidth(text()); |
| 360 } | 353 } |
| 361 | 354 |
| 362 void RenderText::Draw(Canvas* canvas) { | 355 void RenderText::Draw(Canvas* canvas) { |
| 363 // Clip the canvas to the text display area. | 356 // Clip the canvas to the text display area. |
| 364 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), | 357 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 419 int left = 0; | 412 int left = 0; |
| 420 int left_pos = 0; | 413 int left_pos = 0; |
| 421 int right = font.GetStringWidth(text()); | 414 int right = font.GetStringWidth(text()); |
| 422 int right_pos = text().length(); | 415 int right_pos = text().length(); |
| 423 | 416 |
| 424 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); | 417 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); |
| 425 if (x <= left) return SelectionModel(left_pos); | 418 if (x <= left) return SelectionModel(left_pos); |
| 426 if (x >= right) return SelectionModel(right_pos); | 419 if (x >= right) return SelectionModel(right_pos); |
| 427 // binary searching the cursor position. | 420 // binary searching the cursor position. |
| 428 // TODO(oshima): use the center of character instead of edge. | 421 // TODO(oshima): use the center of character instead of edge. |
| 429 // Binary search may not work for language like arabic. | 422 // Binary search may not work for language like Arabic. |
| 430 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { | 423 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { |
| 431 int pivot_pos = left_pos + (right_pos - left_pos) / 2; | 424 int pivot_pos = left_pos + (right_pos - left_pos) / 2; |
| 432 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); | 425 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); |
| 433 if (pivot < x) { | 426 if (pivot < x) { |
| 434 left = pivot; | 427 left = pivot; |
| 435 left_pos = pivot_pos; | 428 left_pos = pivot_pos; |
| 436 } else if (pivot == x) { | 429 } else if (pivot == x) { |
| 437 return SelectionModel(pivot_pos); | 430 return SelectionModel(pivot_pos); |
| 438 } else { | 431 } else { |
| 439 right = pivot; | 432 right = pivot; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 490 | 483 |
| 491 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, | 484 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, |
| 492 BreakType break_type) { | 485 BreakType break_type) { |
| 493 if (break_type == LINE_BREAK) | 486 if (break_type == LINE_BREAK) |
| 494 return SelectionModel(0, 0, SelectionModel::LEADING); | 487 return SelectionModel(0, 0, SelectionModel::LEADING); |
| 495 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), |
| 496 static_cast<long>(0)); | 489 static_cast<long>(0)); |
| 497 if (break_type == CHARACTER_BREAK) | 490 if (break_type == CHARACTER_BREAK) |
| 498 return SelectionModel(pos, pos, SelectionModel::LEADING); | 491 return SelectionModel(pos, pos, SelectionModel::LEADING); |
| 499 | 492 |
| 500 // Notes: We always iterate words from the begining. | 493 // Notes: We always iterate words from the beginning. |
| 501 // This is probably fast enough for our usage, but we may | 494 // This is probably fast enough for our usage, but we may |
| 502 // want to modify WordIterator so that it can start from the | 495 // want to modify WordIterator so that it can start from the |
| 503 // middle of string and advance backwards. | 496 // middle of string and advance backwards. |
| 504 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 497 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 505 bool success = iter.Init(); | 498 bool success = iter.Init(); |
| 506 DCHECK(success); | 499 DCHECK(success); |
| 507 if (!success) | 500 if (!success) |
| 508 return current; | 501 return current; |
| 509 while (iter.Advance()) { | 502 while (iter.Advance()) { |
| 510 if (iter.IsWord()) { | 503 if (iter.IsWord()) { |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 // Apply a selection style override to a copy of the style ranges. | 562 // Apply a selection style override to a copy of the style ranges. |
| 570 if (!EmptySelection()) { | 563 if (!EmptySelection()) { |
| 571 StyleRange selection_style(default_style_); | 564 StyleRange selection_style(default_style_); |
| 572 selection_style.foreground = kSelectedTextColor; | 565 selection_style.foreground = kSelectedTextColor; |
| 573 selection_style.range.set_start(MinOfSelection()); | 566 selection_style.range.set_start(MinOfSelection()); |
| 574 selection_style.range.set_end(MaxOfSelection()); | 567 selection_style.range.set_end(MaxOfSelection()); |
| 575 ApplyStyleRangeImpl(style_ranges, selection_style); | 568 ApplyStyleRangeImpl(style_ranges, selection_style); |
| 576 } | 569 } |
| 577 } | 570 } |
| 578 | 571 |
| 572 Point RenderText::ToTextPoint(const Point& point) { |
| 573 Point p(point.Subtract(display_rect_.origin())); |
| 574 p = p.Subtract(GetUpdatedDisplayOffset()); |
| 575 if (base::i18n::IsRTL()) |
| 576 p.Offset(GetStringWidth() - display_rect_.width() + 1, 0); |
| 577 return p; |
| 578 } |
| 579 |
| 580 Point RenderText::ToViewPoint(const Point& point) { |
| 581 Point p(point.Add(display_rect_.origin())); |
| 582 p = p.Add(GetUpdatedDisplayOffset()); |
| 583 if (base::i18n::IsRTL()) |
| 584 p.Offset(display_rect_.width() - GetStringWidth() - 1, 0); |
| 585 return p; |
| 586 } |
| 587 |
| 588 void RenderText::MoveCursorTo(size_t position, bool select) { |
| 589 size_t caret_pos = GetIndexOfPreviousGrapheme(position); |
| 590 SelectionModel::CaretPlacement placement = (caret_pos == position) ? |
| 591 SelectionModel::LEADING : SelectionModel::TRAILING; |
| 592 size_t selection_start = select ? GetSelectionStart() : position; |
| 593 SelectionModel sel(selection_start, position, caret_pos, placement); |
| 594 SetSelectionModel(sel); |
| 595 } |
| 596 |
| 579 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { | 597 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { |
| 580 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || | 598 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || |
| 581 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); | 599 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); |
| 582 } | 600 } |
| 583 | 601 |
| 584 void RenderText::UpdateCachedBoundsAndOffset() { | 602 void RenderText::UpdateCachedBoundsAndOffset() { |
| 585 if (cached_bounds_and_offset_valid_) | 603 if (cached_bounds_and_offset_valid_) |
| 586 return; | 604 return; |
| 587 // First, set the valid flag true to calculate the current cursor bounds using | 605 // First, set the valid flag true to calculate the current cursor bounds using |
| 588 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 606 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| 589 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 607 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
| 590 cached_bounds_and_offset_valid_ = true; | 608 cached_bounds_and_offset_valid_ = true; |
| 591 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 609 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
| 592 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1)); | 610 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1)); |
| 593 // Update |display_offset_| to ensure the current cursor is visible. | 611 // Update |display_offset_| to ensure the current cursor is visible. |
| 594 int display_width = display_rect_.width(); | 612 int display_width = display_rect_.width(); |
| 595 int string_width = GetStringWidth(); | 613 int string_width = GetStringWidth(); |
| 596 int delta_offset = 0; | 614 int delta_offset = 0; |
| 597 if (string_width < display_width) { | 615 if (string_width < display_width) { |
| 598 // Show all text whenever the text fits to the size. | 616 // Show all text whenever the text fits to the size. |
| 599 delta_offset = -display_offset_.x(); | 617 delta_offset = -display_offset_.x(); |
| 600 } else if (cursor_bounds_.right() > display_rect_.right()) { | 618 } else if (cursor_bounds_.right() > display_rect_.right()) { |
| 619 // TODO(xji): when the character overflow is a RTL character, currently, if |
| 620 // we pan cursor at the rightmost position, the entered RTL character is not |
| 621 // displayed. Should pan cursor to show the last logical characters. |
| 622 // |
| 601 // Pan to show the cursor when it overflows to the right, | 623 // Pan to show the cursor when it overflows to the right, |
| 602 delta_offset = display_rect_.right() - cursor_bounds_.right(); | 624 delta_offset = display_rect_.right() - cursor_bounds_.right(); |
| 603 } else if (cursor_bounds_.x() < display_rect_.x()) { | 625 } else if (cursor_bounds_.x() < display_rect_.x()) { |
| 626 // TODO(xji): have similar problem as above when overflow character is a |
| 627 // LTR character. |
| 628 // |
| 604 // Pan to show the cursor when it overflows to the left. | 629 // Pan to show the cursor when it overflows to the left. |
| 605 delta_offset = display_rect_.x() - cursor_bounds_.x(); | 630 delta_offset = display_rect_.x() - cursor_bounds_.x(); |
| 606 } | 631 } |
| 607 display_offset_.Offset(delta_offset, 0); | 632 display_offset_.Offset(delta_offset, 0); |
| 608 cursor_bounds_.Offset(delta_offset, 0); | 633 cursor_bounds_.Offset(delta_offset, 0); |
| 609 } | 634 } |
| 610 | 635 |
| 636 SelectionModel RenderText::GetSelectionModelForSelectionStart() { |
| 637 if (GetSelectionStart() < GetCursorPosition()) |
| 638 return SelectionModel(GetSelectionStart(), |
| 639 GetSelectionStart(), |
| 640 SelectionModel::LEADING); |
| 641 return SelectionModel(GetSelectionStart(), |
| 642 GetIndexOfPreviousGrapheme(GetSelectionStart()), |
| 643 SelectionModel::TRAILING); |
| 644 } |
| 645 |
| 611 } // namespace gfx | 646 } // namespace gfx |
| OLD | NEW |