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

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

Issue 924543004: adding baseline options for super/sub scripting (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moved RestoreBreakList to file local function Created 5 years, 10 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 #include <climits> 8 #include <climits>
9 9
10 #include "base/command_line.h" 10 #include "base/command_line.h"
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 canvas_->DrawLine(start_, end, paint_); 343 canvas_->DrawLine(start_, end, paint_);
344 344
345 if (clipped) 345 if (clipped)
346 canvas_->Restore(); 346 canvas_->Restore();
347 347
348 x += pieces_[i].first; 348 x += pieces_[i].first;
349 } 349 }
350 } 350 }
351 351
352 StyleIterator::StyleIterator(const BreakList<SkColor>& colors, 352 StyleIterator::StyleIterator(const BreakList<SkColor>& colors,
353 const BreakList<BaselineStyle>& baselines,
353 const std::vector<BreakList<bool> >& styles) 354 const std::vector<BreakList<bool> >& styles)
354 : colors_(colors), 355 : colors_(colors),
356 baselines_(baselines),
355 styles_(styles) { 357 styles_(styles) {
356 color_ = colors_.breaks().begin(); 358 color_ = colors_.breaks().begin();
359 baseline_ = baselines_.breaks().begin();
357 for (size_t i = 0; i < styles_.size(); ++i) 360 for (size_t i = 0; i < styles_.size(); ++i)
358 style_.push_back(styles_[i].breaks().begin()); 361 style_.push_back(styles_[i].breaks().begin());
359 } 362 }
360 363
361 StyleIterator::~StyleIterator() {} 364 StyleIterator::~StyleIterator() {}
362 365
363 Range StyleIterator::GetRange() const { 366 Range StyleIterator::GetRange() const {
364 Range range(colors_.GetRange(color_)); 367 Range range(colors_.GetRange(color_));
368 range = range.Intersect(baselines_.GetRange(baseline_));
365 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) 369 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
366 range = range.Intersect(styles_[i].GetRange(style_[i])); 370 range = range.Intersect(styles_[i].GetRange(style_[i]));
367 return range; 371 return range;
368 } 372 }
369 373
370 void StyleIterator::UpdatePosition(size_t position) { 374 void StyleIterator::UpdatePosition(size_t position) {
371 color_ = colors_.GetBreak(position); 375 color_ = colors_.GetBreak(position);
376 baseline_ = baselines_.GetBreak(position);
372 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) 377 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
373 style_[i] = styles_[i].GetBreak(position); 378 style_[i] = styles_[i].GetBreak(position);
374 } 379 }
375 380
376 LineSegment::LineSegment() : width(0), run(0) {} 381 LineSegment::LineSegment() : width(0), run(0) {}
377 382
378 LineSegment::~LineSegment() {} 383 LineSegment::~LineSegment() {}
379 384
380 Line::Line() : preceding_heights(0), baseline(0) {} 385 Line::Line() : preceding_heights(0), baseline(0) {}
381 386
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 RenderText* RenderText::CreateInstanceForEditing() { 422 RenderText* RenderText::CreateInstanceForEditing() {
418 return new RenderTextHarfBuzz; 423 return new RenderTextHarfBuzz;
419 } 424 }
420 425
421 void RenderText::SetText(const base::string16& text) { 426 void RenderText::SetText(const base::string16& text) {
422 DCHECK(!composition_range_.IsValid()); 427 DCHECK(!composition_range_.IsValid());
423 if (text_ == text) 428 if (text_ == text)
424 return; 429 return;
425 text_ = text; 430 text_ = text;
426 431
427 // Adjust ranged styles and colors to accommodate a new text length. 432 // Adjust ranged styles, baselines, and colors to accommodate a new text
428 // Clear style ranges as they might break new text graphemes and apply 433 // length. Clear style ranges as they might break new text graphemes and apply
429 // the first style to the whole text instead. 434 // the first style to the whole text instead.
430 const size_t text_length = text_.length(); 435 const size_t text_length = text_.length();
431 colors_.SetMax(text_length); 436 colors_.SetMax(text_length);
437 baselines_.SetValue(baselines_.breaks().begin()->second);
438 baselines_.SetMax(text_length);
432 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) { 439 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) {
433 BreakList<bool>& break_list = styles_[style]; 440 BreakList<bool>& break_list = styles_[style];
434 break_list.SetValue(break_list.breaks().begin()->second); 441 break_list.SetValue(break_list.breaks().begin()->second);
435 break_list.SetMax(text_length); 442 break_list.SetMax(text_length);
436 } 443 }
437 cached_bounds_and_offset_valid_ = false; 444 cached_bounds_and_offset_valid_ = false;
438 445
439 // Reset selection model. SetText should always followed by SetSelectionModel 446 // Reset selection model. SetText should always followed by SetSelectionModel
440 // or SetCursorPosition in upper layer. 447 // or SetCursorPosition in upper layer.
441 SetSelectionModel(SelectionModel()); 448 SetSelectionModel(SelectionModel());
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
664 } 671 }
665 672
666 void RenderText::SetColor(SkColor value) { 673 void RenderText::SetColor(SkColor value) {
667 colors_.SetValue(value); 674 colors_.SetValue(value);
668 } 675 }
669 676
670 void RenderText::ApplyColor(SkColor value, const Range& range) { 677 void RenderText::ApplyColor(SkColor value, const Range& range) {
671 colors_.ApplyValue(value, range); 678 colors_.ApplyValue(value, range);
672 } 679 }
673 680
681 void RenderText::SetBaselineStyle(BaselineStyle value) {
682 baselines_.SetValue(value);
683 }
684
685 void RenderText::ApplyBaselineStyle(BaselineStyle value, const Range& range) {
686 baselines_.ApplyValue(value, range);
687 }
688
674 void RenderText::SetStyle(TextStyle style, bool value) { 689 void RenderText::SetStyle(TextStyle style, bool value) {
675 styles_[style].SetValue(value); 690 styles_[style].SetValue(value);
676 691
677 cached_bounds_and_offset_valid_ = false; 692 cached_bounds_and_offset_valid_ = false;
678 // TODO(oshima|msw): Not all style change requires layout changes. 693 // TODO(oshima|msw): Not all style change requires layout changes.
679 // Consider optimizing based on the type of change. 694 // Consider optimizing based on the type of change.
680 OnLayoutTextAttributeChanged(false); 695 OnLayoutTextAttributeChanged(false);
681 } 696 }
682 697
683 void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) { 698 void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) {
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
904 text_direction_(base::i18n::UNKNOWN_DIRECTION), 919 text_direction_(base::i18n::UNKNOWN_DIRECTION),
905 cursor_enabled_(true), 920 cursor_enabled_(true),
906 cursor_visible_(false), 921 cursor_visible_(false),
907 insert_mode_(true), 922 insert_mode_(true),
908 cursor_color_(kDefaultColor), 923 cursor_color_(kDefaultColor),
909 selection_color_(kDefaultColor), 924 selection_color_(kDefaultColor),
910 selection_background_focused_color_(kDefaultSelectionBackgroundColor), 925 selection_background_focused_color_(kDefaultSelectionBackgroundColor),
911 focused_(false), 926 focused_(false),
912 composition_range_(Range::InvalidRange()), 927 composition_range_(Range::InvalidRange()),
913 colors_(kDefaultColor), 928 colors_(kDefaultColor),
929 baselines_(NORMAL_BASELINE),
914 styles_(NUM_TEXT_STYLES), 930 styles_(NUM_TEXT_STYLES),
915 composition_and_selection_styles_applied_(false), 931 composition_and_selection_styles_applied_(false),
916 obscured_(false), 932 obscured_(false),
917 obscured_reveal_index_(-1), 933 obscured_reveal_index_(-1),
918 truncate_length_(0), 934 truncate_length_(0),
919 elide_behavior_(NO_ELIDE), 935 elide_behavior_(NO_ELIDE),
920 text_elided_(false), 936 text_elided_(false),
921 min_line_height_(0), 937 min_line_height_(0),
922 multiline_(false), 938 multiline_(false),
923 background_is_transparent_(false), 939 background_is_transparent_(false),
(...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after
1260 } 1276 }
1261 } 1277 }
1262 static const base::char16 kNewline[] = { '\n', 0 }; 1278 static const base::char16 kNewline[] = { '\n', 0 };
1263 static const base::char16 kNewlineSymbol[] = { 0x2424, 0 }; 1279 static const base::char16 kNewlineSymbol[] = { 0x2424, 0 };
1264 if (!multiline_) 1280 if (!multiline_)
1265 base::ReplaceChars(layout_text_, kNewline, kNewlineSymbol, &layout_text_); 1281 base::ReplaceChars(layout_text_, kNewline, kNewlineSymbol, &layout_text_);
1266 1282
1267 OnLayoutTextAttributeChanged(true); 1283 OnLayoutTextAttributeChanged(true);
1268 } 1284 }
1269 1285
1286 namespace {
msw 2015/02/19 02:50:57 Don't declare an anonymous namespace in the middle
dschuyler 2015/02/25 02:23:06 Done.
1287
1288 // Make sure ranges don't break text graphemes. If a range in |break_list|
1289 // does break a grapheme in |render_text|, the range will be slightly
1290 // extended to encompass the grapheme.
1291 template <typename T>
1292 void RestoreBreakList(RenderText* render_text,
1293 BreakList<T>& break_list) {
1294 break_list.SetMax(render_text->text().length());
1295 Range range;
1296 while (range.end() < break_list.max()) {
1297 const auto& current_break = break_list.GetBreak(range.end());
1298 range = break_list.GetRange(current_break);
1299 if (range.end() < break_list.max() &&
1300 !render_text->IsValidCursorIndex(range.end())) {
1301 range.set_end(render_text->IndexOfAdjacentGrapheme(range.end(),
1302 CURSOR_FORWARD));
1303 break_list.ApplyValue(current_break->second, range);
1304 }
1305 }
1306 }
1307
1308 } // namespace.
1309
1270 base::string16 RenderText::Elide(const base::string16& text, 1310 base::string16 RenderText::Elide(const base::string16& text,
1271 float text_width, 1311 float text_width,
1272 float available_width, 1312 float available_width,
1273 ElideBehavior behavior) { 1313 ElideBehavior behavior) {
1274 if (available_width <= 0 || text.empty()) 1314 if (available_width <= 0 || text.empty())
1275 return base::string16(); 1315 return base::string16();
1276 if (behavior == ELIDE_EMAIL) 1316 if (behavior == ELIDE_EMAIL)
1277 return ElideEmail(text, available_width); 1317 return ElideEmail(text, available_width);
1278 if (text_width > 0 && text_width < available_width) 1318 if (text_width > 0 && text_width < available_width)
1279 return text; 1319 return text;
1280 1320
1281 TRACE_EVENT0("ui", "RenderText::Elide"); 1321 TRACE_EVENT0("ui", "RenderText::Elide");
1282 1322
1283 // Create a RenderText copy with attributes that affect the rendering width. 1323 // Create a RenderText copy with attributes that affect the rendering width.
1284 scoped_ptr<RenderText> render_text = CreateInstanceOfSameType(); 1324 scoped_ptr<RenderText> render_text = CreateInstanceOfSameType();
1285 render_text->SetFontList(font_list_); 1325 render_text->SetFontList(font_list_);
1286 render_text->SetDirectionalityMode(directionality_mode_); 1326 render_text->SetDirectionalityMode(directionality_mode_);
1287 render_text->SetCursorEnabled(cursor_enabled_); 1327 render_text->SetCursorEnabled(cursor_enabled_);
1288 render_text->set_truncate_length(truncate_length_); 1328 render_text->set_truncate_length(truncate_length_);
1289 render_text->styles_ = styles_; 1329 render_text->styles_ = styles_;
1330 render_text->baselines_ = baselines_;
1290 render_text->colors_ = colors_; 1331 render_text->colors_ = colors_;
1291 if (text_width == 0) { 1332 if (text_width == 0) {
1292 render_text->SetText(text); 1333 render_text->SetText(text);
1293 text_width = render_text->GetContentWidthF(); 1334 text_width = render_text->GetContentWidthF();
1294 } 1335 }
1295 if (text_width <= available_width) 1336 if (text_width <= available_width)
1296 return text; 1337 return text;
1297 1338
1298 const base::string16 ellipsis = base::string16(kEllipsisUTF16); 1339 const base::string16 ellipsis = base::string16(kEllipsisUTF16);
1299 const bool insert_ellipsis = (behavior != TRUNCATE); 1340 const bool insert_ellipsis = (behavior != TRUNCATE);
1300 const bool elide_in_middle = (behavior == ELIDE_MIDDLE); 1341 const bool elide_in_middle = (behavior == ELIDE_MIDDLE);
1301 const bool elide_at_beginning = (behavior == ELIDE_HEAD); 1342 const bool elide_at_beginning = (behavior == ELIDE_HEAD);
1302 1343
1303 if (insert_ellipsis) { 1344 if (insert_ellipsis) {
1304 render_text->SetText(ellipsis); 1345 render_text->SetText(ellipsis);
1305 const float ellipsis_width = render_text->GetContentWidthF(); 1346 const float ellipsis_width = render_text->GetContentWidthF();
1306 if (ellipsis_width > available_width) 1347 if (ellipsis_width > available_width)
1307 return base::string16(); 1348 return base::string16();
1308 } 1349 }
1309 1350
1310 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); 1351 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
1311 1352
1312 // Use binary search to compute the elided text. 1353 // Use binary search to compute the elided text.
1313 size_t lo = 0; 1354 size_t lo = 0;
1314 size_t hi = text.length() - 1; 1355 size_t hi = text.length() - 1;
1315 const base::i18n::TextDirection text_direction = GetTextDirection(text); 1356 const base::i18n::TextDirection text_direction = GetTextDirection(text);
1316 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { 1357 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) {
1317 // Restore colors. They will be truncated to size by SetText. 1358 // Restore colors. They will be truncated to size by SetText.
1318 render_text->colors_ = colors_; 1359 render_text->colors_ = colors_;
1360 render_text->baselines_ = baselines_;
1319 base::string16 new_text = 1361 base::string16 new_text =
1320 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL); 1362 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL);
1321 render_text->SetText(new_text); 1363 render_text->SetText(new_text);
1322 1364
1323 // This has to be an additional step so that the ellipsis is rendered with 1365 // This has to be an additional step so that the ellipsis is rendered with
1324 // same style as trailing part of the text. 1366 // same style as trailing part of the text.
1325 if (insert_ellipsis && behavior == ELIDE_TAIL) { 1367 if (insert_ellipsis && behavior == ELIDE_TAIL) {
1326 // When ellipsis follows text whose directionality is not the same as that 1368 // When ellipsis follows text whose directionality is not the same as that
1327 // of the whole text, it will be rendered with the directionality of the 1369 // of the whole text, it will be rendered with the directionality of the
1328 // whole text. Since we want ellipsis to indicate continuation of the 1370 // whole text. Since we want ellipsis to indicate continuation of the
1329 // preceding text, we force the directionality of ellipsis to be same as 1371 // preceding text, we force the directionality of ellipsis to be same as
1330 // the preceding text using LTR or RTL markers. 1372 // the preceding text using LTR or RTL markers.
1331 base::i18n::TextDirection trailing_text_direction = 1373 base::i18n::TextDirection trailing_text_direction =
1332 base::i18n::GetLastStrongCharacterDirection(new_text); 1374 base::i18n::GetLastStrongCharacterDirection(new_text);
1333 new_text.append(ellipsis); 1375 new_text.append(ellipsis);
1334 if (trailing_text_direction != text_direction) { 1376 if (trailing_text_direction != text_direction) {
1335 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) 1377 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT)
1336 new_text += base::i18n::kLeftToRightMark; 1378 new_text += base::i18n::kLeftToRightMark;
1337 else 1379 else
1338 new_text += base::i18n::kRightToLeftMark; 1380 new_text += base::i18n::kRightToLeftMark;
1339 } 1381 }
1340 render_text->SetText(new_text); 1382 render_text->SetText(new_text);
1341 } 1383 }
1342 1384
1343 // Restore styles. Make sure style ranges don't break new text graphemes. 1385 // Restore styles and baselines without breaking multi-character graphemes.
1344 render_text->styles_ = styles_; 1386 render_text->styles_ = styles_;
1345 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) { 1387 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
1346 BreakList<bool>& break_list = render_text->styles_[style]; 1388 RestoreBreakList(render_text.get(), render_text->styles_[style]);
1347 break_list.SetMax(render_text->text_.length()); 1389 RestoreBreakList(render_text.get(), baselines_);
1348 Range range;
1349 while (range.end() < break_list.max()) {
1350 BreakList<bool>::const_iterator current_break =
1351 break_list.GetBreak(range.end());
1352 range = break_list.GetRange(current_break);
1353 if (range.end() < break_list.max() &&
1354 !render_text->IsValidCursorIndex(range.end())) {
1355 range.set_end(render_text->IndexOfAdjacentGrapheme(range.end(),
1356 CURSOR_FORWARD));
1357 break_list.ApplyValue(current_break->second, range);
1358 }
1359 }
1360 }
1361 1390
1362 // We check the width of the whole desired string at once to ensure we 1391 // We check the width of the whole desired string at once to ensure we
1363 // handle kerning/ligatures/etc. correctly. 1392 // handle kerning/ligatures/etc. correctly.
1364 const float guess_width = render_text->GetContentWidthF(); 1393 const float guess_width = render_text->GetContentWidthF();
1365 if (guess_width == available_width) 1394 if (guess_width == available_width)
1366 break; 1395 break;
1367 if (guess_width > available_width) { 1396 if (guess_width > available_width) {
1368 hi = guess - 1; 1397 hi = guess - 1;
1369 // Move back on the loop terminating condition when the guess is too wide. 1398 // Move back on the loop terminating condition when the guess is too wide.
1370 if (hi < lo) 1399 if (hi < lo)
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
1457 1486
1458 SetDisplayOffset(display_offset_.x() + delta_x); 1487 SetDisplayOffset(display_offset_.x() + delta_x);
1459 } 1488 }
1460 1489
1461 void RenderText::DrawSelection(Canvas* canvas) { 1490 void RenderText::DrawSelection(Canvas* canvas) {
1462 for (const Rect& s : GetSubstringBounds(selection())) 1491 for (const Rect& s : GetSubstringBounds(selection()))
1463 canvas->FillRect(s, selection_background_focused_color_); 1492 canvas->FillRect(s, selection_background_focused_color_);
1464 } 1493 }
1465 1494
1466 } // namespace gfx 1495 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698