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) { |
(...skipping 17 matching lines...) Expand all Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |