Chromium Code Reviews| 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 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 175 } | 175 } |
| 176 | 176 |
| 177 size_t RenderText::GetCursorPosition() const { | 177 size_t RenderText::GetCursorPosition() const { |
| 178 return selection_model_.selection_end(); | 178 return selection_model_.selection_end(); |
| 179 } | 179 } |
| 180 | 180 |
| 181 void RenderText::SetCursorPosition(const size_t position) { | 181 void RenderText::SetCursorPosition(const size_t position) { |
| 182 SelectionModel sel(selection_model()); | 182 SelectionModel sel(selection_model()); |
| 183 sel.set_selection_start(position); | 183 sel.set_selection_start(position); |
| 184 sel.set_selection_end(position); | 184 sel.set_selection_end(position); |
| 185 sel.set_caret_pos(GetPreviousGrapheme(position)); | |
| 186 sel.set_caret_placement(SelectionModel::TRAILING); | |
|
xji
2011/08/08 23:56:13
RenderText::ClearSelection() probably wont work af
msw
2011/08/09 01:07:42
Done.
| |
| 185 SetSelectionModel(sel); | 187 SetSelectionModel(sel); |
| 186 } | 188 } |
| 187 | 189 |
| 188 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { | 190 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { |
| 189 if (break_type == LINE_BREAK) { | 191 SelectionModel position = SelectionModel(GetCursorPosition(), |
| 190 SelectionModel selection(GetSelectionStart(), 0, | 192 GetCursorPosition(), SelectionModel::LEADING); |
|
xji
2011/08/08 23:56:13
should the position be selection_model_?
or select
msw
2011/08/09 01:07:42
Done.
| |
| 191 0, SelectionModel::LEADING); | |
| 192 if (!select) | |
| 193 selection.set_selection_start(selection.selection_end()); | |
| 194 MoveCursorTo(selection); | |
| 195 return; | |
| 196 } | |
| 197 SelectionModel position = selection_model_; | |
| 198 // Cancelling a selection moves to the edge of the selection. | 193 // Cancelling a selection moves to the edge of the selection. |
| 199 if (!EmptySelection() && !select) { | 194 if (!EmptySelection() && !select) { |
| 200 // 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. |
| 201 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 196 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), |
| 202 GetSelectionStart(), SelectionModel::LEADING); | 197 SelectionModel::LEADING); |
| 203 if (GetCursorBounds(selection_start, false).x() < | 198 if (GetCursorBounds(selection_start, false).x() < |
| 204 GetCursorBounds(position, false).x()) | 199 GetCursorBounds(position, false).x()) |
| 205 position = selection_start; | 200 position = selection_start; |
| 206 // If |move_by_word|, use the nearest word boundary left of the selection. | 201 // For word and line breaks, the cursor moves beyond the selection edge. |
| 207 if (break_type == WORD_BREAK) | 202 if (break_type != CHARACTER_BREAK) |
| 208 position = GetLeftCursorPosition(position, true); | 203 position = GetLeftSelectionModel(position, break_type); |
| 209 } else { | 204 } else { |
| 210 position = GetLeftCursorPosition(position, break_type == WORD_BREAK); | 205 position = GetLeftSelectionModel(position, break_type); |
| 211 } | 206 } |
| 212 if (!select) | 207 if (select) |
| 213 position.set_selection_start(position.selection_end()); | 208 position.set_selection_start(GetSelectionStart()); |
| 214 MoveCursorTo(position); | 209 MoveCursorTo(position); |
| 215 } | 210 } |
| 216 | 211 |
| 217 void RenderText::MoveCursorRight(BreakType break_type, bool select) { | 212 void RenderText::MoveCursorRight(BreakType break_type, bool select) { |
| 218 if (break_type == LINE_BREAK) { | 213 SelectionModel position = SelectionModel(GetCursorPosition(), |
| 219 SelectionModel selection(GetSelectionStart(), text().length(), | 214 GetCursorPosition(), SelectionModel::LEADING); |
|
xji
2011/08/08 23:56:13
ditto
msw
2011/08/09 01:07:42
Done.
| |
| 220 text().length(), SelectionModel::PREVIOUS_GRAPHEME_TRAILING); | |
| 221 if (!select) | |
| 222 selection.set_selection_start(selection.selection_end()); | |
| 223 MoveCursorTo(selection); | |
| 224 return; | |
| 225 } | |
| 226 SelectionModel position = selection_model_; | |
| 227 // Cancelling a selection moves to the edge of the selection. | 215 // Cancelling a selection moves to the edge of the selection. |
| 228 if (!EmptySelection() && !select) { | 216 if (!EmptySelection() && !select) { |
| 229 // 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. |
| 230 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 218 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), |
| 231 GetSelectionStart(), SelectionModel::LEADING); | 219 SelectionModel::LEADING); |
| 232 if (GetCursorBounds(selection_start, false).x() > | 220 if (GetCursorBounds(selection_start, false).x() > |
| 233 GetCursorBounds(position, false).x()) | 221 GetCursorBounds(position, false).x()) |
| 234 position = selection_start; | 222 position = selection_start; |
| 235 // If |move_by_word|, use the nearest word boundary right of the selection. | 223 // For word and line breaks, the cursor moves beyond the selection edge. |
| 236 if (break_type == WORD_BREAK) | 224 if (break_type != CHARACTER_BREAK) |
| 237 position = GetRightCursorPosition(position, true); | 225 position = GetRightSelectionModel(position, break_type); |
| 238 } else { | 226 } else { |
| 239 position = GetRightCursorPosition(position, break_type == WORD_BREAK); | 227 position = GetRightSelectionModel(position, break_type); |
| 240 } | 228 } |
| 241 if (!select) | 229 if (select) |
| 242 position.set_selection_start(position.selection_end()); | 230 position.set_selection_start(GetSelectionStart()); |
| 243 MoveCursorTo(position); | 231 MoveCursorTo(position); |
| 244 } | 232 } |
| 245 | 233 |
| 246 bool RenderText::MoveCursorTo(const SelectionModel& selection) { | 234 bool RenderText::MoveCursorTo(const SelectionModel& selection) { |
| 247 bool changed = !selection.Equals(selection_model_); | 235 bool changed = !selection.Equals(selection_model_); |
| 248 SetSelectionModel(selection); | 236 SetSelectionModel(selection); |
| 249 return changed; | 237 return changed; |
| 250 } | 238 } |
| 251 | 239 |
| 252 bool RenderText::MoveCursorTo(const Point& point, bool select) { | 240 bool RenderText::MoveCursorTo(const Point& point, bool select) { |
| 253 SelectionModel selection = FindCursorPosition(point); | 241 SelectionModel selection = FindCursorPosition(point); |
| 254 if (select) | 242 if (select) |
| 255 selection.set_selection_start(GetSelectionStart()); | 243 selection.set_selection_start(GetSelectionStart()); |
| 256 else | |
| 257 selection.set_selection_start(selection.selection_end()); | |
| 258 return MoveCursorTo(selection); | 244 return MoveCursorTo(selection); |
| 259 } | 245 } |
| 260 | 246 |
| 261 bool RenderText::IsPointInSelection(const Point& point) { | 247 bool RenderText::IsPointInSelection(const Point& point) { |
| 262 // TODO(xji): should this check whether the point is inside the visual | 248 // TODO(xji): should this check whether the point is inside the visual |
| 263 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points | 249 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points |
| 264 // to the right half of 'c', is the point in selection? | 250 // to the right half of 'c', is the point in selection? |
| 265 size_t pos = FindCursorPosition(point).selection_end(); | 251 size_t pos = FindCursorPosition(point).selection_end(); |
| 266 return (pos >= MinOfSelection() && pos < MaxOfSelection()); | 252 return (pos >= MinOfSelection() && pos < MaxOfSelection()); |
| 267 } | 253 } |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 496 cursor_bounds_valid_(false), | 482 cursor_bounds_valid_(false), |
| 497 cursor_visible_(false), | 483 cursor_visible_(false), |
| 498 insert_mode_(true), | 484 insert_mode_(true), |
| 499 composition_range_(), | 485 composition_range_(), |
| 500 style_ranges_(), | 486 style_ranges_(), |
| 501 default_style_(), | 487 default_style_(), |
| 502 display_rect_(), | 488 display_rect_(), |
| 503 display_offset_() { | 489 display_offset_() { |
| 504 } | 490 } |
| 505 | 491 |
| 506 SelectionModel RenderText::GetLeftCursorPosition(const SelectionModel& current, | 492 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, |
| 507 bool move_by_word) { | 493 BreakType break_type) { |
| 508 size_t position = current.selection_end(); | 494 if (break_type == LINE_BREAK) |
| 509 SelectionModel left = current; | 495 return SelectionModel(0, 0, SelectionModel::LEADING); |
| 510 if (!move_by_word) { | 496 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), |
| 511 left.set_selection_end(std::max(static_cast<long>(position - 1), | 497 static_cast<long>(0)); |
| 512 static_cast<long>(0))); | 498 if (break_type == CHARACTER_BREAK) |
| 513 return left; | 499 return SelectionModel(pos, pos, SelectionModel::LEADING); |
| 514 } | 500 |
| 515 // Notes: We always iterate words from the begining. | 501 // Notes: We always iterate words from the begining. |
| 516 // This is probably fast enough for our usage, but we may | 502 // This is probably fast enough for our usage, but we may |
| 517 // want to modify WordIterator so that it can start from the | 503 // want to modify WordIterator so that it can start from the |
| 518 // middle of string and advance backwards. | 504 // middle of string and advance backwards. |
| 519 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 505 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 520 bool success = iter.Init(); | 506 bool success = iter.Init(); |
| 521 DCHECK(success); | 507 DCHECK(success); |
| 522 if (!success) { | 508 if (!success) |
| 523 left.set_selection_end(position); | 509 return current; |
| 524 return left; | |
| 525 } | |
| 526 int last = 0; | |
| 527 while (iter.Advance()) { | 510 while (iter.Advance()) { |
| 528 if (iter.IsWord()) { | 511 if (iter.IsWord()) { |
| 529 size_t begin = iter.pos() - iter.GetString().length(); | 512 size_t begin = iter.pos() - iter.GetString().length(); |
| 530 if (begin == position) { | 513 if (begin == current.selection_end()) { |
| 531 // The cursor is at the beginning of a word. | 514 // The cursor is at the beginning of a word. |
| 532 // Move to previous word. | 515 // Move to previous word. |
| 533 break; | 516 break; |
| 534 } else if (iter.pos() >= position) { | 517 } else if (iter.pos() >= current.selection_end()) { |
| 535 // The cursor is in the middle or at the end of a word. | 518 // The cursor is in the middle or at the end of a word. |
| 536 // Move to the top of current word. | 519 // Move to the top of current word. |
| 537 last = begin; | 520 pos = begin; |
| 538 break; | 521 break; |
| 539 } else { | 522 } else { |
| 540 last = iter.pos() - iter.GetString().length(); | 523 pos = iter.pos() - iter.GetString().length(); |
| 541 } | 524 } |
| 542 } | 525 } |
| 543 } | 526 } |
| 544 | 527 |
| 545 left.set_selection_end(last); | 528 return SelectionModel(pos, pos, SelectionModel::LEADING); |
| 546 return left; | |
| 547 } | 529 } |
| 548 | 530 |
| 549 SelectionModel RenderText::GetRightCursorPosition(const SelectionModel& current, | 531 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current, |
| 550 bool move_by_word) { | 532 BreakType break_type) { |
| 551 size_t position = current.selection_end(); | 533 if (break_type == LINE_BREAK) |
| 552 SelectionModel right = current; | 534 return SelectionModel(text().length(), text().length(), |
| 553 | 535 SelectionModel::LEADING); |
|
xji
2011/08/08 23:56:13
this will return the rightmost visual extreme of t
msw
2011/08/09 01:07:42
Done.
| |
| 554 if (!move_by_word) { | 536 size_t pos = std::min(current.selection_end() + 1, text().length()); |
| 555 right.set_selection_end(std::min(position + 1, text().length())); | 537 if (break_type == CHARACTER_BREAK) |
| 556 return right; | 538 return SelectionModel(pos, pos, SelectionModel::LEADING); |
| 557 } | |
| 558 | 539 |
| 559 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 540 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 560 bool success = iter.Init(); | 541 bool success = iter.Init(); |
| 561 DCHECK(success); | 542 DCHECK(success); |
| 562 if (!success) { | 543 if (!success) |
| 563 right.set_selection_end(position); | 544 return current; |
| 564 return right; | |
| 565 } | |
| 566 size_t pos = 0; | |
| 567 while (iter.Advance()) { | 545 while (iter.Advance()) { |
| 568 pos = iter.pos(); | 546 pos = iter.pos(); |
| 569 if (iter.IsWord() && pos > position) { | 547 if (iter.IsWord() && pos > current.selection_end()) |
| 570 break; | 548 break; |
| 571 } | |
| 572 } | 549 } |
| 573 right.set_selection_end(pos); | 550 return SelectionModel(pos, pos, SelectionModel::LEADING); |
| 574 return right; | 551 } |
| 552 | |
| 553 size_t RenderText::GetPreviousGrapheme(size_t position) const { | |
| 554 // TODO(msw): Handle complex script. | |
| 555 return std::max(static_cast<int>(position - 1), static_cast<int>(0)); | |
| 575 } | 556 } |
| 576 | 557 |
| 577 void RenderText::ApplyCompositionAndSelectionStyles( | 558 void RenderText::ApplyCompositionAndSelectionStyles( |
| 578 StyleRanges* style_ranges) const { | 559 StyleRanges* style_ranges) const { |
| 579 // TODO(msw): This pattern ought to be reconsidered; what about composition | 560 // TODO(msw): This pattern ought to be reconsidered; what about composition |
| 580 // and selection overlaps, retain existing local style features? | 561 // and selection overlaps, retain existing local style features? |
| 581 // Apply a composition style override to a copy of the style ranges. | 562 // Apply a composition style override to a copy of the style ranges. |
| 582 if (composition_range_.IsValid() && !composition_range_.is_empty()) { | 563 if (composition_range_.IsValid() && !composition_range_.is_empty()) { |
| 583 StyleRange composition_style(default_style_); | 564 StyleRange composition_style(default_style_); |
| 584 composition_style.underline = true; | 565 composition_style.underline = true; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 612 } else if ((display_offset_.x() + cursor_bounds_.right()) > display_width) { | 593 } else if ((display_offset_.x() + cursor_bounds_.right()) > display_width) { |
| 613 // Pan to show the cursor when it overflows to the right, | 594 // Pan to show the cursor when it overflows to the right, |
| 614 display_offset_.set_x(display_width - cursor_bounds_.right()); | 595 display_offset_.set_x(display_width - cursor_bounds_.right()); |
| 615 } else if ((display_offset_.x() + cursor_bounds_.x()) < 0) { | 596 } else if ((display_offset_.x() + cursor_bounds_.x()) < 0) { |
| 616 // Pan to show the cursor when it overflows to the left. | 597 // Pan to show the cursor when it overflows to the left. |
| 617 display_offset_.set_x(-cursor_bounds_.x()); | 598 display_offset_.set_x(-cursor_bounds_.x()); |
| 618 } | 599 } |
| 619 } | 600 } |
| 620 | 601 |
| 621 } // namespace gfx | 602 } // namespace gfx |
| OLD | NEW |