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 |