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(GetIndexOfPreviousGrapheme(position)); |
| 186 sel.set_caret_placement(SelectionModel::TRAILING); |
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(selection_model()); |
190 SelectionModel selection(GetSelectionStart(), 0, | 192 position.set_selection_start(GetCursorPosition()); |
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 (break_type != LINE_BREAK && !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 breaks, use the nearest word boundary left of the selection. |
207 if (break_type == WORD_BREAK) | 202 if (break_type == WORD_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(selection_model()); |
219 SelectionModel selection(GetSelectionStart(), text().length(), | 214 position.set_selection_start(GetCursorPosition()); |
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 (break_type != LINE_BREAK && !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 breaks, use the nearest word boundary right of the selection. |
236 if (break_type == WORD_BREAK) | 224 if (break_type == WORD_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 } |
268 | 254 |
269 void RenderText::ClearSelection() { | 255 void RenderText::ClearSelection() { |
270 SetCursorPosition(GetCursorPosition()); | 256 SelectionModel sel(selection_model()); |
| 257 sel.set_selection_start(GetCursorPosition()); |
| 258 SetSelectionModel(sel); |
271 } | 259 } |
272 | 260 |
273 void RenderText::SelectAll() { | 261 void RenderText::SelectAll() { |
274 SelectionModel sel(0, text().length(), | 262 SelectionModel sel(0, text().length(), |
275 text().length(), SelectionModel::LEADING); | 263 text().length(), SelectionModel::LEADING); |
276 SetSelectionModel(sel); | 264 SetSelectionModel(sel); |
277 } | 265 } |
278 | 266 |
279 void RenderText::SelectWord() { | 267 void RenderText::SelectWord() { |
280 size_t selection_start = GetSelectionStart(); | 268 size_t selection_start = GetSelectionStart(); |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
496 cursor_bounds_valid_(false), | 484 cursor_bounds_valid_(false), |
497 cursor_visible_(false), | 485 cursor_visible_(false), |
498 insert_mode_(true), | 486 insert_mode_(true), |
499 composition_range_(), | 487 composition_range_(), |
500 style_ranges_(), | 488 style_ranges_(), |
501 default_style_(), | 489 default_style_(), |
502 display_rect_(), | 490 display_rect_(), |
503 display_offset_() { | 491 display_offset_() { |
504 } | 492 } |
505 | 493 |
506 SelectionModel RenderText::GetLeftCursorPosition(const SelectionModel& current, | 494 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, |
507 bool move_by_word) { | 495 BreakType break_type) { |
508 size_t position = current.selection_end(); | 496 if (break_type == LINE_BREAK) |
509 SelectionModel left = current; | 497 return SelectionModel(0, 0, SelectionModel::LEADING); |
510 if (!move_by_word) { | 498 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), |
511 left.set_selection_end(std::max(static_cast<long>(position - 1), | 499 static_cast<long>(0)); |
512 static_cast<long>(0))); | 500 if (break_type == CHARACTER_BREAK) |
513 return left; | 501 return SelectionModel(pos, pos, SelectionModel::LEADING); |
514 } | 502 |
515 // Notes: We always iterate words from the begining. | 503 // Notes: We always iterate words from the begining. |
516 // This is probably fast enough for our usage, but we may | 504 // This is probably fast enough for our usage, but we may |
517 // want to modify WordIterator so that it can start from the | 505 // want to modify WordIterator so that it can start from the |
518 // middle of string and advance backwards. | 506 // middle of string and advance backwards. |
519 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 507 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
520 bool success = iter.Init(); | 508 bool success = iter.Init(); |
521 DCHECK(success); | 509 DCHECK(success); |
522 if (!success) { | 510 if (!success) |
523 left.set_selection_end(position); | 511 return current; |
524 return left; | |
525 } | |
526 int last = 0; | |
527 while (iter.Advance()) { | 512 while (iter.Advance()) { |
528 if (iter.IsWord()) { | 513 if (iter.IsWord()) { |
529 size_t begin = iter.pos() - iter.GetString().length(); | 514 size_t begin = iter.pos() - iter.GetString().length(); |
530 if (begin == position) { | 515 if (begin == current.selection_end()) { |
531 // The cursor is at the beginning of a word. | 516 // The cursor is at the beginning of a word. |
532 // Move to previous word. | 517 // Move to previous word. |
533 break; | 518 break; |
534 } else if (iter.pos() >= position) { | 519 } else if (iter.pos() >= current.selection_end()) { |
535 // The cursor is in the middle or at the end of a word. | 520 // The cursor is in the middle or at the end of a word. |
536 // Move to the top of current word. | 521 // Move to the top of current word. |
537 last = begin; | 522 pos = begin; |
538 break; | 523 break; |
539 } else { | 524 } else { |
540 last = iter.pos() - iter.GetString().length(); | 525 pos = iter.pos() - iter.GetString().length(); |
541 } | 526 } |
542 } | 527 } |
543 } | 528 } |
544 | 529 |
545 left.set_selection_end(last); | 530 return SelectionModel(pos, pos, SelectionModel::LEADING); |
546 return left; | |
547 } | 531 } |
548 | 532 |
549 SelectionModel RenderText::GetRightCursorPosition(const SelectionModel& current, | 533 SelectionModel RenderText::GetRightSelectionModel(const SelectionModel& current, |
550 bool move_by_word) { | 534 BreakType break_type) { |
551 size_t position = current.selection_end(); | 535 if (break_type == LINE_BREAK) |
552 SelectionModel right = current; | 536 return SelectionModel(text().length(), |
553 | 537 GetIndexOfPreviousGrapheme(text().length()), SelectionModel::TRAILING); |
554 if (!move_by_word) { | 538 size_t pos = std::min(current.selection_end() + 1, text().length()); |
555 right.set_selection_end(std::min(position + 1, text().length())); | 539 if (break_type == CHARACTER_BREAK) |
556 return right; | 540 return SelectionModel(pos, pos, SelectionModel::LEADING); |
557 } | |
558 | 541 |
559 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 542 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
560 bool success = iter.Init(); | 543 bool success = iter.Init(); |
561 DCHECK(success); | 544 DCHECK(success); |
562 if (!success) { | 545 if (!success) |
563 right.set_selection_end(position); | 546 return current; |
564 return right; | |
565 } | |
566 size_t pos = 0; | |
567 while (iter.Advance()) { | 547 while (iter.Advance()) { |
568 pos = iter.pos(); | 548 pos = iter.pos(); |
569 if (iter.IsWord() && pos > position) { | 549 if (iter.IsWord() && pos > current.selection_end()) |
570 break; | 550 break; |
571 } | |
572 } | 551 } |
573 right.set_selection_end(pos); | 552 return SelectionModel(pos, pos, SelectionModel::LEADING); |
574 return right; | 553 } |
| 554 |
| 555 size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) const { |
| 556 // TODO(msw): Handle complex script. |
| 557 return std::max(static_cast<int>(position - 1), static_cast<int>(0)); |
575 } | 558 } |
576 | 559 |
577 void RenderText::ApplyCompositionAndSelectionStyles( | 560 void RenderText::ApplyCompositionAndSelectionStyles( |
578 StyleRanges* style_ranges) const { | 561 StyleRanges* style_ranges) const { |
579 // TODO(msw): This pattern ought to be reconsidered; what about composition | 562 // TODO(msw): This pattern ought to be reconsidered; what about composition |
580 // and selection overlaps, retain existing local style features? | 563 // and selection overlaps, retain existing local style features? |
581 // Apply a composition style override to a copy of the style ranges. | 564 // Apply a composition style override to a copy of the style ranges. |
582 if (composition_range_.IsValid() && !composition_range_.is_empty()) { | 565 if (composition_range_.IsValid() && !composition_range_.is_empty()) { |
583 StyleRange composition_style(default_style_); | 566 StyleRange composition_style(default_style_); |
584 composition_style.underline = true; | 567 composition_style.underline = true; |
(...skipping 27 matching lines...) Expand all Loading... |
612 } else if ((display_offset_.x() + cursor_bounds_.right()) > display_width) { | 595 } else if ((display_offset_.x() + cursor_bounds_.right()) > display_width) { |
613 // Pan to show the cursor when it overflows to the right, | 596 // Pan to show the cursor when it overflows to the right, |
614 display_offset_.set_x(display_width - cursor_bounds_.right()); | 597 display_offset_.set_x(display_width - cursor_bounds_.right()); |
615 } else if ((display_offset_.x() + cursor_bounds_.x()) < 0) { | 598 } else if ((display_offset_.x() + cursor_bounds_.x()) < 0) { |
616 // Pan to show the cursor when it overflows to the left. | 599 // Pan to show the cursor when it overflows to the left. |
617 display_offset_.set_x(-cursor_bounds_.x()); | 600 display_offset_.set_x(-cursor_bounds_.x()); |
618 } | 601 } |
619 } | 602 } |
620 | 603 |
621 } // namespace gfx | 604 } // namespace gfx |
OLD | NEW |