| 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 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 // The current model only supports caret positions at valid character indices. | 240 // The current model only supports caret positions at valid character indices. |
| 241 if (text_length == 0) { | 241 if (text_length == 0) { |
| 242 sel.set_caret_pos(0); | 242 sel.set_caret_pos(0); |
| 243 sel.set_caret_placement(SelectionModel::LEADING); | 243 sel.set_caret_placement(SelectionModel::LEADING); |
| 244 } else if (sel.caret_pos() >= text_length) { | 244 } else if (sel.caret_pos() >= text_length) { |
| 245 SelectionModel end = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? | 245 SelectionModel end = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? |
| 246 LeftEndSelectionModel() : RightEndSelectionModel(); | 246 LeftEndSelectionModel() : RightEndSelectionModel(); |
| 247 sel.set_caret_pos(end.caret_pos()); | 247 sel.set_caret_pos(end.caret_pos()); |
| 248 sel.set_caret_placement(end.caret_placement()); | 248 sel.set_caret_placement(end.caret_placement()); |
| 249 } | 249 } |
| 250 |
| 251 if (!IsCursorablePosition(sel.selection_start()) || |
| 252 !IsCursorablePosition(sel.selection_end()) || |
| 253 !IsCursorablePosition(sel.caret_pos())) |
| 254 return false; |
| 255 |
| 250 bool changed = !sel.Equals(selection_model_); | 256 bool changed = !sel.Equals(selection_model_); |
| 251 SetSelectionModel(sel); | 257 SetSelectionModel(sel); |
| 252 return changed; | 258 return changed; |
| 253 } | 259 } |
| 254 | 260 |
| 255 bool RenderText::MoveCursorTo(const Point& point, bool select) { | 261 bool RenderText::MoveCursorTo(const Point& point, bool select) { |
| 256 SelectionModel selection = FindCursorPosition(point); | 262 SelectionModel selection = FindCursorPosition(point); |
| 257 if (select) | 263 if (select) |
| 258 selection.set_selection_start(GetSelectionStart()); | 264 selection.set_selection_start(GetSelectionStart()); |
| 259 return MoveCursorTo(selection); | 265 return MoveCursorTo(selection); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 274 sel.set_selection_start(GetCursorPosition()); | 280 sel.set_selection_start(GetCursorPosition()); |
| 275 SetSelectionModel(sel); | 281 SetSelectionModel(sel); |
| 276 } | 282 } |
| 277 | 283 |
| 278 void RenderText::SelectAll() { | 284 void RenderText::SelectAll() { |
| 279 SelectionModel sel(RightEndSelectionModel()); | 285 SelectionModel sel(RightEndSelectionModel()); |
| 280 sel.set_selection_start(LeftEndSelectionModel().selection_start()); | 286 sel.set_selection_start(LeftEndSelectionModel().selection_start()); |
| 281 SetSelectionModel(sel); | 287 SetSelectionModel(sel); |
| 282 } | 288 } |
| 283 | 289 |
| 284 // TODO(xji): it does not work for languages do not use space as word breaker, | |
| 285 // such as Chinese. Should use BreakIterator. | |
| 286 void RenderText::SelectWord() { | 290 void RenderText::SelectWord() { |
| 287 size_t selection_start = GetSelectionStart(); | |
| 288 size_t cursor_position = GetCursorPosition(); | 291 size_t cursor_position = GetCursorPosition(); |
| 289 // First we setup selection_start_ and selection_end_. There are so many cases | |
| 290 // because we try to emulate what select-word looks like in a gtk textfield. | |
| 291 // See associated testcase for different cases. | |
| 292 if (cursor_position > 0 && cursor_position < text().length()) { | |
| 293 if (u_isalnum(text()[cursor_position])) { | |
| 294 selection_start = cursor_position; | |
| 295 cursor_position++; | |
| 296 } else | |
| 297 selection_start = cursor_position - 1; | |
| 298 } else if (cursor_position == 0) { | |
| 299 selection_start = cursor_position; | |
| 300 if (text().length() > 0) | |
| 301 cursor_position++; | |
| 302 } else { | |
| 303 selection_start = cursor_position - 1; | |
| 304 } | |
| 305 | 292 |
| 306 // Now we move selection_start_ to beginning of selection. Selection boundary | 293 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 307 // is defined as the position where we have alpha-num character on one side | 294 bool success = iter.Init(); |
| 308 // and non-alpha-num char on the other side. | 295 DCHECK(success); |
| 309 for (; selection_start > 0; selection_start--) { | 296 if (!success) |
| 310 if (IsPositionAtWordSelectionBoundary(selection_start)) | 297 return; |
| 298 |
| 299 size_t selection_start = cursor_position; |
| 300 for (; selection_start != 0; --selection_start) { |
| 301 if (iter.IsStartOfWord(selection_start) || |
| 302 iter.IsEndOfWord(selection_start)) |
| 311 break; | 303 break; |
| 312 } | 304 } |
| 313 | 305 |
| 314 // Now we move selection_end_ to end of selection. Selection boundary | 306 if (selection_start == cursor_position) |
| 315 // is defined as the position where we have alpha-num character on one side | 307 ++cursor_position; |
| 316 // and non-alpha-num char on the other side. | 308 |
| 317 for (; cursor_position < text().length(); cursor_position++) { | 309 for (; cursor_position < text().length(); ++cursor_position) { |
| 318 if (IsPositionAtWordSelectionBoundary(cursor_position)) | 310 if (iter.IsEndOfWord(cursor_position) || |
| 311 iter.IsStartOfWord(cursor_position)) |
| 319 break; | 312 break; |
| 320 } | 313 } |
| 321 | 314 |
| 322 MoveCursorTo(selection_start, false); | 315 MoveCursorTo(selection_start, false); |
| 323 MoveCursorTo(cursor_position, true); | 316 MoveCursorTo(cursor_position, true); |
| 324 } | 317 } |
| 325 | 318 |
| 326 const ui::Range& RenderText::GetCompositionRange() const { | 319 const ui::Range& RenderText::GetCompositionRange() const { |
| 327 return composition_range_; | 320 return composition_range_; |
| 328 } | 321 } |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 455 size_t from = selection.selection_end(); | 448 size_t from = selection.selection_end(); |
| 456 size_t to = insert_mode ? from : std::min(text_.length(), from + 1); | 449 size_t to = insert_mode ? from : std::min(text_.length(), from + 1); |
| 457 return GetSubstringBounds(from, to)[0]; | 450 return GetSubstringBounds(from, to)[0]; |
| 458 } | 451 } |
| 459 | 452 |
| 460 const Rect& RenderText::GetUpdatedCursorBounds() { | 453 const Rect& RenderText::GetUpdatedCursorBounds() { |
| 461 UpdateCachedBoundsAndOffset(); | 454 UpdateCachedBoundsAndOffset(); |
| 462 return cursor_bounds_; | 455 return cursor_bounds_; |
| 463 } | 456 } |
| 464 | 457 |
| 458 size_t RenderText::GetIndexOfNextGrapheme(size_t position) { |
| 459 return IndexOfAdjacentGrapheme(position, true); |
| 460 } |
| 461 |
| 465 RenderText::RenderText() | 462 RenderText::RenderText() |
| 466 : text_(), | 463 : text_(), |
| 467 selection_model_(), | 464 selection_model_(), |
| 468 cursor_bounds_(), | 465 cursor_bounds_(), |
| 469 cursor_visible_(false), | 466 cursor_visible_(false), |
| 470 insert_mode_(true), | 467 insert_mode_(true), |
| 471 composition_range_(ui::Range::InvalidRange()), | 468 composition_range_(ui::Range::InvalidRange()), |
| 472 style_ranges_(), | 469 style_ranges_(), |
| 473 default_style_(), | 470 default_style_(), |
| 474 display_rect_(), | 471 display_rect_(), |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 550 | 547 |
| 551 SelectionModel RenderText::RightEndSelectionModel() { | 548 SelectionModel RenderText::RightEndSelectionModel() { |
| 552 size_t cursor = text().length(); | 549 size_t cursor = text().length(); |
| 553 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); | 550 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); |
| 554 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? | 551 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? |
| 555 SelectionModel::LEADING : SelectionModel::TRAILING; | 552 SelectionModel::LEADING : SelectionModel::TRAILING; |
| 556 return SelectionModel(cursor, caret_pos, placement); | 553 return SelectionModel(cursor, caret_pos, placement); |
| 557 } | 554 } |
| 558 | 555 |
| 559 size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) { | 556 size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) { |
| 560 // TODO(msw): Handle complex script. | 557 return IndexOfAdjacentGrapheme(position, false); |
| 561 return std::max(static_cast<long>(position - 1), static_cast<long>(0)); | |
| 562 } | 558 } |
| 563 | 559 |
| 564 std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) { | 560 std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) { |
| 565 size_t start = std::min(from, to); | 561 size_t start = std::min(from, to); |
| 566 size_t end = std::max(from, to); | 562 size_t end = std::max(from, to); |
| 567 const Font& font = default_style_.font; | 563 const Font& font = default_style_.font; |
| 568 int start_x = font.GetStringWidth(text().substr(0, start)); | 564 int start_x = font.GetStringWidth(text().substr(0, start)); |
| 569 int end_x = font.GetStringWidth(text().substr(0, end)); | 565 int end_x = font.GetStringWidth(text().substr(0, end)); |
| 570 Rect rect(start_x, 0, end_x - start_x, font.GetHeight()); | 566 Rect rect(start_x, 0, end_x - start_x, font.GetHeight()); |
| 571 rect.Offset(display_rect_.origin()); | 567 rect.Offset(display_rect_.origin()); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 625 | 621 |
| 626 cached_bounds_and_offset_valid_ = false; | 622 cached_bounds_and_offset_valid_ = false; |
| 627 } | 623 } |
| 628 | 624 |
| 629 void RenderText::MoveCursorTo(size_t position, bool select) { | 625 void RenderText::MoveCursorTo(size_t position, bool select) { |
| 630 size_t cursor = std::min(position, text().length()); | 626 size_t cursor = std::min(position, text().length()); |
| 631 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); | 627 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); |
| 632 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? | 628 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? |
| 633 SelectionModel::LEADING : SelectionModel::TRAILING; | 629 SelectionModel::LEADING : SelectionModel::TRAILING; |
| 634 size_t selection_start = select ? GetSelectionStart() : cursor; | 630 size_t selection_start = select ? GetSelectionStart() : cursor; |
| 635 SelectionModel sel(selection_start, cursor, caret_pos, placement); | 631 if (IsCursorablePosition(cursor)) { |
| 636 SetSelectionModel(sel); | 632 SelectionModel sel(selection_start, cursor, caret_pos, placement); |
| 637 } | 633 SetSelectionModel(sel); |
| 638 | 634 } |
| 639 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { | |
| 640 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || | |
| 641 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); | |
| 642 } | 635 } |
| 643 | 636 |
| 644 void RenderText::UpdateCachedBoundsAndOffset() { | 637 void RenderText::UpdateCachedBoundsAndOffset() { |
| 645 if (cached_bounds_and_offset_valid_) | 638 if (cached_bounds_and_offset_valid_) |
| 646 return; | 639 return; |
| 647 // First, set the valid flag true to calculate the current cursor bounds using | 640 // First, set the valid flag true to calculate the current cursor bounds using |
| 648 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 641 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| 649 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 642 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
| 650 cached_bounds_and_offset_valid_ = true; | 643 cached_bounds_and_offset_valid_ = true; |
| 651 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 644 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 682 selection_start, | 675 selection_start, |
| 683 SelectionModel::LEADING); | 676 SelectionModel::LEADING); |
| 684 else if (selection_start > selection_end) | 677 else if (selection_start > selection_end) |
| 685 return SelectionModel(selection_start, | 678 return SelectionModel(selection_start, |
| 686 GetIndexOfPreviousGrapheme(selection_start), | 679 GetIndexOfPreviousGrapheme(selection_start), |
| 687 SelectionModel::TRAILING); | 680 SelectionModel::TRAILING); |
| 688 return selection_model_; | 681 return selection_model_; |
| 689 } | 682 } |
| 690 | 683 |
| 691 } // namespace gfx | 684 } // namespace gfx |
| OLD | NEW |