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

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: Update comments. 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) {
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
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(size_t from, size_t to) {
448 size_t from, size_t to) const {
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 // First, set the valid flag true to calculate the current cursor bounds using
581 // the stale |display_offset_|. Applying |delta_offset| at the end of this
582 // function will set |cursor_bounds_| and |display_offset_| to correct values.
583 cached_bounds_and_offset_valid_ = true;
584 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
585 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1));
589 // Update |display_offset_| to ensure the current cursor is visible. 586 // Update |display_offset_| to ensure the current cursor is visible.
590 int display_width = display_rect_.width(); 587 int display_width = display_rect_.width();
591 int string_width = GetStringWidth(); 588 int string_width = GetStringWidth();
589 int delta_offset = 0;
592 if (string_width < display_width) { 590 if (string_width < display_width) {
593 // Show all text whenever the text fits to the size. 591 // Show all text whenever the text fits to the size.
594 display_offset_.set_x(0); 592 delta_offset = -display_offset_.x();
595 } else if ((display_offset_.x() + cursor_bounds_.right()) > display_width) { 593 } else if (cursor_bounds_.right() > display_rect_.right()) {
596 // Pan to show the cursor when it overflows to the right, 594 // Pan to show the cursor when it overflows to the right,
597 display_offset_.set_x(display_width - cursor_bounds_.right()); 595 delta_offset = display_rect_.right() - cursor_bounds_.right();
598 } else if ((display_offset_.x() + cursor_bounds_.x()) < 0) { 596 } else if (cursor_bounds_.x() < display_rect_.x()) {
599 // Pan to show the cursor when it overflows to the left. 597 // Pan to show the cursor when it overflows to the left.
600 display_offset_.set_x(-cursor_bounds_.x()); 598 delta_offset = display_rect_.x() - cursor_bounds_.x();
601 } 599 }
600 display_offset_.Offset(delta_offset, 0);
601 cursor_bounds_.Offset(delta_offset, 0);
602 } 602 }
603 603
604 } // namespace gfx 604 } // 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