Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(583)

Side by Side Diff: ui/gfx/render_text.cc

Issue 7466048: Fix RenderText cached bounds and offset logic; update clients. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Expand touch_selection_controller_impl_unittest textfield bounds to avoid unexpected text overrun. Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/gfx/render_text.h ('k') | views/controls/textfield/native_textfield_views.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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) {
(...skipping 17 matching lines...) Expand all
164 } 160 }
165 161
166 void RenderText::SetSelectionModel(const SelectionModel& sel) { 162 void RenderText::SetSelectionModel(const SelectionModel& sel) {
167 size_t start = sel.selection_start(); 163 size_t start = sel.selection_start();
168 size_t end = sel.selection_end(); 164 size_t end = sel.selection_end();
169 selection_model_.set_selection_start(std::min(start, text().length())); 165 selection_model_.set_selection_start(std::min(start, text().length()));
170 selection_model_.set_selection_end(std::min(end, text().length())); 166 selection_model_.set_selection_end(std::min(end, text().length()));
171 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length())); 167 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length()));
172 selection_model_.set_caret_placement(sel.caret_placement()); 168 selection_model_.set_caret_placement(sel.caret_placement());
173 169
174 cursor_bounds_valid_ = false; 170 cached_bounds_and_offset_valid_ = false;
171 }
172
173 void RenderText::SetDisplayRect(const Rect& r) {
174 display_rect_ = r;
175 cached_bounds_and_offset_valid_ = false;
xji 2011/08/10 00:36:15 does it make sense to invalidate this in SetText()
msw 2011/08/10 01:27:39 Done.
175 } 176 }
176 177
177 size_t RenderText::GetCursorPosition() const { 178 size_t RenderText::GetCursorPosition() const {
178 return selection_model_.selection_end(); 179 return selection_model_.selection_end();
179 } 180 }
180 181
181 void RenderText::SetCursorPosition(const size_t position) { 182 void RenderText::SetCursorPosition(const size_t position) {
182 SelectionModel sel(selection_model()); 183 SelectionModel sel(selection_model());
183 sel.set_selection_start(position); 184 sel.set_selection_start(position);
184 sel.set_selection_end(position); 185 sel.set_selection_end(position);
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 void RenderText::ApplyStyleRange(StyleRange style_range) { 322 void RenderText::ApplyStyleRange(StyleRange style_range) {
322 const ui::Range& new_range = style_range.range; 323 const ui::Range& new_range = style_range.range;
323 if (!new_range.IsValid() || new_range.is_empty()) 324 if (!new_range.IsValid() || new_range.is_empty())
324 return; 325 return;
325 CHECK(!new_range.is_reversed()); 326 CHECK(!new_range.is_reversed());
326 CHECK(ui::Range(0, text_.length()).Contains(new_range)); 327 CHECK(ui::Range(0, text_.length()).Contains(new_range));
327 ApplyStyleRangeImpl(&style_ranges_, style_range); 328 ApplyStyleRangeImpl(&style_ranges_, style_range);
328 #ifndef NDEBUG 329 #ifndef NDEBUG
329 CheckStyleRanges(style_ranges_, text_.length()); 330 CheckStyleRanges(style_ranges_, text_.length());
330 #endif 331 #endif
331 // TODO(xji): only invalidate cursor_bounds if font or underline change. 332 // TODO(xji): only invalidate if font or underline changes.
332 cursor_bounds_valid_ = false; 333 cached_bounds_and_offset_valid_ = false;
333 } 334 }
334 335
335 void RenderText::ApplyDefaultStyle() { 336 void RenderText::ApplyDefaultStyle() {
336 style_ranges_.clear(); 337 style_ranges_.clear();
337 StyleRange style = StyleRange(default_style_); 338 StyleRange style = StyleRange(default_style_);
338 style.range.set_end(text_.length()); 339 style.range.set_end(text_.length());
339 style_ranges_.push_back(style); 340 style_ranges_.push_back(style);
340 cursor_bounds_valid_ = false; 341 cached_bounds_and_offset_valid_ = false;
341 } 342 }
342 343
343 base::i18n::TextDirection RenderText::GetTextDirection() const { 344 base::i18n::TextDirection RenderText::GetTextDirection() const {
344 // TODO(msw): Bidi implementation, intended to replace the functionality added 345 // TODO(msw): Bidi implementation, intended to replace the functionality added
345 // in crrev.com/91881 (discussed in codereview.chromium.org/7324011). 346 // in crrev.com/91881 (discussed in codereview.chromium.org/7324011).
346 return base::i18n::LEFT_TO_RIGHT; 347 return base::i18n::LEFT_TO_RIGHT;
347 } 348 }
348 349
349 int RenderText::GetStringWidth() { 350 int RenderText::GetStringWidth() {
350 return GetSubstringBounds(0, text_.length())[0].width(); 351 return default_style_.font.GetStringWidth(text());
351 } 352 }
352 353
353 void RenderText::Draw(Canvas* canvas) { 354 void RenderText::Draw(Canvas* canvas) {
354 // Clip the canvas to the text display area. 355 // Clip the canvas to the text display area.
355 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), 356 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(),
356 display_rect_.width(), display_rect_.height()); 357 display_rect_.width(), display_rect_.height());
357 358
358 // Draw the selection. 359 // Draw the selection.
359 std::vector<Rect> selection(GetSubstringBounds(GetSelectionStart(), 360 std::vector<Rect> selection(GetSubstringBounds(GetSelectionStart(),
360 GetCursorPosition())); 361 GetCursorPosition()));
361 SkColor selection_color = 362 SkColor selection_color =
362 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; 363 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
363 for (std::vector<Rect>::const_iterator i = selection.begin(); 364 for (std::vector<Rect>::const_iterator i = selection.begin();
364 i < selection.end(); ++i) { 365 i < selection.end(); ++i) {
365 Rect r(*i); 366 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()); 367 canvas->FillRectInt(selection_color, r.x(), r.y(), r.width(), r.height());
371 } 368 }
372 369
373 // Create a temporary copy of the style ranges for composition and selection. 370 // Create a temporary copy of the style ranges for composition and selection.
374 StyleRanges style_ranges(style_ranges_); 371 StyleRanges style_ranges(style_ranges_);
375 ApplyCompositionAndSelectionStyles(&style_ranges); 372 ApplyCompositionAndSelectionStyles(&style_ranges);
376 373
377 // Draw the text. 374 // Draw the text.
378 Rect bounds(display_rect_); 375 Rect bounds(display_rect_);
379 bounds.Offset(display_offset_); 376 bounds.Offset(GetUpdatedDisplayOffset());
380 for (StyleRanges::const_iterator i = style_ranges.begin(); 377 for (StyleRanges::const_iterator i = style_ranges.begin();
381 i < style_ranges.end(); ++i) { 378 i < style_ranges.end(); ++i) {
382 const Font& font = !i->underline ? i->font : 379 const Font& font = !i->underline ? i->font :
383 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED); 380 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED);
384 string16 text = text_.substr(i->range.start(), i->range.length()); 381 string16 text = text_.substr(i->range.start(), i->range.length());
385 bounds.set_width(font.GetStringWidth(text)); 382 bounds.set_width(font.GetStringWidth(text));
386 canvas->DrawStringInt(text, font, i->foreground, bounds); 383 canvas->DrawStringInt(text, font, i->foreground, bounds);
387 384
388 // Draw the strikethrough. 385 // Draw the strikethrough.
389 if (i->strike) { 386 if (i->strike) {
390 SkPaint paint; 387 SkPaint paint;
391 paint.setAntiAlias(true); 388 paint.setAntiAlias(true);
392 paint.setStyle(SkPaint::kFill_Style); 389 paint.setStyle(SkPaint::kFill_Style);
393 paint.setColor(i->foreground); 390 paint.setColor(i->foreground);
394 paint.setStrokeWidth(kStrikeWidth); 391 paint.setStrokeWidth(kStrikeWidth);
395 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()), 392 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(bounds.x()),
396 SkIntToScalar(bounds.bottom()), 393 SkIntToScalar(bounds.bottom()),
397 SkIntToScalar(bounds.right()), 394 SkIntToScalar(bounds.right()),
398 SkIntToScalar(bounds.y()), 395 SkIntToScalar(bounds.y()),
399 paint); 396 paint);
400 } 397 }
401 398
402 bounds.set_x(bounds.x() + bounds.width()); 399 bounds.set_x(bounds.x() + bounds.width());
403 } 400 }
404 401
405 // Paint cursor. Replace cursor is drawn as rectangle for now. 402 // Paint cursor. Replace cursor is drawn as rectangle for now.
406 if (cursor_visible() && focused()) { 403 Rect cursor(GetUpdatedCursorBounds());
407 bounds = CursorBounds(); 404 if (cursor_visible() && focused() && !cursor.IsEmpty())
408 bounds.Offset(display_offset_); 405 canvas->DrawRectInt(kCursorColor, cursor.x(), cursor.y(),
409 if (!bounds.IsEmpty()) 406 cursor.width(), cursor.height());
410 canvas->DrawRectInt(kCursorColor,
411 bounds.x(),
412 bounds.y(),
413 bounds.width(),
414 bounds.height());
415 }
416 } 407 }
417 408
418 SelectionModel RenderText::FindCursorPosition(const Point& point) { 409 SelectionModel RenderText::FindCursorPosition(const Point& point) {
419 const Font& font = default_style_.font; 410 const Font& font = default_style_.font;
420 int left = 0; 411 int left = 0;
421 int left_pos = 0; 412 int left_pos = 0;
422 int right = font.GetStringWidth(text()); 413 int right = font.GetStringWidth(text());
423 int right_pos = text().length(); 414 int right_pos = text().length();
424 415
425 int x = point.x(); 416 int x = point.x() - (display_rect_.x() + GetUpdatedDisplayOffset().x());
426 if (x <= left) return SelectionModel(left_pos); 417 if (x <= left) return SelectionModel(left_pos);
427 if (x >= right) return SelectionModel(right_pos); 418 if (x >= right) return SelectionModel(right_pos);
428 // binary searching the cursor position. 419 // binary searching the cursor position.
429 // TODO(oshima): use the center of character instead of edge. 420 // TODO(oshima): use the center of character instead of edge.
430 // Binary search may not work for language like arabic. 421 // Binary search may not work for language like arabic.
431 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) { 422 while (std::abs(static_cast<long>(right_pos - left_pos)) > 1) {
432 int pivot_pos = left_pos + (right_pos - left_pos) / 2; 423 int pivot_pos = left_pos + (right_pos - left_pos) / 2;
433 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); 424 int pivot = font.GetStringWidth(text().substr(0, pivot_pos));
434 if (pivot < x) { 425 if (pivot < x) {
435 left = pivot; 426 left = pivot;
436 left_pos = pivot_pos; 427 left_pos = pivot_pos;
437 } else if (pivot == x) { 428 } else if (pivot == x) {
438 return SelectionModel(pivot_pos); 429 return SelectionModel(pivot_pos);
439 } else { 430 } else {
440 right = pivot; 431 right = pivot;
441 right_pos = pivot_pos; 432 right_pos = pivot_pos;
442 } 433 }
443 } 434 }
444 return SelectionModel(left_pos); 435 return SelectionModel(left_pos);
445 } 436 }
446 437
447 std::vector<Rect> RenderText::GetSubstringBounds( 438 std::vector<Rect> RenderText::GetSubstringBounds(
448 size_t from, size_t to) const { 439 size_t from, size_t to) {
449 size_t start = std::min(from, to); 440 size_t start = std::min(from, to);
450 size_t end = std::max(from, to); 441 size_t end = std::max(from, to);
451 const Font& font = default_style_.font; 442 const Font& font = default_style_.font;
452 int start_x = font.GetStringWidth(text().substr(0, start)); 443 int start_x = font.GetStringWidth(text().substr(0, start));
453 int end_x = font.GetStringWidth(text().substr(0, end)); 444 int end_x = font.GetStringWidth(text().substr(0, end));
454 std::vector<Rect> bounds; 445 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())); 446 rect.Offset(display_rect_.origin());
456 return bounds; 447 rect.Offset(GetUpdatedDisplayOffset());
448 // Center the rect vertically in |display_rect_|.
449 rect.Offset(Point(0, (display_rect_.height() - rect.height()) / 2));
450 return std::vector<Rect>(1, rect);
457 } 451 }
458 452
459 Rect RenderText::GetCursorBounds(const SelectionModel& selection, 453 Rect RenderText::GetCursorBounds(const SelectionModel& selection,
460 bool insert_mode) { 454 bool insert_mode) {
461 size_t cursor_pos = selection.selection_end(); 455 size_t from = selection.selection_end();
462 const Font& font = default_style_.font; 456 size_t to = insert_mode ? from : std::min(text_.length(), from + 1);
463 int x = font.GetStringWidth(text_.substr(0U, cursor_pos)); 457 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 } 458 }
471 459
472 const Rect& RenderText::CursorBounds() { 460 const Rect& RenderText::GetUpdatedCursorBounds() {
473 if (cursor_bounds_valid_ == false) { 461 UpdateCachedBoundsAndOffset();
474 UpdateCursorBoundsAndDisplayOffset();
475 cursor_bounds_valid_ = true;
476 }
477 return cursor_bounds_; 462 return cursor_bounds_;
478 } 463 }
479 464
480 RenderText::RenderText() 465 RenderText::RenderText()
481 : text_(), 466 : text_(),
482 selection_model_(), 467 selection_model_(),
483 cursor_bounds_(), 468 cursor_bounds_(),
484 cursor_bounds_valid_(false),
485 cursor_visible_(false), 469 cursor_visible_(false),
486 insert_mode_(true), 470 insert_mode_(true),
487 composition_range_(), 471 composition_range_(),
488 style_ranges_(), 472 style_ranges_(),
489 default_style_(), 473 default_style_(),
490 display_rect_(), 474 display_rect_(),
491 display_offset_() { 475 display_offset_(),
476 cached_bounds_and_offset_valid_(false) {
477 }
478
479 const Point& RenderText::GetUpdatedDisplayOffset() {
480 UpdateCachedBoundsAndOffset();
481 return display_offset_;
492 } 482 }
493 483
494 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current, 484 SelectionModel RenderText::GetLeftSelectionModel(const SelectionModel& current,
495 BreakType break_type) { 485 BreakType break_type) {
496 if (break_type == LINE_BREAK) 486 if (break_type == LINE_BREAK)
497 return SelectionModel(0, 0, SelectionModel::LEADING); 487 return SelectionModel(0, 0, SelectionModel::LEADING);
498 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),
499 static_cast<long>(0)); 489 static_cast<long>(0));
500 if (break_type == CHARACTER_BREAK) 490 if (break_type == CHARACTER_BREAK)
501 return SelectionModel(pos, pos, SelectionModel::LEADING); 491 return SelectionModel(pos, pos, SelectionModel::LEADING);
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 selection_style.range.set_end(MaxOfSelection()); 567 selection_style.range.set_end(MaxOfSelection());
578 ApplyStyleRangeImpl(style_ranges, selection_style); 568 ApplyStyleRangeImpl(style_ranges, selection_style);
579 } 569 }
580 } 570 }
581 571
582 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { 572 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) {
583 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || 573 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) ||
584 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); 574 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos]));
585 } 575 }
586 576
587 void RenderText::UpdateCursorBoundsAndDisplayOffset() { 577 void RenderText::UpdateCachedBoundsAndOffset() {
588 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode()); 578 if (cached_bounds_and_offset_valid_)
579 return;
580 cached_bounds_and_offset_valid_ = true;
581 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
582 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1));
589 // Update |display_offset_| to ensure the current cursor is visible. 583 // Update |display_offset_| to ensure the current cursor is visible.
590 int display_width = display_rect_.width(); 584 int display_width = display_rect_.width();
591 int string_width = GetStringWidth(); 585 int string_width = GetStringWidth();
586 int delta_offset = 0;
592 if (string_width < display_width) { 587 if (string_width < display_width) {
593 // Show all text whenever the text fits to the size. 588 // Show all text whenever the text fits to the size.
594 display_offset_.set_x(0); 589 delta_offset = -display_offset_.x();
595 } else if ((display_offset_.x() + cursor_bounds_.right()) > display_width) { 590 } else if (cursor_bounds_.right() > display_rect_.right()) {
596 // Pan to show the cursor when it overflows to the right, 591 // Pan to show the cursor when it overflows to the right,
597 display_offset_.set_x(display_width - cursor_bounds_.right()); 592 delta_offset = display_rect_.right() - cursor_bounds_.right();
598 } else if ((display_offset_.x() + cursor_bounds_.x()) < 0) { 593 } else if (cursor_bounds_.x() < display_rect_.x()) {
599 // Pan to show the cursor when it overflows to the left. 594 // Pan to show the cursor when it overflows to the left.
600 display_offset_.set_x(-cursor_bounds_.x()); 595 delta_offset = display_rect_.x() - cursor_bounds_.x();
601 } 596 }
597 display_offset_.Offset(delta_offset, 0);
598 cursor_bounds_.Offset(delta_offset, 0);
xji 2011/08/10 00:36:15 In the case of both cursor_bounds_ and display_off
msw 2011/08/10 01:27:39 Your first paragraph is correct, I've added a comm
xji 2011/08/10 18:15:54 I looked at GetCursorBounds() again. And I think y
602 } 599 }
603 600
604 } // namespace gfx 601 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text.h ('k') | views/controls/textfield/native_textfield_views.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698