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 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 } else if (i->range.end() > text_.length()) { | 155 } else if (i->range.end() > text_.length()) { |
156 i->range.set_end(text_.length()); | 156 i->range.set_end(text_.length()); |
157 } | 157 } |
158 } | 158 } |
159 style_ranges_.back().range.set_end(text_.length()); | 159 style_ranges_.back().range.set_end(text_.length()); |
160 } | 160 } |
161 #ifndef NDEBUG | 161 #ifndef NDEBUG |
162 CheckStyleRanges(style_ranges_, text_.length()); | 162 CheckStyleRanges(style_ranges_, text_.length()); |
163 #endif | 163 #endif |
164 cached_bounds_and_offset_valid_ = false; | 164 cached_bounds_and_offset_valid_ = false; |
| 165 |
| 166 // Reset selection model. SetText should always followed by SetSelectionModel |
| 167 // or SetCursorPosition in upper layer. |
| 168 SetSelectionModel(SelectionModel(0, 0, SelectionModel::LEADING)); |
165 } | 169 } |
166 | 170 |
167 void RenderText::SetSelectionModel(const SelectionModel& sel) { | 171 void RenderText::SetSelectionModel(const SelectionModel& sel) { |
168 size_t start = sel.selection_start(); | 172 size_t start = sel.selection_start(); |
169 size_t end = sel.selection_end(); | 173 size_t end = sel.selection_end(); |
170 selection_model_.set_selection_start(std::min(start, text().length())); | 174 selection_model_.set_selection_start(std::min(start, text().length())); |
171 selection_model_.set_selection_end(std::min(end, text().length())); | 175 selection_model_.set_selection_end(std::min(end, text().length())); |
172 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length())); | 176 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length())); |
173 selection_model_.set_caret_placement(sel.caret_placement()); | 177 selection_model_.set_caret_placement(sel.caret_placement()); |
174 | 178 |
175 cached_bounds_and_offset_valid_ = false; | 179 cached_bounds_and_offset_valid_ = false; |
176 } | 180 } |
177 | 181 |
178 void RenderText::SetDisplayRect(const Rect& r) { | 182 void RenderText::SetDisplayRect(const Rect& r) { |
179 display_rect_ = r; | 183 display_rect_ = r; |
180 cached_bounds_and_offset_valid_ = false; | 184 cached_bounds_and_offset_valid_ = false; |
181 } | 185 } |
182 | 186 |
183 size_t RenderText::GetCursorPosition() const { | 187 size_t RenderText::GetCursorPosition() const { |
184 return selection_model_.selection_end(); | 188 return selection_model_.selection_end(); |
185 } | 189 } |
186 | 190 |
187 void RenderText::SetCursorPosition(const size_t position) { | 191 void RenderText::SetCursorPosition(size_t position) { |
188 SelectionModel sel(selection_model()); | 192 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 } | 193 } |
195 | 194 |
196 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { | 195 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { |
197 SelectionModel position(selection_model()); | 196 SelectionModel position(selection_model()); |
198 position.set_selection_start(GetCursorPosition()); | 197 position.set_selection_start(GetCursorPosition()); |
199 // Cancelling a selection moves to the edge of the selection. | 198 // Cancelling a selection moves to the edge of the selection. |
200 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 199 if (break_type != LINE_BREAK && !EmptySelection() && !select) { |
201 // Use the selection start if it is left of the selection end. | 200 // Use the selection start if it is left of the selection end. |
202 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 201 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
203 SelectionModel::LEADING); | |
204 if (GetCursorBounds(selection_start, false).x() < | 202 if (GetCursorBounds(selection_start, false).x() < |
205 GetCursorBounds(position, false).x()) | 203 GetCursorBounds(position, false).x()) |
206 position = selection_start; | 204 position = selection_start; |
207 // For word breaks, use the nearest word boundary left of the selection. | 205 // For word breaks, use the nearest word boundary left of the selection. |
208 if (break_type == WORD_BREAK) | 206 if (break_type == WORD_BREAK) |
209 position = GetLeftSelectionModel(position, break_type); | 207 position = GetLeftSelectionModel(position, break_type); |
210 } else { | 208 } else { |
211 position = GetLeftSelectionModel(position, break_type); | 209 position = GetLeftSelectionModel(position, break_type); |
212 } | 210 } |
213 if (select) | 211 if (select) |
214 position.set_selection_start(GetSelectionStart()); | 212 position.set_selection_start(GetSelectionStart()); |
215 MoveCursorTo(position); | 213 MoveCursorTo(position); |
216 } | 214 } |
217 | 215 |
218 void RenderText::MoveCursorRight(BreakType break_type, bool select) { | 216 void RenderText::MoveCursorRight(BreakType break_type, bool select) { |
219 SelectionModel position(selection_model()); | 217 SelectionModel position(selection_model()); |
220 position.set_selection_start(GetCursorPosition()); | 218 position.set_selection_start(GetCursorPosition()); |
221 // Cancelling a selection moves to the edge of the selection. | 219 // Cancelling a selection moves to the edge of the selection. |
222 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 220 if (break_type != LINE_BREAK && !EmptySelection() && !select) { |
223 // Use the selection start if it is right of the selection end. | 221 // Use the selection start if it is right of the selection end. |
224 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 222 SelectionModel selection_start = GetSelectionModelForSelectionStart(); |
225 SelectionModel::LEADING); | |
226 if (GetCursorBounds(selection_start, false).x() > | 223 if (GetCursorBounds(selection_start, false).x() > |
227 GetCursorBounds(position, false).x()) | 224 GetCursorBounds(position, false).x()) |
228 position = selection_start; | 225 position = selection_start; |
229 // For word breaks, use the nearest word boundary right of the selection. | 226 // For word breaks, use the nearest word boundary right of the selection. |
230 if (break_type == WORD_BREAK) | 227 if (break_type == WORD_BREAK) |
231 position = GetRightSelectionModel(position, break_type); | 228 position = GetRightSelectionModel(position, break_type); |
232 } else { | 229 } else { |
233 position = GetRightSelectionModel(position, break_type); | 230 position = GetRightSelectionModel(position, break_type); |
234 } | 231 } |
235 if (select) | 232 if (select) |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 } | 298 } |
302 | 299 |
303 // Now we move selection_end_ to end of selection. Selection boundary | 300 // Now we move selection_end_ to end of selection. Selection boundary |
304 // is defined as the position where we have alpha-num character on one side | 301 // is defined as the position where we have alpha-num character on one side |
305 // and non-alpha-num char on the other side. | 302 // and non-alpha-num char on the other side. |
306 for (; cursor_position < text().length(); cursor_position++) { | 303 for (; cursor_position < text().length(); cursor_position++) { |
307 if (IsPositionAtWordSelectionBoundary(cursor_position)) | 304 if (IsPositionAtWordSelectionBoundary(cursor_position)) |
308 break; | 305 break; |
309 } | 306 } |
310 | 307 |
311 SelectionModel sel(selection_model()); | 308 MoveCursorTo(selection_start, false); |
312 sel.set_selection_start(selection_start); | 309 MoveCursorTo(cursor_position, true); |
313 sel.set_selection_end(cursor_position); | |
314 sel.set_caret_pos(GetIndexOfPreviousGrapheme(cursor_position)); | |
315 sel.set_caret_placement(SelectionModel::TRAILING); | |
316 SetSelectionModel(sel); | |
317 } | 310 } |
318 | 311 |
319 const ui::Range& RenderText::GetCompositionRange() const { | 312 const ui::Range& RenderText::GetCompositionRange() const { |
320 return composition_range_; | 313 return composition_range_; |
321 } | 314 } |
322 | 315 |
323 void RenderText::SetCompositionRange(const ui::Range& composition_range) { | 316 void RenderText::SetCompositionRange(const ui::Range& composition_range) { |
324 CHECK(!composition_range.IsValid() || | 317 CHECK(!composition_range.IsValid() || |
325 ui::Range(0, text_.length()).Contains(composition_range)); | 318 ui::Range(0, text_.length()).Contains(composition_range)); |
326 composition_range_.set_end(composition_range.end()); | 319 composition_range_.set_end(composition_range.end()); |
(...skipping 15 matching lines...) Expand all 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 11 matching lines...) Expand all Loading... |
522 pos = iter.pos() - iter.GetString().length(); | 515 pos = iter.pos() - iter.GetString().length(); |
523 } | 516 } |
524 } | 517 } |
525 } | 518 } |
526 | 519 |
527 return SelectionModel(pos, pos, SelectionModel::LEADING); | 520 return SelectionModel(pos, pos, SelectionModel::LEADING); |
528 } | 521 } |
529 | 522 |
530 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current, | 523 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current, |
531 BreakType break_type) { | 524 BreakType break_type) { |
| 525 if (text_.empty()) |
| 526 return SelectionModel(0, 0, SelectionModel::LEADING); |
532 if (break_type == LINE_BREAK) | 527 if (break_type == LINE_BREAK) |
533 return SelectionModel(text().length(), | 528 return SelectionModel(text().length(), |
534 GetIndexOfPreviousGrapheme(text().length()), SelectionModel::TRAILING); | 529 GetIndexOfPreviousGrapheme(text().length()), SelectionModel::TRAILING); |
535 size_t pos = std::min(current.selection_end() + 1, text().length()); | 530 size_t pos = std::min(current.selection_end() + 1, text().length()); |
536 if (break_type == CHARACTER_BREAK) | 531 if (break_type == CHARACTER_BREAK) |
537 return SelectionModel(pos, pos, SelectionModel::LEADING); | 532 return SelectionModel(pos, pos, SelectionModel::LEADING); |
538 | 533 |
539 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 534 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
540 bool success = iter.Init(); | 535 bool success = iter.Init(); |
541 DCHECK(success); | 536 DCHECK(success); |
(...skipping 27 matching lines...) Expand all Loading... |
569 // Apply a selection style override to a copy of the style ranges. | 564 // Apply a selection style override to a copy of the style ranges. |
570 if (!EmptySelection()) { | 565 if (!EmptySelection()) { |
571 StyleRange selection_style(default_style_); | 566 StyleRange selection_style(default_style_); |
572 selection_style.foreground = kSelectedTextColor; | 567 selection_style.foreground = kSelectedTextColor; |
573 selection_style.range.set_start(MinOfSelection()); | 568 selection_style.range.set_start(MinOfSelection()); |
574 selection_style.range.set_end(MaxOfSelection()); | 569 selection_style.range.set_end(MaxOfSelection()); |
575 ApplyStyleRangeImpl(style_ranges, selection_style); | 570 ApplyStyleRangeImpl(style_ranges, selection_style); |
576 } | 571 } |
577 } | 572 } |
578 | 573 |
| 574 Point RenderText::ToTextPoint(const Point& point) { |
| 575 Point p(point.Subtract(display_rect_.origin())); |
| 576 p = p.Subtract(GetUpdatedDisplayOffset()); |
| 577 if (base::i18n::IsRTL()) |
| 578 p.Offset(GetStringWidth() - display_rect_.width() + 1, 0); |
| 579 return p; |
| 580 } |
| 581 |
| 582 Point RenderText::ToViewPoint(const Point& point) { |
| 583 Point p(point.Add(display_rect_.origin())); |
| 584 p = p.Add(GetUpdatedDisplayOffset()); |
| 585 if (base::i18n::IsRTL()) |
| 586 p.Offset(display_rect_.width() - GetStringWidth() - 1, 0); |
| 587 return p; |
| 588 } |
| 589 |
| 590 void RenderText::MoveCursorTo(size_t position, bool select) { |
| 591 size_t caret_pos = GetIndexOfPreviousGrapheme(position); |
| 592 SelectionModel::CaretPlacement placement = (caret_pos == position) ? |
| 593 SelectionModel::LEADING : SelectionModel::TRAILING; |
| 594 size_t selection_start = select ? GetSelectionStart() : position; |
| 595 SelectionModel sel(selection_start, position, caret_pos, placement); |
| 596 SetSelectionModel(sel); |
| 597 } |
| 598 |
579 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { | 599 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { |
580 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || | 600 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || |
581 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); | 601 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); |
582 } | 602 } |
583 | 603 |
584 void RenderText::UpdateCachedBoundsAndOffset() { | 604 void RenderText::UpdateCachedBoundsAndOffset() { |
585 if (cached_bounds_and_offset_valid_) | 605 if (cached_bounds_and_offset_valid_) |
586 return; | 606 return; |
587 // First, set the valid flag true to calculate the current cursor bounds using | 607 // 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 | 608 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
589 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 609 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
590 cached_bounds_and_offset_valid_ = true; | 610 cached_bounds_and_offset_valid_ = true; |
591 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 611 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
592 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1)); | 612 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1)); |
593 // Update |display_offset_| to ensure the current cursor is visible. | 613 // Update |display_offset_| to ensure the current cursor is visible. |
594 int display_width = display_rect_.width(); | 614 int display_width = display_rect_.width(); |
595 int string_width = GetStringWidth(); | 615 int string_width = GetStringWidth(); |
596 int delta_offset = 0; | 616 int delta_offset = 0; |
597 if (string_width < display_width) { | 617 if (string_width < display_width) { |
598 // Show all text whenever the text fits to the size. | 618 // Show all text whenever the text fits to the size. |
599 delta_offset = -display_offset_.x(); | 619 delta_offset = -display_offset_.x(); |
600 } else if (cursor_bounds_.right() > display_rect_.right()) { | 620 } else if (cursor_bounds_.right() > display_rect_.right()) { |
| 621 // TODO(xji): when the character overflow is a RTL character, currently, if |
| 622 // we pan cursor at the rightmost position, the entered RTL character is not |
| 623 // displayed. Should pan cursor to show the last logical characters. |
| 624 // |
601 // Pan to show the cursor when it overflows to the right, | 625 // Pan to show the cursor when it overflows to the right, |
602 delta_offset = display_rect_.right() - cursor_bounds_.right(); | 626 delta_offset = display_rect_.right() - cursor_bounds_.right(); |
603 } else if (cursor_bounds_.x() < display_rect_.x()) { | 627 } else if (cursor_bounds_.x() < display_rect_.x()) { |
| 628 // TODO(xji): have similar problem as above when overflow character is a |
| 629 // LTR character. |
| 630 // |
604 // Pan to show the cursor when it overflows to the left. | 631 // Pan to show the cursor when it overflows to the left. |
605 delta_offset = display_rect_.x() - cursor_bounds_.x(); | 632 delta_offset = display_rect_.x() - cursor_bounds_.x(); |
606 } | 633 } |
607 display_offset_.Offset(delta_offset, 0); | 634 display_offset_.Offset(delta_offset, 0); |
608 cursor_bounds_.Offset(delta_offset, 0); | 635 cursor_bounds_.Offset(delta_offset, 0); |
609 } | 636 } |
610 | 637 |
| 638 SelectionModel RenderText::GetSelectionModelForSelectionStart() { |
| 639 size_t selection_start = GetSelectionStart(); |
| 640 size_t selection_end = GetCursorPosition(); |
| 641 if (selection_start < selection_end) |
| 642 return SelectionModel(selection_start, |
| 643 selection_start, |
| 644 SelectionModel::LEADING); |
| 645 else if (selection_start > selection_end) |
| 646 return SelectionModel(selection_start, |
| 647 GetIndexOfPreviousGrapheme(selection_start), |
| 648 SelectionModel::TRAILING); |
| 649 return selection_model_; |
| 650 } |
| 651 |
611 } // namespace gfx | 652 } // namespace gfx |
OLD | NEW |