OLD | NEW |
---|---|
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...) Loading... | |
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...) Loading... | |
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...) Loading... | |
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...) Loading... | |
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...) Loading... | |
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 template <typename T> | |
1287 void RenderText::RestoreBreakList(RenderText* render_text, | |
msw
2015/02/18 23:32:08
Make this a file-local function in the anon namesp
dschuyler
2015/02/19 00:29:14
Done.
| |
1288 T& break_list) { | |
msw
2015/02/18 23:32:08
Make this a BreakList<T>, other types shouldn't be
dschuyler
2015/02/19 00:29:14
Done.
| |
1289 break_list.SetMax(render_text->text_.length()); | |
1290 Range range; | |
1291 while (range.end() < break_list.max()) { | |
1292 typename T::const_iterator current_break = break_list.GetBreak(range.end()); | |
1293 range = break_list.GetRange(current_break); | |
1294 if (range.end() < break_list.max() && | |
1295 !render_text->IsValidCursorIndex(range.end())) { | |
1296 range.set_end(render_text->IndexOfAdjacentGrapheme(range.end(), | |
1297 CURSOR_FORWARD)); | |
1298 break_list.ApplyValue(current_break->second, range); | |
1299 } | |
1300 } | |
1301 } | |
1302 | |
1270 base::string16 RenderText::Elide(const base::string16& text, | 1303 base::string16 RenderText::Elide(const base::string16& text, |
1271 float text_width, | 1304 float text_width, |
1272 float available_width, | 1305 float available_width, |
1273 ElideBehavior behavior) { | 1306 ElideBehavior behavior) { |
1274 if (available_width <= 0 || text.empty()) | 1307 if (available_width <= 0 || text.empty()) |
1275 return base::string16(); | 1308 return base::string16(); |
1276 if (behavior == ELIDE_EMAIL) | 1309 if (behavior == ELIDE_EMAIL) |
1277 return ElideEmail(text, available_width); | 1310 return ElideEmail(text, available_width); |
1278 if (text_width > 0 && text_width < available_width) | 1311 if (text_width > 0 && text_width < available_width) |
1279 return text; | 1312 return text; |
1280 | 1313 |
1281 TRACE_EVENT0("ui", "RenderText::Elide"); | 1314 TRACE_EVENT0("ui", "RenderText::Elide"); |
1282 | 1315 |
1283 // Create a RenderText copy with attributes that affect the rendering width. | 1316 // Create a RenderText copy with attributes that affect the rendering width. |
1284 scoped_ptr<RenderText> render_text = CreateInstanceOfSameType(); | 1317 scoped_ptr<RenderText> render_text = CreateInstanceOfSameType(); |
1285 render_text->SetFontList(font_list_); | 1318 render_text->SetFontList(font_list_); |
1286 render_text->SetDirectionalityMode(directionality_mode_); | 1319 render_text->SetDirectionalityMode(directionality_mode_); |
1287 render_text->SetCursorEnabled(cursor_enabled_); | 1320 render_text->SetCursorEnabled(cursor_enabled_); |
1288 render_text->set_truncate_length(truncate_length_); | 1321 render_text->set_truncate_length(truncate_length_); |
1289 render_text->styles_ = styles_; | 1322 render_text->styles_ = styles_; |
1323 render_text->baselines_ = baselines_; | |
1290 render_text->colors_ = colors_; | 1324 render_text->colors_ = colors_; |
1291 if (text_width == 0) { | 1325 if (text_width == 0) { |
1292 render_text->SetText(text); | 1326 render_text->SetText(text); |
1293 text_width = render_text->GetContentWidthF(); | 1327 text_width = render_text->GetContentWidthF(); |
1294 } | 1328 } |
1295 if (text_width <= available_width) | 1329 if (text_width <= available_width) |
1296 return text; | 1330 return text; |
1297 | 1331 |
1298 const base::string16 ellipsis = base::string16(kEllipsisUTF16); | 1332 const base::string16 ellipsis = base::string16(kEllipsisUTF16); |
1299 const bool insert_ellipsis = (behavior != TRUNCATE); | 1333 const bool insert_ellipsis = (behavior != TRUNCATE); |
1300 const bool elide_in_middle = (behavior == ELIDE_MIDDLE); | 1334 const bool elide_in_middle = (behavior == ELIDE_MIDDLE); |
1301 const bool elide_at_beginning = (behavior == ELIDE_HEAD); | 1335 const bool elide_at_beginning = (behavior == ELIDE_HEAD); |
1302 | 1336 |
1303 if (insert_ellipsis) { | 1337 if (insert_ellipsis) { |
1304 render_text->SetText(ellipsis); | 1338 render_text->SetText(ellipsis); |
1305 const float ellipsis_width = render_text->GetContentWidthF(); | 1339 const float ellipsis_width = render_text->GetContentWidthF(); |
1306 if (ellipsis_width > available_width) | 1340 if (ellipsis_width > available_width) |
1307 return base::string16(); | 1341 return base::string16(); |
1308 } | 1342 } |
1309 | 1343 |
1310 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); | 1344 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); |
1311 | 1345 |
1312 // Use binary search to compute the elided text. | 1346 // Use binary search to compute the elided text. |
1313 size_t lo = 0; | 1347 size_t lo = 0; |
1314 size_t hi = text.length() - 1; | 1348 size_t hi = text.length() - 1; |
1315 const base::i18n::TextDirection text_direction = GetTextDirection(text); | 1349 const base::i18n::TextDirection text_direction = GetTextDirection(text); |
1316 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { | 1350 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { |
1317 // Restore colors. They will be truncated to size by SetText. | 1351 // Restore colors. They will be truncated to size by SetText. |
1318 render_text->colors_ = colors_; | 1352 render_text->colors_ = colors_; |
1353 render_text->baselines_ = baselines_; | |
1319 base::string16 new_text = | 1354 base::string16 new_text = |
1320 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL); | 1355 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL); |
1321 render_text->SetText(new_text); | 1356 render_text->SetText(new_text); |
1322 | 1357 |
1323 // This has to be an additional step so that the ellipsis is rendered with | 1358 // This has to be an additional step so that the ellipsis is rendered with |
1324 // same style as trailing part of the text. | 1359 // same style as trailing part of the text. |
1325 if (insert_ellipsis && behavior == ELIDE_TAIL) { | 1360 if (insert_ellipsis && behavior == ELIDE_TAIL) { |
1326 // When ellipsis follows text whose directionality is not the same as that | 1361 // 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 | 1362 // 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 | 1363 // whole text. Since we want ellipsis to indicate continuation of the |
1329 // preceding text, we force the directionality of ellipsis to be same as | 1364 // preceding text, we force the directionality of ellipsis to be same as |
1330 // the preceding text using LTR or RTL markers. | 1365 // the preceding text using LTR or RTL markers. |
1331 base::i18n::TextDirection trailing_text_direction = | 1366 base::i18n::TextDirection trailing_text_direction = |
1332 base::i18n::GetLastStrongCharacterDirection(new_text); | 1367 base::i18n::GetLastStrongCharacterDirection(new_text); |
1333 new_text.append(ellipsis); | 1368 new_text.append(ellipsis); |
1334 if (trailing_text_direction != text_direction) { | 1369 if (trailing_text_direction != text_direction) { |
1335 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) | 1370 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) |
1336 new_text += base::i18n::kLeftToRightMark; | 1371 new_text += base::i18n::kLeftToRightMark; |
1337 else | 1372 else |
1338 new_text += base::i18n::kRightToLeftMark; | 1373 new_text += base::i18n::kRightToLeftMark; |
1339 } | 1374 } |
1340 render_text->SetText(new_text); | 1375 render_text->SetText(new_text); |
1341 } | 1376 } |
1342 | 1377 |
1343 // Restore styles. Make sure style ranges don't break new text graphemes. | 1378 // Restore styles. Make sure style ranges don't break new text graphemes. |
msw
2015/02/18 23:32:08
nit: combine comments: "Restore styles and baselin
dschuyler
2015/02/19 00:29:14
Done.
| |
1344 render_text->styles_ = styles_; | 1379 render_text->styles_ = styles_; |
1345 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) { | 1380 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) { |
1346 BreakList<bool>& break_list = render_text->styles_[style]; | 1381 BreakList<bool>& break_list = render_text->styles_[style]; |
msw
2015/02/18 23:32:08
nit: inline render_text->styles_[style] below, rem
dschuyler
2015/02/19 00:29:14
Done.
| |
1347 break_list.SetMax(render_text->text_.length()); | 1382 RestoreBreakList(render_text.get(), break_list); |
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 } | 1383 } |
1361 | 1384 |
1385 // Restore baselines. Make sure the ranges don't break graphemes. | |
1386 RestoreBreakList(render_text.get(), baselines_); | |
1387 | |
1362 // We check the width of the whole desired string at once to ensure we | 1388 // We check the width of the whole desired string at once to ensure we |
1363 // handle kerning/ligatures/etc. correctly. | 1389 // handle kerning/ligatures/etc. correctly. |
1364 const float guess_width = render_text->GetContentWidthF(); | 1390 const float guess_width = render_text->GetContentWidthF(); |
1365 if (guess_width == available_width) | 1391 if (guess_width == available_width) |
1366 break; | 1392 break; |
1367 if (guess_width > available_width) { | 1393 if (guess_width > available_width) { |
1368 hi = guess - 1; | 1394 hi = guess - 1; |
1369 // Move back on the loop terminating condition when the guess is too wide. | 1395 // Move back on the loop terminating condition when the guess is too wide. |
1370 if (hi < lo) | 1396 if (hi < lo) |
1371 lo = hi; | 1397 lo = hi; |
(...skipping 85 matching lines...) Loading... | |
1457 | 1483 |
1458 SetDisplayOffset(display_offset_.x() + delta_x); | 1484 SetDisplayOffset(display_offset_.x() + delta_x); |
1459 } | 1485 } |
1460 | 1486 |
1461 void RenderText::DrawSelection(Canvas* canvas) { | 1487 void RenderText::DrawSelection(Canvas* canvas) { |
1462 for (const Rect& s : GetSubstringBounds(selection())) | 1488 for (const Rect& s : GetSubstringBounds(selection())) |
1463 canvas->FillRect(s, selection_background_focused_color_); | 1489 canvas->FillRect(s, selection_background_focused_color_); |
1464 } | 1490 } |
1465 | 1491 |
1466 } // namespace gfx | 1492 } // namespace gfx |
OLD | NEW |