Index: ui/gfx/render_text.cc |
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc |
index d68d7aaf6a1810340e66dcbadf4e56f464bda3d2..af3491d823446aa9b468f7f0ddd2219a8ec7ec83 100644 |
--- a/ui/gfx/render_text.cc |
+++ b/ui/gfx/render_text.cc |
@@ -17,6 +17,8 @@ |
#include "ui/gfx/skia_util.h" |
#include "ui/gfx/text_constants.h" |
+namespace gfx { |
+ |
namespace { |
// All chars are replaced by this char when the password style is set. |
@@ -24,90 +26,70 @@ namespace { |
// that's available in the font (find_invisible_char() in gtkentry.c). |
const char16 kPasswordReplacementChar = '*'; |
-// Default color used for the cursor. |
-const SkColor kDefaultCursorColor = SK_ColorBLACK; |
- |
-// Default color used for drawing selection text. |
-const SkColor kDefaultSelectionColor = SK_ColorBLACK; |
+// Default color used for the text and cursor. |
+const SkColor kDefaultColor = SK_ColorBLACK; |
// Default color used for drawing selection background. |
const SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY; |
+// Fraction of the text size to lower a strike through below the baseline. |
+const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21); |
+// Fraction of the text size to lower an underline below the baseline. |
+const SkScalar kUnderlineOffset = (SK_Scalar1 / 9); |
+// Fraction of the text size to use for a strike through or under-line. |
+const SkScalar kLineThickness = (SK_Scalar1 / 18); |
+// Fraction of the text size to use for a top margin of a diagonal strike. |
+const SkScalar kDiagonalStrikeMarginOffset = (SK_Scalar1 / 4); |
+ |
+// Get the break at or preceeding |index|; |breaks| must not be empty. |
Alexei Svitkine (slow)
2013/01/23 16:52:54
Please mention what units the |index| is. The way
msw
2013/01/25 09:10:02
Replaced 'index' terminology with 'position' and m
|
+template <class T> |
+typename std::vector<std::pair<size_t, T> >::iterator GetBreak( |
Alexei Svitkine (slow)
2013/01/23 16:52:54
Can you make this templated on the vector too, to
msw
2013/01/25 09:10:02
Moot.
|
+ std::vector<std::pair<size_t, T> >* breaks, |
+ size_t index) { |
+ typename std::vector<std::pair<size_t, T> >::iterator i = breaks->end() - 1; |
+ for (; i != breaks->begin() && i->first > index; --i); |
+ return i; |
+} |
+ |
#ifndef NDEBUG |
-// Check StyleRanges invariant conditions: sorted and non-overlapping ranges. |
-void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) { |
- if (length == 0) { |
- DCHECK(style_ranges.empty()) << "Style ranges exist for empty text."; |
- return; |
- } |
- for (gfx::StyleRanges::size_type i = 0; i < style_ranges.size() - 1; i++) { |
- const ui::Range& former = style_ranges[i].range; |
- const ui::Range& latter = style_ranges[i + 1].range; |
- DCHECK(!former.is_empty()) << "Empty range at " << i << ":" << |
- former.ToString(); |
- DCHECK(former.IsValid()) << "Invalid range at " << i << ":" << |
- former.ToString(); |
- DCHECK(!former.is_reversed()) << "Reversed range at " << i << ":" << |
- former.ToString(); |
- DCHECK(former.end() == latter.start()) << "Ranges gap/overlap/unsorted." << |
- "former:" << former.ToString() << ", latter:" << latter.ToString(); |
+// Check for ordered breaks [0->length) with no adjacent equivalent values. |
+template <class T> |
+void CheckBreaks(const std::vector<std::pair<size_t, T> >& breaks, |
+ size_t text_length) { |
+ DCHECK_EQ(breaks[0].first, 0U) << "The first break must be at index 0."; |
+ for (size_t i = 0; i < breaks.size() - 1; ++i) { |
+ DCHECK_LT(breaks[i].first, breaks[i + 1].first) << "Break out of order."; |
+ DCHECK_NE(breaks[i].second, breaks[i + 1].second) << "Redundant break."; |
} |
- const gfx::StyleRange& end_style = *style_ranges.rbegin(); |
- DCHECK(!end_style.range.is_empty()) << "Empty range at end."; |
- DCHECK(end_style.range.IsValid()) << "Invalid range at end."; |
- DCHECK(!end_style.range.is_reversed()) << "Reversed range at end."; |
- DCHECK(end_style.range.end() == length) << "Style and text length mismatch."; |
+ if (text_length > 0) |
+ DCHECK_LT(breaks.back().first, text_length) << "Break beyond text length."; |
} |
#endif |
-void ApplyStyleRangeImpl(gfx::StyleRanges* style_ranges, |
- const gfx::StyleRange& style_range) { |
- const ui::Range& new_range = style_range.range; |
- // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges. |
- gfx::StyleRanges::iterator i; |
- for (i = style_ranges->begin(); i != style_ranges->end();) { |
- if (i->range.end() < new_range.start()) { |
- i++; |
- } else if (i->range.start() == new_range.end()) { |
- break; |
- } else if (new_range.Contains(i->range)) { |
- i = style_ranges->erase(i); |
- if (i == style_ranges->end()) |
- break; |
- } else if (i->range.start() < new_range.start() && |
- i->range.end() > new_range.end()) { |
- // Split the current style into two styles. |
- gfx::StyleRange split_style = gfx::StyleRange(*i); |
- split_style.range.set_end(new_range.start()); |
- i = style_ranges->insert(i, split_style) + 1; |
- i->range.set_start(new_range.end()); |
- break; |
- } else if (i->range.start() < new_range.start()) { |
- i->range.set_end(new_range.start()); |
- i++; |
- } else if (i->range.end() > new_range.end()) { |
- i->range.set_start(new_range.end()); |
- break; |
- } else { |
- NOTREACHED(); |
- } |
+template <class T> |
+void ResizeBreaks(std::vector<std::pair<size_t, T> >* breaks, size_t length) { |
Alexei Svitkine (slow)
2013/01/23 16:52:54
Add a comment. Also, |breaks| should be last in pa
msw
2013/01/25 09:10:02
Moot.
|
+ if (length == 0) { |
+ breaks->resize(1); |
+ } else { |
+ for (size_t i = 0; i < breaks->size(); ++i) |
+ if (breaks->at(i).first >= length) |
+ breaks->resize(i); |
} |
- // Add the new range in its sorted location. |
- style_ranges->insert(i, style_range); |
+#ifndef NDEBUG |
+ CheckBreaks(*breaks, length); |
+#endif |
} |
// Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags. |
SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) { |
int skia_style = SkTypeface::kNormal; |
- if (font_style & gfx::Font::BOLD) |
- skia_style |= SkTypeface::kBold; |
- if (font_style & gfx::Font::ITALIC) |
- skia_style |= SkTypeface::kItalic; |
+ skia_style |= (font_style & gfx::Font::BOLD) ? SkTypeface::kBold : 0; |
+ skia_style |= (font_style & gfx::Font::ITALIC) ? SkTypeface::kItalic : 0; |
return static_cast<SkTypeface::Style>(skia_style); |
} |
// Given |font| and |display_width|, returns the width of the fade gradient. |
-int CalculateFadeGradientWidth(const gfx::Font& font, int display_width) { |
+int CalculateFadeGradientWidth(const Font& font, int display_width) { |
// Fade in/out about 2.5 characters of the beginning/end of the string. |
// The .5 here is helpful if one of the characters is a space. |
// Use a quarter of the display width if the display width is very short. |
@@ -120,8 +102,8 @@ int CalculateFadeGradientWidth(const gfx::Font& font, int display_width) { |
// Appends to |positions| and |colors| values corresponding to the fade over |
// |fade_rect| from color |c0| to color |c1|. |
-void AddFadeEffect(const gfx::Rect& text_rect, |
- const gfx::Rect& fade_rect, |
+void AddFadeEffect(const Rect& text_rect, |
+ const Rect& fade_rect, |
SkColor c0, |
SkColor c1, |
std::vector<SkScalar>* positions, |
@@ -143,9 +125,9 @@ void AddFadeEffect(const gfx::Rect& text_rect, |
// Creates a SkShader to fade the text, with |left_part| specifying the left |
// fade effect, if any, and |right_part| specifying the right fade effect. |
-skia::RefPtr<SkShader> CreateFadeShader(const gfx::Rect& text_rect, |
- const gfx::Rect& left_part, |
- const gfx::Rect& right_part, |
+skia::RefPtr<SkShader> CreateFadeShader(const Rect& text_rect, |
+ const Rect& left_part, |
+ const Rect& right_part, |
SkColor color) { |
// Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color. |
const SkColor fade_color = SkColorSetA(color, 51); |
@@ -177,8 +159,6 @@ skia::RefPtr<SkShader> CreateFadeShader(const gfx::Rect& text_rect, |
} // namespace |
-namespace gfx { |
- |
namespace internal { |
// Value of |underline_thickness_| that indicates that underline metrics have |
@@ -293,103 +273,63 @@ void SkiaTextRenderer::DrawPosText(const SkPoint* pos, |
canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_); |
} |
-// Draw underline and strike through text decorations. |
-// Based on |SkCanvas::DrawTextDecorations()| and constants from: |
-// third_party/skia/src/core/SkTextFormatParams.h |
-void SkiaTextRenderer::DrawDecorations(int x, int y, int width, |
- const StyleRange& style) { |
- if (!style.underline && !style.strike && !style.diagonal_strike) |
- return; |
+void SkiaTextRenderer::DrawDecorations(int x, int y, int width, bool underline, |
+ bool strike, bool diagonal_strike) { |
+ if (underline) |
+ DrawUnderline(x, y, width); |
+ if (strike) |
+ DrawStrike(x, y, width); |
+ if (diagonal_strike) |
+ DrawDiagonalStrike(x, y, width); |
+} |
- // Fraction of the text size to lower a strike through below the baseline. |
- const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21); |
- // Fraction of the text size to lower an underline below the baseline. |
- const SkScalar kUnderlineOffset = (SK_Scalar1 / 9); |
- // Fraction of the text size to use for a strike through or under-line. |
- const SkScalar kLineThickness = (SK_Scalar1 / 18); |
- // Fraction of the text size to use for a top margin of a diagonal strike. |
- const SkScalar kDiagonalStrikeThroughMarginOffset = (SK_Scalar1 / 4); |
- |
- SkScalar text_size = paint_.getTextSize(); |
- SkScalar height = SkScalarMul(text_size, kLineThickness); |
- SkRect r; |
- |
- r.fLeft = x; |
- r.fRight = x + width; |
- |
- if (style.underline) { |
- if (underline_thickness_ == kUnderlineMetricsNotSet) { |
- r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y); |
- r.fBottom = r.fTop + height; |
- } else { |
- r.fTop = y + underline_position_; |
- r.fBottom = r.fTop + underline_thickness_; |
- } |
- canvas_skia_->drawRect(r, paint_); |
- } |
- if (style.strike) { |
- SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y); |
- r.fTop = offset; |
- r.fBottom = offset + height; |
- canvas_skia_->drawRect(r, paint_); |
- } |
- if (style.diagonal_strike) { |
- SkScalar offset = |
- SkScalarMul(text_size, kDiagonalStrikeThroughMarginOffset); |
- SkPaint paint(paint_); |
- paint.setAntiAlias(true); |
- paint.setStyle(SkPaint::kFill_Style); |
- paint.setStrokeWidth(height * 2); |
- canvas_skia_->drawLine( |
- SkIntToScalar(x), SkIntToScalar(y), |
- SkIntToScalar(x + width), SkIntToScalar(y) - text_size + offset, |
- paint); |
+void SkiaTextRenderer::DrawUnderline(int x, int y, int width) { |
+ SkRect r = SkRect::MakeLTRB(x, y + underline_position_, x + width, |
+ y + underline_position_ + underline_thickness_); |
+ if (underline_thickness_ == kUnderlineMetricsNotSet) { |
+ const SkScalar text_size = paint_.getTextSize(); |
+ r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y); |
+ r.fBottom = r.fTop + SkScalarMul(text_size, kLineThickness); |
} |
+ canvas_skia_->drawRect(r, paint_); |
} |
-} // namespace internal |
+void SkiaTextRenderer::DrawStrike(int x, int y, int width) const { |
+ const SkScalar text_size = paint_.getTextSize(); |
+ const SkScalar height = SkScalarMul(text_size, kLineThickness); |
+ const SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y); |
+ const SkRect r = SkRect::MakeLTRB(x, offset, x + width, offset + height); |
+ canvas_skia_->drawRect(r, paint_); |
+} |
+void SkiaTextRenderer::DrawDiagonalStrike(int x, int y, int width) const { |
+ const SkScalar text_size = paint_.getTextSize(); |
+ const SkScalar offset = SkScalarMul(text_size, kDiagonalStrikeMarginOffset); |
-StyleRange::StyleRange() |
- : foreground(SK_ColorBLACK), |
- font_style(gfx::Font::NORMAL), |
- strike(false), |
- diagonal_strike(false), |
- underline(false) { |
+ SkPaint paint(paint_); |
+ paint.setAntiAlias(true); |
+ paint.setStyle(SkPaint::kFill_Style); |
+ paint.setStrokeWidth(SkScalarMul(text_size, kLineThickness) * 2); |
+ canvas_skia_->drawLine(x, y, x + width, y - text_size + offset, paint); |
} |
+} // namespace internal |
+ |
RenderText::~RenderText() { |
} |
void RenderText::SetText(const string16& text) { |
DCHECK(!composition_range_.IsValid()); |
- size_t old_text_length = text_.length(); |
+ const size_t old_length = text_.length(); |
text_ = text; |
- // Update the style ranges as needed. |
- if (text_.empty()) { |
- style_ranges_.clear(); |
- } else if (style_ranges_.empty()) { |
- ApplyDefaultStyle(); |
- } else if (text_.length() > old_text_length) { |
- style_ranges_.back().range.set_end(text_.length()); |
- } else if (text_.length() < old_text_length) { |
- StyleRanges::iterator i; |
- for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) { |
- if (i->range.start() >= text_.length()) { |
- // Style ranges are sorted and non-overlapping, so all the subsequent |
- // style ranges should be out of text_.length() as well. |
- style_ranges_.erase(i, style_ranges_.end()); |
- break; |
- } |
- } |
- // Since style ranges are sorted and non-overlapping, if there is a style |
- // range ends beyond text_.length, it must be the last one. |
- style_ranges_.back().range.set_end(text_.length()); |
+ // Adjust any ranged styles and colors to accomodate a new text size. |
Alexei Svitkine (slow)
2013/01/23 16:52:54
Nit: "text size" -> "text length" in comment, to a
msw
2013/01/25 09:10:02
Done.
|
+ const size_t new_length = text_.length(); |
+ if (new_length < old_length) { |
+ ResizeBreaks(&colors_, new_length); |
+ for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) |
+ ResizeBreaks(&styles_[style], new_length); |
} |
-#ifndef NDEBUG |
- CheckStyleRanges(style_ranges_, text_.length()); |
-#endif |
cached_bounds_and_offset_valid_ = false; |
// Reset selection model. SetText should always followed by SetSelectionModel |
@@ -582,28 +522,39 @@ void RenderText::SetCompositionRange(const ui::Range& composition_range) { |
ResetLayout(); |
} |
-void RenderText::ApplyStyleRange(const StyleRange& style_range) { |
- const ui::Range& new_range = style_range.range; |
- if (!new_range.IsValid() || new_range.is_empty()) |
- return; |
- CHECK(!new_range.is_reversed()); |
- CHECK(ui::Range(0, text_.length()).Contains(new_range)); |
- ApplyStyleRangeImpl(&style_ranges_, style_range); |
-#ifndef NDEBUG |
- CheckStyleRanges(style_ranges_, text_.length()); |
-#endif |
- // TODO(xji): only invalidate if font or underline changes. |
- cached_bounds_and_offset_valid_ = false; |
- ResetLayout(); |
+void RenderText::SetColor(SkColor value) { |
+ ApplyColor(value, ui::Range(0, text().length())); |
} |
-void RenderText::ApplyDefaultStyle() { |
- style_ranges_.clear(); |
- StyleRange style = StyleRange(default_style_); |
- style.range.set_end(text_.length()); |
- style_ranges_.push_back(style); |
+void RenderText::ApplyColor(SkColor value, const ui::Range& range) { |
+ ApplyBreaks(value, range, &colors_); |
+ |
+#if defined(OS_WIN) |
+ // TODO(msw): Windows applies colors and decorations in the layout process. |
cached_bounds_and_offset_valid_ = false; |
ResetLayout(); |
+#endif |
+} |
+ |
+void RenderText::SetStyle(TextStyle style, bool value) { |
+ ApplyStyle(style, value, ui::Range(0, text().length())); |
+} |
+ |
+void RenderText::ApplyStyle(TextStyle style, |
+ bool value, |
+ const ui::Range& range) { |
+ ApplyBreaks(value, range, &styles_[style]); |
+ |
+ // Only invalidate the layout on font changes; not for colors or decorations. |
+ bool invalidate = (style == BOLD) || (style == UNDERLINE); |
+#if defined(OS_WIN) |
+ // TODO(msw): Windows applies colors and decorations in the layout process. |
+ invalidate = true; |
+#endif |
+ if (invalidate) { |
+ cached_bounds_and_offset_valid_ = false; |
+ ResetLayout(); |
+ } |
} |
void RenderText::SetDirectionalityMode(DirectionalityMode mode) { |
@@ -651,7 +602,7 @@ void RenderText::Draw(Canvas* canvas) { |
EnsureLayout(); |
if (clip_to_display_rect()) { |
- gfx::Rect clip_rect(display_rect()); |
+ Rect clip_rect(display_rect()); |
clip_rect.Inset(ShadowValue::GetMargin(text_shadows_)); |
canvas->Save(); |
@@ -752,8 +703,8 @@ RenderText::RenderText() |
cursor_enabled_(true), |
cursor_visible_(false), |
insert_mode_(true), |
- cursor_color_(kDefaultCursorColor), |
- selection_color_(kDefaultSelectionColor), |
+ cursor_color_(kDefaultColor), |
+ selection_color_(kDefaultColor), |
selection_background_focused_color_(kDefaultSelectionBackgroundColor), |
selection_background_unfocused_color_(kDefaultSelectionBackgroundColor), |
focused_(false), |
@@ -764,6 +715,9 @@ RenderText::RenderText() |
background_is_transparent_(false), |
clip_to_display_rect_(true), |
cached_bounds_and_offset_valid_(false) { |
+ colors_.push_back(ColorBreak(0, kDefaultColor)); |
+ for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) |
+ styles_[style].push_back(StyleBreak(0, false)); |
} |
const Vector2d& RenderText::GetUpdatedDisplayOffset() { |
@@ -802,41 +756,28 @@ const string16& RenderText::GetLayoutText() const { |
return obscured() ? obscured_text_ : text(); |
} |
-void RenderText::ApplyCompositionAndSelectionStyles( |
- StyleRanges* style_ranges) { |
- // TODO(msw): This pattern ought to be reconsidered; what about composition |
- // and selection overlaps, retain existing local style features? |
- // Apply a composition style override to a copy of the style ranges. |
- if (composition_range_.IsValid() && !composition_range_.is_empty()) { |
- StyleRange composition_style(default_style_); |
- composition_style.underline = true; |
- composition_style.range = composition_range_; |
- ApplyStyleRangeImpl(style_ranges, composition_style); |
- } |
- // Apply a selection style override to a copy of the style ranges. |
+void RenderText::ApplyCompositionStyle(std::vector<StyleBreak>* underlines) { |
+ // Apply an underline to the composition range in |underlines|. |
+ if (composition_range_.IsValid() && !composition_range_.is_empty()) |
+ ApplyBreaks(true, composition_range_, underlines); |
+} |
+ |
+void RenderText::ApplySelectionColor(std::vector<ColorBreak>* colors) { |
+ // Apply the selected text color to the selection range in |colors|. |
if (!selection().is_empty()) { |
- StyleRange selection_style(default_style_); |
- selection_style.foreground = selection_color_; |
- selection_style.range = ui::Range(selection().GetMin(), |
- selection().GetMax()); |
- ApplyStyleRangeImpl(style_ranges, selection_style); |
+ const ui::Range range(selection().GetMin(), selection().GetMax()); |
+ ApplyBreaks(selection_color_, range, colors); |
} |
// Apply replacement-mode style override to a copy of the style ranges. |
// |
- // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to |
- // apply styles) in ItemizeLogicalText(). In order for the cursor's underline |
- // character to be drawn correctly, we will need to re-layout the text. It's |
- // not practical to do layout on every cursor blink. We need to fix Windows |
- // port to apply styles during drawing phase like Linux port does. |
- // http://crbug.com/110109 |
+ // TODO(xji|msw): RenderTextWin calls this function in ItemizeLogicalText(), |
+ // but the character at the cursor changes color on every cursor blink in |
+ // overtype mode. RenderTextWin should apply colors in DrawVisualText(), as |
+ // done by RenderTextLinux; see http://crbug.com/110109 |
if (!insert_mode_ && cursor_visible() && focused()) { |
- StyleRange replacement_mode_style(default_style_); |
- replacement_mode_style.foreground = selection_color_; |
- size_t cursor = cursor_position(); |
- replacement_mode_style.range.set_start(cursor); |
- replacement_mode_style.range.set_end( |
- IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD)); |
- ApplyStyleRangeImpl(style_ranges, replacement_mode_style); |
+ const size_t cursor = cursor_position(); |
+ const size_t next = IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD); |
+ ApplyBreaks(selection_color_, ui::Range(cursor, next), colors); |
} |
} |
@@ -899,9 +840,9 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { |
if (horizontal_alignment() == ALIGN_RIGHT) |
std::swap(fade_left, fade_right); |
- gfx::Rect solid_part = display_rect(); |
- gfx::Rect left_part; |
- gfx::Rect right_part; |
+ Rect solid_part = display_rect(); |
+ Rect left_part; |
+ Rect right_part; |
if (fade_left) { |
left_part = solid_part; |
left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); |
@@ -913,19 +854,17 @@ void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) { |
solid_part.Inset(0, 0, gradient_width, 0); |
} |
- gfx::Rect text_rect = display_rect(); |
+ Rect text_rect = display_rect(); |
text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0); |
- const SkColor color = default_style().foreground; |
- skia::RefPtr<SkShader> shader = |
- CreateFadeShader(text_rect, left_part, right_part, color); |
+ skia::RefPtr<SkShader> shader = CreateFadeShader( |
+ text_rect, left_part, right_part, colors_.front().second); |
if (shader) |
renderer->SetShader(shader.get(), display_rect()); |
} |
void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { |
- skia::RefPtr<SkDrawLooper> looper = |
- gfx::CreateShadowDrawLooper(text_shadows_); |
+ skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_); |
renderer->SetDrawLooper(looper.get()); |
} |
@@ -939,6 +878,35 @@ bool RenderText::RangeContainsCaret(const ui::Range& range, |
return range.Contains(ui::Range(caret_pos, adjacent)); |
} |
+template <class T> |
+void RenderText::ApplyBreaks(T value, |
+ const ui::Range& range, |
+ std::vector<std::pair<size_t, T> >* breaks) { |
+ if (!range.IsValid() || (range.is_empty() && !text().empty())) |
+ return; |
+ DCHECK(!breaks->empty()); |
+ DCHECK(!range.is_reversed()); |
+ DCHECK(ui::Range(0, text_.length()).Contains(range)); |
+ |
+ // Erase any breaks in |range|, then add start and end breaks as needed. |
+ typename std::vector<std::pair<size_t, T> >::iterator start = |
+ GetBreak(breaks, range.start()); |
+ start += start->first < range.start() ? 1 : 0; |
+ typename std::vector<std::pair<size_t, T> >::iterator end = |
+ GetBreak(breaks, range.end()); |
+ T trailing_value = text().empty() ? value : end->second; |
+ typename std::vector<std::pair<size_t, T> >::iterator i = |
+ start == breaks->end() ? start : breaks->erase(start, end + 1); |
+ if (range.start() == 0 || (i - 1)->second != value) |
+ i = breaks->insert(i, std::pair<size_t, T>(range.start(), value)) + 1; |
+ if (trailing_value != value && range.end() != text_.length()) |
+ breaks->insert(i, std::pair<size_t, T>(range.end(), trailing_value)); |
+ |
+#ifndef NDEBUG |
+ CheckBreaks(*breaks, text_.length()); |
+#endif |
+} |
+ |
void RenderText::MoveCursorTo(size_t position, bool select) { |
size_t cursor = std::min(position, text().length()); |
if (IsCursorablePosition(cursor)) |
@@ -999,7 +967,7 @@ void RenderText::UpdateCachedBoundsAndOffset() { |
} |
} |
- gfx::Vector2d delta_offset(delta_x, 0); |
+ Vector2d delta_offset(delta_x, 0); |
display_offset_ += delta_offset; |
cursor_bounds_ += delta_offset; |
} |