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 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 CaretPlacement placement) { | 123 CaretPlacement placement) { |
124 selection_start_ = start; | 124 selection_start_ = start; |
125 selection_end_ = end; | 125 selection_end_ = end; |
126 caret_pos_ = pos; | 126 caret_pos_ = pos; |
127 caret_placement_ = placement; | 127 caret_placement_ = placement; |
128 } | 128 } |
129 | 129 |
130 RenderText::~RenderText() { | 130 RenderText::~RenderText() { |
131 } | 131 } |
132 | 132 |
133 void RenderText::set_display_rect(const Rect& r) { | |
134 display_rect_ = r; | |
135 } | |
136 | |
137 void RenderText::SetText(const string16& text) { | 133 void RenderText::SetText(const string16& text) { |
138 size_t old_text_length = text_.length(); | 134 size_t old_text_length = text_.length(); |
139 text_ = text; | 135 text_ = text; |
140 | 136 |
141 // Update the style ranges as needed. | 137 // Update the style ranges as needed. |
142 if (text_.empty()) { | 138 if (text_.empty()) { |
143 style_ranges_.clear(); | 139 style_ranges_.clear(); |
144 } else if (style_ranges_.empty()) { | 140 } else if (style_ranges_.empty()) { |
145 ApplyDefaultStyle(); | 141 ApplyDefaultStyle(); |
146 } else if (text_.length() > old_text_length) { | 142 } else if (text_.length() > old_text_length) { |
147 style_ranges_.back().range.set_end(text_.length()); | 143 style_ranges_.back().range.set_end(text_.length()); |
148 } else if (text_.length() < old_text_length) { | 144 } else if (text_.length() < old_text_length) { |
149 StyleRanges::iterator i; | 145 StyleRanges::iterator i; |
150 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) { | 146 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) { |
151 if (i->range.start() >= text_.length()) { | 147 if (i->range.start() >= text_.length()) { |
152 i = style_ranges_.erase(i); | 148 i = style_ranges_.erase(i); |
153 if (i == style_ranges_.end()) | 149 if (i == style_ranges_.end()) |
154 break; | 150 break; |
155 } else if (i->range.end() > text_.length()) { | 151 } else if (i->range.end() > text_.length()) { |
156 i->range.set_end(text_.length()); | 152 i->range.set_end(text_.length()); |
157 } | 153 } |
158 } | 154 } |
159 style_ranges_.back().range.set_end(text_.length()); | 155 style_ranges_.back().range.set_end(text_.length()); |
160 } | 156 } |
161 #ifndef NDEBUG | 157 #ifndef NDEBUG |
162 CheckStyleRanges(style_ranges_, text_.length()); | 158 CheckStyleRanges(style_ranges_, text_.length()); |
163 #endif | 159 #endif |
| 160 cached_bounds_and_offset_valid_ = false; |
164 } | 161 } |
165 | 162 |
166 void RenderText::SetSelectionModel(const SelectionModel& sel) { | 163 void RenderText::SetSelectionModel(const SelectionModel& sel) { |
167 size_t start = sel.selection_start(); | 164 size_t start = sel.selection_start(); |
168 size_t end = sel.selection_end(); | 165 size_t end = sel.selection_end(); |
169 selection_model_.set_selection_start(std::min(start, text().length())); | 166 selection_model_.set_selection_start(std::min(start, text().length())); |
170 selection_model_.set_selection_end(std::min(end, text().length())); | 167 selection_model_.set_selection_end(std::min(end, text().length())); |
171 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length())); | 168 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length())); |
172 selection_model_.set_caret_placement(sel.caret_placement()); | 169 selection_model_.set_caret_placement(sel.caret_placement()); |
173 | 170 |
174 cursor_bounds_valid_ = false; | 171 cached_bounds_and_offset_valid_ = false; |
| 172 } |
| 173 |
| 174 void RenderText::SetDisplayRect(const Rect& r) { |
| 175 display_rect_ = r; |
| 176 cached_bounds_and_offset_valid_ = false; |
175 } | 177 } |
176 | 178 |
177 size_t RenderText::GetCursorPosition() const { | 179 size_t RenderText::GetCursorPosition() const { |
178 return selection_model_.selection_end(); | 180 return selection_model_.selection_end(); |
179 } | 181 } |
180 | 182 |
181 void RenderText::SetCursorPosition(const size_t position) { | 183 void RenderText::SetCursorPosition(const size_t position) { |
182 SelectionModel sel(selection_model()); | 184 SelectionModel sel(selection_model()); |
183 sel.set_selection_start(position); | 185 sel.set_selection_start(position); |
184 sel.set_selection_end(position); | 186 sel.set_selection_end(position); |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 void RenderText::ApplyStyleRange(StyleRange style_range) { | 323 void RenderText::ApplyStyleRange(StyleRange style_range) { |
322 const ui::Range& new_range = style_range.range; | 324 const ui::Range& new_range = style_range.range; |
323 if (!new_range.IsValid() || new_range.is_empty()) | 325 if (!new_range.IsValid() || new_range.is_empty()) |
324 return; | 326 return; |
325 CHECK(!new_range.is_reversed()); | 327 CHECK(!new_range.is_reversed()); |
326 CHECK(ui::Range(0, text_.length()).Contains(new_range)); | 328 CHECK(ui::Range(0, text_.length()).Contains(new_range)); |
327 ApplyStyleRangeImpl(&style_ranges_, style_range); | 329 ApplyStyleRangeImpl(&style_ranges_, style_range); |
328 #ifndef NDEBUG | 330 #ifndef NDEBUG |
329 CheckStyleRanges(style_ranges_, text_.length()); | 331 CheckStyleRanges(style_ranges_, text_.length()); |
330 #endif | 332 #endif |
331 // TODO(xji): only invalidate cursor_bounds if font or underline change. | 333 // TODO(xji): only invalidate if font or underline changes. |
332 cursor_bounds_valid_ = false; | 334 cached_bounds_and_offset_valid_ = false; |
333 } | 335 } |
334 | 336 |
335 void RenderText::ApplyDefaultStyle() { | 337 void RenderText::ApplyDefaultStyle() { |
336 style_ranges_.clear(); | 338 style_ranges_.clear(); |
337 StyleRange style = StyleRange(default_style_); | 339 StyleRange style = StyleRange(default_style_); |
338 style.range.set_end(text_.length()); | 340 style.range.set_end(text_.length()); |
339 style_ranges_.push_back(style); | 341 style_ranges_.push_back(style); |
340 cursor_bounds_valid_ = false; | 342 cached_bounds_and_offset_valid_ = false; |
341 } | 343 } |
342 | 344 |
343 base::i18n::TextDirection RenderText::GetTextDirection() const { | 345 base::i18n::TextDirection RenderText::GetTextDirection() const { |
344 // TODO(msw): Bidi implementation, intended to replace the functionality added | 346 // TODO(msw): Bidi implementation, intended to replace the functionality added |
345 // in crrev.com/91881 (discussed in codereview.chromium.org/7324011). | 347 // in crrev.com/91881 (discussed in codereview.chromium.org/7324011). |
346 return base::i18n::LEFT_TO_RIGHT; | 348 return base::i18n::LEFT_TO_RIGHT; |
347 } | 349 } |
348 | 350 |
349 int RenderText::GetStringWidth() { | 351 int RenderText::GetStringWidth() { |
350 return GetSubstringBounds(0, text_.length())[0].width(); | 352 return default_style_.font.GetStringWidth(text()); |
351 } | 353 } |
352 | 354 |
353 void RenderText::Draw(Canvas* canvas) { | 355 void RenderText::Draw(Canvas* canvas) { |
354 // Clip the canvas to the text display area. | 356 // Clip the canvas to the text display area. |
355 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), | 357 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), |
356 display_rect_.width(), display_rect_.height()); | 358 display_rect_.width(), display_rect_.height()); |
357 | 359 |
358 // Draw the selection. | 360 // Draw the selection. |
359 std::vector<Rect> selection(GetSubstringBounds(GetSelectionStart(), | 361 std::vector<Rect> selection(GetSubstringBounds(GetSelectionStart(), |
360 GetCursorPosition())); | 362 GetCursorPosition())); |
361 SkColor selection_color = | 363 SkColor selection_color = |
362 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; | 364 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; |
363 for (std::vector<Rect>::const_iterator i = selection.begin(); | 365 for (std::vector<Rect>::const_iterator i = selection.begin(); |
364 i < selection.end(); ++i) { | 366 i < selection.end(); ++i) { |
365 Rect r(*i); | 367 Rect r(*i); |
366 r.Offset(display_rect_.origin()); | |
367 r.Offset(display_offset_); | |
368 // Center the rect vertically in |display_rect_|. | |
369 r.Offset(Point(0, (display_rect_.height() - r.height()) / 2)); | |
370 canvas->FillRectInt(selection_color, r.x(), r.y(), r.width(), r.height()); | 368 canvas->FillRectInt(selection_color, r.x(), r.y(), r.width(), r.height()); |
371 } | 369 } |
372 | 370 |
373 // Create a temporary copy of the style ranges for composition and selection. | 371 // Create a temporary copy of the style ranges for composition and selection. |
374 StyleRanges style_ranges(style_ranges_); | 372 StyleRanges style_ranges(style_ranges_); |
375 ApplyCompositionAndSelectionStyles(&style_ranges); | 373 ApplyCompositionAndSelectionStyles(&style_ranges); |
376 | 374 |
377 // Draw the text. | 375 // Draw the text. |
378 Rect bounds(display_rect_); | 376 Rect bounds(display_rect_); |
379 bounds.Offset(display_offset_); | 377 bounds.Offset(GetUpdatedDisplayOffset()); |
380 for (StyleRanges::const_iterator i = style_ranges.begin(); | 378 for (StyleRanges::const_iterator i = style_ranges.begin(); |
381 i < style_ranges.end(); ++i) { | 379 i < style_ranges.end(); ++i) { |
382 const Font& font = !i->underline ? i->font : | 380 const Font& font = !i->underline ? i->font : |
383 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED); | 381 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED); |
384 string16 text = text_.substr(i->range.start(), i->range.length()); | 382 string16 text = text_.substr(i->range.start(), i->range.length()); |
385 bounds.set_width(font.GetStringWidth(text)); | 383 bounds.set_width(font.GetStringWidth(text)); |
386 canvas->DrawStringInt(text, font, i->foreground, bounds); | 384 canvas->DrawStringInt(text, font, i->foreground, bounds); |
387 | 385 |
388 // Draw the strikethrough. | 386 // Draw the strikethrough. |
389 if (i->strike) { | 387 if (i->strike) { |
390 SkPaint paint; | 388 SkPaint paint; |
391 paint.setAntiAlias(true); | 389 paint.setAntiAlias(true); |
392 paint.setStyle(SkPaint::kFill_Style); | 390 paint.setStyle(SkPaint::kFill_Style); |
393 paint.setColor(i->foreground); | 391 paint.setColor(i->foreground); |
394 paint.setStrokeWidth(kStrikeWidth); | 392 paint.setStrokeWidth(kStrikeWidth); |
395 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()), | 393 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()), |
396 SkIntToScalar(bounds.bottom()), | 394 SkIntToScalar(bounds.bottom()), |
397 SkIntToScalar(bounds.right()), | 395 SkIntToScalar(bounds.right()), |
398 SkIntToScalar(bounds.y()), | 396 SkIntToScalar(bounds.y()), |
399 paint); | 397 paint); |
400 } | 398 } |
401 | 399 |
402 bounds.set_x(bounds.x() + bounds.width()); | 400 bounds.set_x(bounds.x() + bounds.width()); |
403 } | 401 } |
404 | 402 |
405 // Paint cursor. Replace cursor is drawn as rectangle for now. | 403 // Paint cursor. Replace cursor is drawn as rectangle for now. |
406 if (cursor_visible() && focused()) { | 404 Rect cursor(GetUpdatedCursorBounds()); |
407 bounds = CursorBounds(); | 405 if (cursor_visible() && focused() && !cursor.IsEmpty()) |
408 bounds.Offset(display_offset_); | 406 canvas->DrawRectInt(kCursorColor, cursor.x(), cursor.y(), |
409 if (!bounds.IsEmpty()) | 407 cursor.width(), cursor.height()); |
410 canvas->DrawRectInt(kCursorColor, | |
411 bounds.x(), | |
412 bounds.y(), | |
413 bounds.width(), | |
414 bounds.height()); | |
415 } | |
416 } | 408 } |
417 | 409 |
418 SelectionModel RenderText::FindCursorPosition(const Point& point) { | 410 SelectionModel RenderText::FindCursorPosition(const Point& point) { |
419 const Font& font = default_style_.font; | 411 const Font& font = default_style_.font; |
420 int left = 0; | 412 int left = 0; |
421 int left_pos = 0; | 413 int left_pos = 0; |
422 int right = font.GetStringWidth(text()); | 414 int right = font.GetStringWidth(text()); |
423 int right_pos = text().length(); | 415 int right_pos = text().length(); |
424 | 416 |
425 int x = point.x(); | 417 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x()); |
426 if (x <= left) return SelectionModel(left_pos); | 418 if (x <= left) return SelectionModel(left_pos); |
427 if (x >= right) return SelectionModel(right_pos); | 419 if (x >= right) return SelectionModel(right_pos); |
428 // binary searching the cursor position. | 420 // binary searching the cursor position. |
429 // TODO(oshima): use the center of character instead of edge. | 421 // TODO(oshima): use the center of character instead of edge. |
430 // Binary search may not work for language like arabic. | 422 // Binary search may not work for language like arabic. |
431 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { | 423 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { |
432 int pivot_pos = left_pos + (right_pos - left_pos) / 2; | 424 int pivot_pos = left_pos + (right_pos - left_pos) / 2; |
433 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); | 425 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); |
434 if (pivot < x) { | 426 if (pivot < x) { |
435 left = pivot; | 427 left = pivot; |
436 left_pos = pivot_pos; | 428 left_pos = pivot_pos; |
437 } else if (pivot == x) { | 429 } else if (pivot == x) { |
438 return SelectionModel(pivot_pos); | 430 return SelectionModel(pivot_pos); |
439 } else { | 431 } else { |
440 right = pivot; | 432 right = pivot; |
441 right_pos = pivot_pos; | 433 right_pos = pivot_pos; |
442 } | 434 } |
443 } | 435 } |
444 return SelectionModel(left_pos); | 436 return SelectionModel(left_pos); |
445 } | 437 } |
446 | 438 |
447 std::vector<Rect> RenderText::GetSubstringBounds( | 439 std::vector<Rect> RenderText::GetSubstringBounds( |
448 size_t from, size_t to) const { | 440 size_t from, size_t to) { |
449 size_t start = std::min(from, to); | 441 size_t start = std::min(from, to); |
450 size_t end = std::max(from, to); | 442 size_t end = std::max(from, to); |
451 const Font& font = default_style_.font; | 443 const Font& font = default_style_.font; |
452 int start_x = font.GetStringWidth(text().substr(0, start)); | 444 int start_x = font.GetStringWidth(text().substr(0, start)); |
453 int end_x = font.GetStringWidth(text().substr(0, end)); | 445 int end_x = font.GetStringWidth(text().substr(0, end)); |
454 std::vector<Rect> bounds; | 446 Rect rect(start_x, 0, end_x - start_x, font.GetHeight()); |
455 bounds.push_back(Rect(start_x, 0, end_x - start_x, font.GetHeight())); | 447 rect.Offset(display_rect_.origin()); |
456 return bounds; | 448 rect.Offset(GetUpdatedDisplayOffset()); |
| 449 // Center the rect vertically in |display_rect_|. |
| 450 rect.Offset(Point(0, (display_rect_.height() - rect.height()) / 2)); |
| 451 return std::vector<Rect>(1, rect); |
457 } | 452 } |
458 | 453 |
459 Rect RenderText::GetCursorBounds(const SelectionModel& selection, | 454 Rect RenderText::GetCursorBounds(const SelectionModel& selection, |
460 bool insert_mode) { | 455 bool insert_mode) { |
461 size_t cursor_pos = selection.selection_end(); | 456 size_t from = selection.selection_end(); |
462 const Font& font = default_style_.font; | 457 size_t to = insert_mode ? from : std::min(text_.length(), from + 1); |
463 int x = font.GetStringWidth(text_.substr(0U, cursor_pos)); | 458 return GetSubstringBounds(from, to)[0]; |
464 DCHECK_GE(x, 0); | |
465 int h = std::min(display_rect_.height(), font.GetHeight()); | |
466 Rect bounds(x, (display_rect_.height() - h) / 2, 1, h); | |
467 if (!insert_mode && text_.length() != cursor_pos) | |
468 bounds.set_width(font.GetStringWidth(text_.substr(0, cursor_pos + 1)) - x); | |
469 return bounds; | |
470 } | 459 } |
471 | 460 |
472 const Rect& RenderText::CursorBounds() { | 461 const Rect& RenderText::GetUpdatedCursorBounds() { |
473 if (cursor_bounds_valid_ == false) { | 462 UpdateCachedBoundsAndOffset(); |
474 UpdateCursorBoundsAndDisplayOffset(); | |
475 cursor_bounds_valid_ = true; | |
476 } | |
477 return cursor_bounds_; | 463 return cursor_bounds_; |
478 } | 464 } |
479 | 465 |
480 RenderText::RenderText() | 466 RenderText::RenderText() |
481 : text_(), | 467 : text_(), |
482 selection_model_(), | 468 selection_model_(), |
483 cursor_bounds_(), | 469 cursor_bounds_(), |
484 cursor_bounds_valid_(false), | |
485 cursor_visible_(false), | 470 cursor_visible_(false), |
486 insert_mode_(true), | 471 insert_mode_(true), |
487 composition_range_(), | 472 composition_range_(), |
488 style_ranges_(), | 473 style_ranges_(), |
489 default_style_(), | 474 default_style_(), |
490 display_rect_(), | 475 display_rect_(), |
491 display_offset_() { | 476 display_offset_(), |
| 477 cached_bounds_and_offset_valid_(false) { |
| 478 } |
| 479 |
| 480 const Point& RenderText::GetUpdatedDisplayOffset() { |
| 481 UpdateCachedBoundsAndOffset(); |
| 482 return display_offset_; |
492 } | 483 } |
493 | 484 |
494 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, | 485 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, |
495 BreakType break_type) { | 486 BreakType break_type) { |
496 if (break_type == LINE_BREAK) | 487 if (break_type == LINE_BREAK) |
497 return SelectionModel(0, 0, SelectionModel::LEADING); | 488 return SelectionModel(0, 0, SelectionModel::LEADING); |
498 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), | 489 size_t pos = std::max(static_cast<long>(current.selection_end() - 1), |
499 static_cast<long>(0)); | 490 static_cast<long>(0)); |
500 if (break_type == CHARACTER_BREAK) | 491 if (break_type == CHARACTER_BREAK) |
501 return SelectionModel(pos, pos, SelectionModel::LEADING); | 492 return SelectionModel(pos, pos, SelectionModel::LEADING); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
577 selection_style.range.set_end(MaxOfSelection()); | 568 selection_style.range.set_end(MaxOfSelection()); |
578 ApplyStyleRangeImpl(style_ranges, selection_style); | 569 ApplyStyleRangeImpl(style_ranges, selection_style); |
579 } | 570 } |
580 } | 571 } |
581 | 572 |
582 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { | 573 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { |
583 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || | 574 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || |
584 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); | 575 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); |
585 } | 576 } |
586 | 577 |
587 void RenderText::UpdateCursorBoundsAndDisplayOffset() { | 578 void RenderText::UpdateCachedBoundsAndOffset() { |
588 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode()); | 579 if (cached_bounds_and_offset_valid_) |
| 580 return; |
| 581 // First, set the valid flag true to calculate the current cursor bounds using |
| 582 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| 583 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
| 584 cached_bounds_and_offset_valid_ = true; |
| 585 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
| 586 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1)); |
589 // Update |display_offset_| to ensure the current cursor is visible. | 587 // Update |display_offset_| to ensure the current cursor is visible. |
590 int display_width = display_rect_.width(); | 588 int display_width = display_rect_.width(); |
591 int string_width = GetStringWidth(); | 589 int string_width = GetStringWidth(); |
| 590 int delta_offset = 0; |
592 if (string_width < display_width) { | 591 if (string_width < display_width) { |
593 // Show all text whenever the text fits to the size. | 592 // Show all text whenever the text fits to the size. |
594 display_offset_.set_x(0); | 593 delta_offset = -display_offset_.x(); |
595 } else if ((display_offset_.x() + cursor_bounds_.right()) > display_width) { | 594 } else if (cursor_bounds_.right() > display_rect_.right()) { |
596 // Pan to show the cursor when it overflows to the right, | 595 // Pan to show the cursor when it overflows to the right, |
597 display_offset_.set_x(display_width - cursor_bounds_.right()); | 596 delta_offset = display_rect_.right() - cursor_bounds_.right(); |
598 } else if ((display_offset_.x() + cursor_bounds_.x()) < 0) { | 597 } else if (cursor_bounds_.x() < display_rect_.x()) { |
599 // Pan to show the cursor when it overflows to the left. | 598 // Pan to show the cursor when it overflows to the left. |
600 display_offset_.set_x(-cursor_bounds_.x()); | 599 delta_offset = display_rect_.x() - cursor_bounds_.x(); |
601 } | 600 } |
| 601 display_offset_.Offset(delta_offset, 0); |
| 602 cursor_bounds_.Offset(delta_offset, 0); |
602 } | 603 } |
603 | 604 |
604 } // namespace gfx | 605 } // namespace gfx |
OLD | NEW |