summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authormsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-01 04:42:17 +0000
committermsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-01 04:42:17 +0000
commitccfa43f042db868f6584f9cceef94ca7c4ddf239 (patch)
treef3f3980981147928cae30754448f88d45527d2d0 /ui
parent8c49a50da6675ed9329b01a1e960c78e42d4ba82 (diff)
downloadchromium_src-ccfa43f042db868f6584f9cceef94ca7c4ddf239.zip
chromium_src-ccfa43f042db868f6584f9cceef94ca7c4ddf239.tar.gz
chromium_src-ccfa43f042db868f6584f9cceef94ca7c4ddf239.tar.bz2
Replace StyleRange with BreakList; update RenderText, etc.
This is a functional rewrite with no observable behavior/appearance changes. (it helps by merging adjacent equivalent styles, reducing artificial run breaks) (it helps disambiguate font/adornment styles for application in layout/drawing) Remove gfx::StyleRange and its use within gfx::RenderText[Win|Linux|Mac]. Add new BreakList class for managing [ranged] colors and styles; add/update tests. Add gfx::TextStyle enum for bold, italic, underline, strike, and diagonal strike. Split ApplyStyleRange into [Set|Apply]Color and [Set|Apply]Style. Split ApplyDefaultStyle and |default_style_| into the first colors_ and styles_. Split up SkiaTextRenderer::DrawDecorations for Underline/Strike/DiagonalStrike. Update ApplyCompositionAndSelectionStyles, add UndoCompositionAndSelectionStyles. Add temporary StyleIterator convenience class for RenderText subclass style iteration. Update RenderText[Win|Linux|Mac], Textfield classes, and other users. Simplify OmniboxResultView (nix bold font, and ClassificationData). Rename gfx::Font::FontStyle::UNDERLINE (was UNDERLINED); TODO(followup): Only break runs for bold/italic, color/adorn while drawing. TODO(followup): Support more custom/ranged colors; merge TextStyle/FontStyle? BUG=90426,164047,131660 TEST=No observable appearance/performance/behavior changes. R=asvitkine@chromium.org,pkasting@chromium.org,sky@chromium.org Review URL: https://chromiumcodereview.appspot.com/11535014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@180067 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/app_list/views/search_result_view.cc19
-rw-r--r--ui/gfx/break_list.h165
-rw-r--r--ui/gfx/break_list_unittest.cc166
-rw-r--r--ui/gfx/canvas_skia.cc108
-rw-r--r--ui/gfx/font.h4
-rw-r--r--ui/gfx/font_list.cc3
-rw-r--r--ui/gfx/pango_util.cc2
-rw-r--r--ui/gfx/platform_font.h2
-rw-r--r--ui/gfx/platform_font_pango.cc4
-rw-r--r--ui/gfx/platform_font_win.cc4
-rw-r--r--ui/gfx/render_text.cc390
-rw-r--r--ui/gfx/render_text.h96
-rw-r--r--ui/gfx/render_text_linux.cc159
-rw-r--r--ui/gfx/render_text_linux.h6
-rw-r--r--ui/gfx/render_text_mac.cc64
-rw-r--r--ui/gfx/render_text_mac.h4
-rw-r--r--ui/gfx/render_text_unittest.cc399
-rw-r--r--ui/gfx/render_text_win.cc64
-rw-r--r--ui/gfx/render_text_win.h8
-rw-r--r--ui/gfx/text_constants.h11
-rw-r--r--ui/ui.gyp1
-rw-r--r--ui/ui_unittests.gypi1
-rw-r--r--ui/views/controls/link.cc14
-rw-r--r--ui/views/controls/textfield/native_textfield_views.cc26
-rw-r--r--ui/views/controls/textfield/native_textfield_views.h8
-rw-r--r--ui/views/controls/textfield/native_textfield_win.cc14
-rw-r--r--ui/views/controls/textfield/native_textfield_win.h8
-rw-r--r--ui/views/controls/textfield/native_textfield_wrapper.h15
-rw-r--r--ui/views/controls/textfield/textfield.cc20
-rw-r--r--ui/views/controls/textfield/textfield.h21
-rw-r--r--ui/views/examples/text_example.cc2
-rw-r--r--ui/views/examples/textfield_example.cc25
32 files changed, 1016 insertions, 817 deletions
diff --git a/ui/app_list/views/search_result_view.cc b/ui/app_list/views/search_result_view.cc
index 47b9825..85caf54 100644
--- a/ui/app_list/views/search_result_view.cc
+++ b/ui/app_list/views/search_result_view.cc
@@ -64,11 +64,7 @@ gfx::RenderText* CreateRenderText(const string16& text,
const app_list::SearchResult::Tags& tags) {
gfx::RenderText* render_text = gfx::RenderText::CreateInstance();
render_text->SetText(text);
-
- gfx::StyleRange default_style;
- default_style.foreground = kDefaultTextColor;
- render_text->set_default_style(default_style);
- render_text->ApplyDefaultStyle();
+ render_text->SetColor(kDefaultTextColor);
for (app_list::SearchResult::Tags::const_iterator it = tags.begin();
it != tags.end();
@@ -77,17 +73,12 @@ gfx::RenderText* CreateRenderText(const string16& text,
if (it->styles == app_list::SearchResult::Tag::NONE)
continue;
- gfx::StyleRange style;
- style.range = it->range;
-
if (it->styles & app_list::SearchResult::Tag::MATCH)
- style.font_style = gfx::Font::BOLD;
- if (it->styles & app_list::SearchResult::Tag::URL)
- style.foreground = kURLTextColor;
+ render_text->ApplyStyle(gfx::BOLD, true, it->range);
if (it->styles & app_list::SearchResult::Tag::DIM)
- style.foreground = kDimmedTextColor;
-
- render_text->ApplyStyleRange(style);
+ render_text->ApplyColor(kDimmedTextColor, it->range);
+ else if (it->styles & app_list::SearchResult::Tag::URL)
+ render_text->ApplyColor(kURLTextColor, it->range);
}
return render_text;
diff --git a/ui/gfx/break_list.h b/ui/gfx/break_list.h
new file mode 100644
index 0000000..c380222
--- /dev/null
+++ b/ui/gfx/break_list.h
@@ -0,0 +1,165 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_BREAK_LIST_H_
+#define UI_GFX_BREAK_LIST_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "ui/base/range/range.h"
+
+namespace gfx {
+
+// BreakLists manage ordered, non-overlapping, and non-repeating ranged values.
+// These may be used to apply ranged colors and styles to text, for an example.
+//
+// Each break stores the start position and value of its associated range.
+// A solitary break at position 0 applies to the entire space [0, max_).
+// |max_| is initially 0 and should be set to match the available ranged space.
+// The first break always has position 0, to ensure all positions have a value.
+// The value of the terminal break applies to the range [break.first, max_).
+// The value of other breaks apply to the range [break.first, (break+1).first).
+template <typename T>
+class BreakList {
+ public:
+ // The break type and const iterator, typedef'ed for convenience.
+ typedef std::pair<size_t, T> Break;
+ typedef typename std::vector<Break>::const_iterator const_iterator;
+
+ // Initialize a break at position 0 with the default or supplied |value|.
+ BreakList();
+ explicit BreakList(T value);
+
+ const std::vector<Break>& breaks() const { return breaks_; }
+
+ // Clear the breaks and set a break at position 0 with the supplied |value|.
+ void SetValue(T value);
+
+ // Adjust the breaks to apply |value| over the supplied |range|.
+ void ApplyValue(T value, const ui::Range& range);
+
+ // Set the max position and trim any breaks at or beyond that position.
+ void SetMax(size_t max);
+
+ // Get the break applicable to |position| (at or preceeding |position|).
+ typename std::vector<Break>::iterator GetBreak(size_t position);
+
+ // Get the range of the supplied break; returns the break's start position and
+ // the next break's start position (or |max_| for the terminal break).
+ ui::Range GetRange(const typename BreakList<T>::const_iterator& i) const;
+
+ // Comparison functions for testing purposes.
+ bool EqualsValueForTesting(T value) const;
+ bool EqualsForTesting(const std::vector<Break>& breaks) const;
+
+ private:
+#ifndef NDEBUG
+ // Check for ordered breaks [0, |max_|) with no adjacent equivalent values.
+ void CheckBreaks();
+#endif
+
+ std::vector<Break> breaks_;
+ size_t max_;
+};
+
+template<class T>
+BreakList<T>::BreakList() : breaks_(1, Break(0, T())), max_(0) {
+}
+
+template<class T>
+BreakList<T>::BreakList(T value) : breaks_(1, Break(0, value)), max_(0) {
+}
+
+template<class T>
+void BreakList<T>::SetValue(T value) {
+ breaks_.clear();
+ breaks_.push_back(Break(0, value));
+}
+
+template<class T>
+void BreakList<T>::ApplyValue(T value, const ui::Range& range) {
+ if (!range.IsValid() || range.is_empty())
+ return;
+ DCHECK(!breaks_.empty());
+ DCHECK(!range.is_reversed());
+ DCHECK(ui::Range(0, max_).Contains(range));
+
+ // Erase any breaks in |range|, then add start and end breaks as needed.
+ typename std::vector<Break>::iterator start = GetBreak(range.start());
+ start += start->first < range.start() ? 1 : 0;
+ typename std::vector<Break>::iterator end = GetBreak(range.end());
+ T trailing_value = end->second;
+ typename std::vector<Break>::iterator i =
+ start == breaks_.end() ? start : breaks_.erase(start, end + 1);
+ if (range.start() == 0 || (i - 1)->second != value)
+ i = breaks_.insert(i, Break(range.start(), value)) + 1;
+ if (trailing_value != value && range.end() != max_)
+ breaks_.insert(i, Break(range.end(), trailing_value));
+
+#ifndef NDEBUG
+ CheckBreaks();
+#endif
+}
+
+template<class T>
+void BreakList<T>::SetMax(size_t max) {
+ typename std::vector<Break>::iterator i = GetBreak(max);
+ i += (i == breaks_.begin() || i->first < max) ? 1 : 0;
+ breaks_.erase(i, breaks_.end());
+ max_ = max;
+
+#ifndef NDEBUG
+ CheckBreaks();
+#endif
+}
+
+template<class T>
+typename std::vector<std::pair<size_t, T> >::iterator BreakList<T>::GetBreak(
+ size_t position) {
+ typename std::vector<Break>::iterator i = breaks_.end() - 1;
+ for (; i != breaks_.begin() && i->first > position; --i);
+ return i;
+}
+
+template<class T>
+ui::Range BreakList<T>::GetRange(
+ const typename BreakList<T>::const_iterator& i) const {
+ const typename BreakList<T>::const_iterator next = i + 1;
+ return ui::Range(i->first, next == breaks_.end() ? max_ : next->first);
+}
+
+template<class T>
+bool BreakList<T>::EqualsValueForTesting(T value) const {
+ return breaks_.size() == 1 && breaks_[0] == Break(0, value);
+}
+
+template<class T>
+bool BreakList<T>::EqualsForTesting(const std::vector<Break>& breaks) const {
+ if (breaks_.size() != breaks.size())
+ return false;
+ for (size_t i = 0; i < breaks.size(); ++i)
+ if (breaks_[i] != breaks[i])
+ return false;
+ return true;
+}
+
+#ifndef NDEBUG
+template <class T>
+void BreakList<T>::CheckBreaks() {
+ DCHECK_EQ(breaks_[0].first, 0U) << "The first break must be at position 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.";
+ }
+ if (max_ > 0)
+ DCHECK_LT(breaks_.back().first, max_) << "Break beyond max position.";
+}
+#endif
+
+} // namespace gfx
+
+#endif // UI_GFX_BREAK_LIST_H_
diff --git a/ui/gfx/break_list_unittest.cc b/ui/gfx/break_list_unittest.cc
new file mode 100644
index 0000000..4acb0d2
--- /dev/null
+++ b/ui/gfx/break_list_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/gfx/break_list.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/range/range.h"
+
+namespace gfx {
+
+class BreakListTest : public testing::Test {};
+
+TEST_F(BreakListTest, SetValue) {
+ // Check the default values applied to new instances.
+ BreakList<bool> style_breaks(false);
+ EXPECT_TRUE(style_breaks.EqualsValueForTesting(false));
+ style_breaks.SetValue(true);
+ EXPECT_TRUE(style_breaks.EqualsValueForTesting(true));
+
+ // Ensure that setting values works correctly.
+ BreakList<SkColor> color_breaks(SK_ColorRED);
+ EXPECT_TRUE(color_breaks.EqualsValueForTesting(SK_ColorRED));
+ color_breaks.SetValue(SK_ColorBLACK);
+ EXPECT_TRUE(color_breaks.EqualsValueForTesting(SK_ColorBLACK));
+}
+
+TEST_F(BreakListTest, ApplyValue) {
+ BreakList<bool> breaks(false);
+ const size_t max = 99;
+ breaks.SetMax(max);
+
+ // Ensure ApplyValue is a no-op on invalid and empty ranges.
+ breaks.ApplyValue(true, ui::Range::InvalidRange());
+ EXPECT_TRUE(breaks.EqualsValueForTesting(false));
+ for (size_t i = 0; i < 3; ++i) {
+ breaks.ApplyValue(true, ui::Range(i, i));
+ EXPECT_TRUE(breaks.EqualsValueForTesting(false));
+ }
+
+ // Apply a value to a valid range, check breaks; repeating should be no-op.
+ std::vector<std::pair<size_t, bool> > expected;
+ expected.push_back(std::pair<size_t, bool>(0, false));
+ expected.push_back(std::pair<size_t, bool>(2, true));
+ expected.push_back(std::pair<size_t, bool>(3, false));
+ for (size_t i = 0; i < 2; ++i) {
+ breaks.ApplyValue(true, ui::Range(2, 3));
+ EXPECT_TRUE(breaks.EqualsForTesting(expected));
+ }
+
+ // Ensure setting a value overrides the ranged value.
+ breaks.SetValue(true);
+ EXPECT_TRUE(breaks.EqualsValueForTesting(true));
+
+ // Ensure applying a value over [0, |max|) is the same as setting a value.
+ breaks.ApplyValue(false, ui::Range(0, max));
+ EXPECT_TRUE(breaks.EqualsValueForTesting(false));
+
+ // Ensure applying a value that is already applied has no effect.
+ breaks.ApplyValue(false, ui::Range(0, 2));
+ breaks.ApplyValue(false, ui::Range(3, 6));
+ breaks.ApplyValue(false, ui::Range(7, max));
+ EXPECT_TRUE(breaks.EqualsValueForTesting(false));
+
+ // Ensure applying an identical neighboring value merges the ranges.
+ breaks.ApplyValue(true, ui::Range(0, 3));
+ breaks.ApplyValue(true, ui::Range(3, 6));
+ breaks.ApplyValue(true, ui::Range(6, max));
+ EXPECT_TRUE(breaks.EqualsValueForTesting(true));
+
+ // Ensure applying a value with the same range overrides the ranged value.
+ breaks.ApplyValue(false, ui::Range(2, 3));
+ breaks.ApplyValue(true, ui::Range(2, 3));
+ EXPECT_TRUE(breaks.EqualsValueForTesting(true));
+
+ // Ensure applying a value with a containing range overrides contained values.
+ breaks.ApplyValue(false, ui::Range(0, 1));
+ breaks.ApplyValue(false, ui::Range(2, 3));
+ breaks.ApplyValue(true, ui::Range(0, 3));
+ EXPECT_TRUE(breaks.EqualsValueForTesting(true));
+ breaks.ApplyValue(false, ui::Range(4, 5));
+ breaks.ApplyValue(false, ui::Range(6, 7));
+ breaks.ApplyValue(false, ui::Range(8, 9));
+ breaks.ApplyValue(true, ui::Range(4, 9));
+ EXPECT_TRUE(breaks.EqualsValueForTesting(true));
+
+ // Ensure applying various overlapping values yields the intended results.
+ breaks.ApplyValue(false, ui::Range(1, 4));
+ breaks.ApplyValue(false, ui::Range(5, 8));
+ breaks.ApplyValue(true, ui::Range(0, 2));
+ breaks.ApplyValue(true, ui::Range(3, 6));
+ breaks.ApplyValue(true, ui::Range(7, max));
+ std::vector<std::pair<size_t, bool> > overlap;
+ overlap.push_back(std::pair<size_t, bool>(0, true));
+ overlap.push_back(std::pair<size_t, bool>(2, false));
+ overlap.push_back(std::pair<size_t, bool>(3, true));
+ overlap.push_back(std::pair<size_t, bool>(6, false));
+ overlap.push_back(std::pair<size_t, bool>(7, true));
+ EXPECT_TRUE(breaks.EqualsForTesting(overlap));
+}
+
+TEST_F(BreakListTest, SetMax) {
+ // Ensure values adjust to accommodate max position changes.
+ BreakList<bool> breaks(false);
+ breaks.SetMax(9);
+ breaks.ApplyValue(true, ui::Range(0, 2));
+ breaks.ApplyValue(true, ui::Range(3, 6));
+ breaks.ApplyValue(true, ui::Range(7, 9));
+
+ std::vector<std::pair<size_t, bool> > expected;
+ expected.push_back(std::pair<size_t, bool>(0, true));
+ expected.push_back(std::pair<size_t, bool>(2, false));
+ expected.push_back(std::pair<size_t, bool>(3, true));
+ expected.push_back(std::pair<size_t, bool>(6, false));
+ expected.push_back(std::pair<size_t, bool>(7, true));
+ EXPECT_TRUE(breaks.EqualsForTesting(expected));
+
+ // Setting a smaller max should remove any corresponding breaks.
+ breaks.SetMax(7);
+ expected.resize(4);
+ EXPECT_TRUE(breaks.EqualsForTesting(expected));
+ breaks.SetMax(4);
+ expected.resize(3);
+ EXPECT_TRUE(breaks.EqualsForTesting(expected));
+ breaks.SetMax(4);
+ EXPECT_TRUE(breaks.EqualsForTesting(expected));
+
+ // Setting a larger max should not change any breaks.
+ breaks.SetMax(50);
+ EXPECT_TRUE(breaks.EqualsForTesting(expected));
+}
+
+TEST_F(BreakListTest, GetBreakAndRange) {
+ BreakList<bool> breaks(false);
+ breaks.SetMax(8);
+ breaks.ApplyValue(true, ui::Range(1, 2));
+ breaks.ApplyValue(true, ui::Range(4, 6));
+
+ struct {
+ size_t position;
+ size_t break_index;
+ ui::Range range;
+ } cases[] = {
+ { 0, 0, ui::Range(0, 1) },
+ { 1, 1, ui::Range(1, 2) },
+ { 2, 2, ui::Range(2, 4) },
+ { 3, 2, ui::Range(2, 4) },
+ { 4, 3, ui::Range(4, 6) },
+ { 5, 3, ui::Range(4, 6) },
+ { 6, 4, ui::Range(6, 8) },
+ { 7, 4, ui::Range(6, 8) },
+ // Positions at or beyond the max simply return the last break and range.
+ { 8, 4, ui::Range(6, 8) },
+ { 9, 4, ui::Range(6, 8) },
+ };
+
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ BreakList<bool>::const_iterator it = breaks.GetBreak(cases[i].position);
+ EXPECT_EQ(breaks.breaks()[cases[i].break_index], *it);
+ EXPECT_EQ(breaks.GetRange(it), cases[i].range);
+ }
+}
+
+} // namespace gfx
diff --git a/ui/gfx/canvas_skia.cc b/ui/gfx/canvas_skia.cc
index 263819b..2d995cf 100644
--- a/ui/gfx/canvas_skia.cc
+++ b/ui/gfx/canvas_skia.cc
@@ -17,6 +17,8 @@
#include "ui/gfx/shadow_value.h"
#include "ui/gfx/text_utils.h"
+namespace gfx {
+
namespace {
// If necessary, wraps |text| with RTL/LTR directionality characters based on
@@ -28,11 +30,11 @@ bool AdjustStringDirection(int flags, string16* text) {
// If the string is empty or LTR was forced, simply return false since the
// default RenderText directionality is already LTR.
- if (text->empty() || (flags & gfx::Canvas::FORCE_LTR_DIRECTIONALITY))
+ if (text->empty() || (flags & Canvas::FORCE_LTR_DIRECTIONALITY))
return false;
// If RTL is forced, apply it to the string.
- if (flags & gfx::Canvas::FORCE_RTL_DIRECTIONALITY) {
+ if (flags & Canvas::FORCE_RTL_DIRECTIONALITY) {
base::i18n::WrapStringWithRTLFormatting(text);
return true;
}
@@ -84,11 +86,11 @@ bool PixelShouldGetHalo(const SkBitmap& bitmap,
// Returns a range in |text| to underline or ui::Range::InvalidRange() if
// underlining is not needed.
ui::Range StripAcceleratorChars(int flags, string16* text) {
- if (flags & (gfx::Canvas::SHOW_PREFIX | gfx::Canvas::HIDE_PREFIX)) {
+ if (flags & (Canvas::SHOW_PREFIX | Canvas::HIDE_PREFIX)) {
int char_pos = -1;
int char_span = 0;
- *text = gfx::RemoveAcceleratorChar(*text, '&', &char_pos, &char_span);
- if ((flags & gfx::Canvas::SHOW_PREFIX) && char_pos != -1)
+ *text = RemoveAcceleratorChar(*text, '&', &char_pos, &char_span);
+ if ((flags & Canvas::SHOW_PREFIX) && char_pos != -1)
return ui::Range(char_pos, char_pos + char_span);
}
return ui::Range::InvalidRange();
@@ -96,7 +98,7 @@ ui::Range StripAcceleratorChars(int flags, string16* text) {
// Elides |text| and adjusts |range| appropriately. If eliding causes |range|
// to no longer point to the same character in |text|, |range| is made invalid.
-void ElideTextAndAdjustRange(const gfx::Font& font,
+void ElideTextAndAdjustRange(const Font& font,
int width,
string16* text,
ui::Range* range) {
@@ -111,55 +113,42 @@ void ElideTextAndAdjustRange(const gfx::Font& font,
}
// Updates |render_text| from the specified parameters.
-void UpdateRenderText(const gfx::Rect& rect,
+void UpdateRenderText(const Rect& rect,
const string16& text,
- const gfx::Font& font,
+ const Font& font,
int flags,
SkColor color,
- gfx::RenderText* render_text) {
+ RenderText* render_text) {
render_text->SetFont(font);
render_text->SetText(text);
render_text->SetCursorEnabled(false);
- gfx::Rect display_rect = rect;
+ Rect display_rect = rect;
display_rect.set_height(font.GetHeight());
render_text->SetDisplayRect(display_rect);
// Set the text alignment explicitly based on the directionality of the UI,
// if not specified.
- if (!(flags & (gfx::Canvas::TEXT_ALIGN_CENTER |
- gfx::Canvas::TEXT_ALIGN_RIGHT |
- gfx::Canvas::TEXT_ALIGN_LEFT))) {
- flags |= gfx::Canvas::DefaultCanvasTextAlignment();
+ if (!(flags & (Canvas::TEXT_ALIGN_CENTER |
+ Canvas::TEXT_ALIGN_RIGHT |
+ Canvas::TEXT_ALIGN_LEFT))) {
+ flags |= Canvas::DefaultCanvasTextAlignment();
}
- if (flags & gfx::Canvas::TEXT_ALIGN_RIGHT)
- render_text->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
- else if (flags & gfx::Canvas::TEXT_ALIGN_CENTER)
- render_text->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+ if (flags & Canvas::TEXT_ALIGN_RIGHT)
+ render_text->SetHorizontalAlignment(ALIGN_RIGHT);
+ else if (flags & Canvas::TEXT_ALIGN_CENTER)
+ render_text->SetHorizontalAlignment(ALIGN_CENTER);
else
- render_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ render_text->SetHorizontalAlignment(ALIGN_LEFT);
- if (flags & gfx::Canvas::NO_SUBPIXEL_RENDERING)
+ if (flags & Canvas::NO_SUBPIXEL_RENDERING)
render_text->set_background_is_transparent(true);
- gfx::StyleRange style;
- style.foreground = color;
- style.font_style = font.GetStyle();
- if (font.GetStyle() & gfx::Font::UNDERLINED)
- style.underline = true;
- render_text->set_default_style(style);
- render_text->ApplyDefaultStyle();
-}
-
-// Adds an underline style to |render_text| over |range|.
-void ApplyUnderlineStyle(const ui::Range& range, gfx::RenderText* render_text) {
- gfx::StyleRange style = render_text->default_style();
- if (range.IsValid() && !style.underline) {
- style.range = range;
- style.underline = true;
- render_text->ApplyStyleRange(style);
- }
+ render_text->SetColor(color);
+ render_text->SetStyle(BOLD, (font.GetStyle() & Font::BOLD) != 0);
+ render_text->SetStyle(ITALIC, (font.GetStyle() & Font::ITALIC) != 0);
+ render_text->SetStyle(UNDERLINE, (font.GetStyle() & Font::UNDERLINE) != 0);
}
// Returns updated |flags| to match platform-specific expected behavior.
@@ -168,7 +157,7 @@ int AdjustPlatformSpecificFlags(const string16& text, int flags) {
// TODO(asvitkine): ash/tooltips/tooltip_controller.cc adds \n's to the string
// without passing MULTI_LINE.
if (text.find('\n') != string16::npos)
- flags |= gfx::Canvas::MULTI_LINE;
+ flags |= Canvas::MULTI_LINE;
#endif
return flags;
@@ -176,11 +165,9 @@ int AdjustPlatformSpecificFlags(const string16& text, int flags) {
} // namespace
-namespace gfx {
-
// static
void Canvas::SizeStringInt(const string16& text,
- const gfx::Font& font,
+ const Font& font,
int* width, int* height,
int flags) {
DCHECK_GE(*width, 0);
@@ -200,7 +187,7 @@ void Canvas::SizeStringInt(const string16& text,
else if (!(flags & NO_ELLIPSIS))
wrap_behavior = ui::ELIDE_LONG_WORDS;
- gfx::Rect rect(*width, INT_MAX);
+ Rect rect(*width, INT_MAX);
std::vector<string16> strings;
ui::ElideRectangleText(adjusted_text, font, rect.width(), rect.height(),
wrap_behavior, &strings);
@@ -227,7 +214,7 @@ void Canvas::SizeStringInt(const string16& text,
*height = font.GetHeight();
} else {
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
- gfx::Rect rect(*width, *height);
+ Rect rect(*width, *height);
StripAcceleratorChars(flags, &adjusted_text);
UpdateRenderText(rect, adjusted_text, font, flags, 0, render_text.get());
const Size string_size = render_text->GetStringSize();
@@ -238,9 +225,9 @@ void Canvas::SizeStringInt(const string16& text,
}
void Canvas::DrawStringWithShadows(const string16& text,
- const gfx::Font& font,
+ const Font& font,
SkColor color,
- const gfx::Rect& text_bounds,
+ const Rect& text_bounds,
int flags,
const ShadowValues& shadows) {
if (!IntersectsClipRect(text_bounds))
@@ -248,13 +235,13 @@ void Canvas::DrawStringWithShadows(const string16& text,
flags = AdjustPlatformSpecificFlags(text, flags);
- gfx::Rect clip_rect(text_bounds);
+ Rect clip_rect(text_bounds);
clip_rect.Inset(ShadowValue::GetMargin(shadows));
canvas_->save(SkCanvas::kClip_SaveFlag);
ClipRect(clip_rect);
- gfx::Rect rect(text_bounds);
+ Rect rect(text_bounds);
string16 adjusted_text = text;
#if defined(OS_WIN)
@@ -288,16 +275,17 @@ void Canvas::DrawStringWithShadows(const string16& text,
if (i == 0) {
// TODO(msw|asvitkine): Support multi-line text with varied heights.
const int aggregate_height = strings.size() * line_height;
- rect += gfx::Vector2d(0, (text_bounds.height() - aggregate_height) / 2);
+ rect += Vector2d(0, (text_bounds.height() - aggregate_height) / 2);
}
#endif
rect.set_height(line_height);
- ApplyUnderlineStyle(range, render_text.get());
+ if (range.IsValid())
+ render_text->ApplyStyle(UNDERLINE, true, range);
render_text->SetDisplayRect(rect);
render_text->Draw(this);
- rect += gfx::Vector2d(0, line_height);
+ rect += Vector2d(0, line_height);
}
} else {
ui::Range range = StripAcceleratorChars(flags, &adjusted_text);
@@ -327,11 +315,11 @@ void Canvas::DrawStringWithShadows(const string16& text,
const int line_height = render_text->GetStringSize().height();
// Center the text vertically.
- rect += gfx::Vector2d(0, (text_bounds.height() - line_height) / 2);
+ rect += Vector2d(0, (text_bounds.height() - line_height) / 2);
rect.set_height(line_height);
render_text->SetDisplayRect(rect);
-
- ApplyUnderlineStyle(range, render_text.get());
+ if (range.IsValid())
+ render_text->ApplyStyle(UNDERLINE, true, range);
render_text->Draw(this);
}
@@ -339,7 +327,7 @@ void Canvas::DrawStringWithShadows(const string16& text,
}
void Canvas::DrawStringWithHalo(const string16& text,
- const gfx::Font& font,
+ const Font& font,
SkColor text_color,
SkColor halo_color_in,
int x, int y, int w, int h,
@@ -354,7 +342,7 @@ void Canvas::DrawStringWithHalo(const string16& text,
Canvas text_canvas(size, scale_factor(), true);
SkPaint bkgnd_paint;
bkgnd_paint.setColor(halo_color);
- text_canvas.DrawRect(gfx::Rect(size), bkgnd_paint);
+ text_canvas.DrawRect(Rect(size), bkgnd_paint);
// Draw the text into the temporary buffer. This will have correct
// ClearType since the background color is the same as the halo color.
@@ -379,7 +367,7 @@ void Canvas::DrawStringWithHalo(const string16& text,
}
// Draw the halo bitmap with blur.
- gfx::ImageSkia text_image = gfx::ImageSkia(gfx::ImageSkiaRep(text_bitmap,
+ ImageSkia text_image = ImageSkia(ImageSkiaRep(text_bitmap,
text_canvas.scale_factor()));
DrawImageInt(text_image, x - 1, y - 1);
}
@@ -388,9 +376,9 @@ void Canvas::DrawFadeTruncatingString(
const string16& text,
TruncateFadeMode truncate_mode,
size_t desired_characters_to_truncate_from_head,
- const gfx::Font& font,
+ const Font& font,
SkColor color,
- const gfx::Rect& display_rect) {
+ const Rect& display_rect) {
int flags = NO_ELLIPSIS;
// If the whole string fits in the destination then just draw it directly.
@@ -441,12 +429,12 @@ void Canvas::DrawFadeTruncatingString(
if (!(flags & TEXT_ALIGN_RIGHT))
flags |= TEXT_ALIGN_LEFT;
- gfx::Rect rect = display_rect;
+ Rect rect = display_rect;
UpdateRenderText(rect, clipped_text, font, flags, color, render_text.get());
const int line_height = render_text->GetStringSize().height();
// Center the text vertically.
- rect += gfx::Vector2d(0, (display_rect.height() - line_height) / 2);
+ rect += Vector2d(0, (display_rect.height() - line_height) / 2);
rect.set_height(line_height);
render_text->SetDisplayRect(rect);
diff --git a/ui/gfx/font.h b/ui/gfx/font.h
index aa2b1c7..c05e76c 100644
--- a/ui/gfx/font.h
+++ b/ui/gfx/font.h
@@ -25,7 +25,7 @@ class UI_EXPORT Font {
NORMAL = 0,
BOLD = 1,
ITALIC = 2,
- UNDERLINED = 4,
+ UNDERLINE = 4,
};
// Creates a font with the default name and style.
@@ -56,7 +56,7 @@ class UI_EXPORT Font {
// |size_delta| is the size in pixels to add to the current font. See the
// single argument version of this method for an example.
// The style parameter specifies the new style for the font, and is a
- // bitmask of the values: BOLD, ITALIC and UNDERLINED.
+ // bitmask of the values: BOLD, ITALIC and UNDERLINE.
Font DeriveFont(int size_delta, int style) const;
// Returns the number of vertical pixels needed to display characters from
diff --git a/ui/gfx/font_list.cc b/ui/gfx/font_list.cc
index dc42c2f..464e722 100644
--- a/ui/gfx/font_list.cc
+++ b/ui/gfx/font_list.cc
@@ -28,8 +28,7 @@ void ParseFontDescriptionString(const std::string& font_description_string,
DCHECK_GT(*font_size, 0);
font_names->pop_back();
- // Besides underline (which is supported through StyleRange), Font only
- // supports BOLD and ITALIC, but not other styles.
+ // Font supports BOLD and ITALIC; underline is supported via RenderText.
*font_style = 0;
for (size_t i = 0; i < styles_size.size() - 1; ++i) {
// Styles are separated by white spaces. base::SplitString splits styles
diff --git a/ui/gfx/pango_util.cc b/ui/gfx/pango_util.cc
index 424eb45..612cd63 100644
--- a/ui/gfx/pango_util.cc
+++ b/ui/gfx/pango_util.cc
@@ -346,7 +346,7 @@ void DrawPangoLayout(cairo_t* cr,
cairo_move_to(cr, text_rect.x(), text_rect.y());
pango_cairo_show_layout(cr, layout);
- if (font.GetStyle() & gfx::Font::UNDERLINED) {
+ if (font.GetStyle() & gfx::Font::UNDERLINE) {
gfx::PlatformFontPango* platform_font =
static_cast<gfx::PlatformFontPango*>(font.platform_font());
DrawPangoTextUnderline(cr, platform_font, 0.0, text_rect);
diff --git a/ui/gfx/platform_font.h b/ui/gfx/platform_font.h
index e624847..a78eda6 100644
--- a/ui/gfx/platform_font.h
+++ b/ui/gfx/platform_font.h
@@ -29,7 +29,7 @@ class UI_EXPORT PlatformFont : public base::RefCounted<PlatformFont> {
// Returns a new Font derived from the existing font.
// |size_delta| is the size in pixels to add to the current font.
// The style parameter specifies the new style for the font, and is a
- // bitmask of the values: BOLD, ITALIC and UNDERLINED.
+ // bitmask of the values: BOLD, ITALIC and UNDERLINE.
virtual Font DeriveFont(int size_delta, int style) const = 0;
// Returns the number of vertical pixels needed to display characters from
diff --git a/ui/gfx/platform_font_pango.cc b/ui/gfx/platform_font_pango.cc
index 6bb580b..3d2b986 100644
--- a/ui/gfx/platform_font_pango.cc
+++ b/ui/gfx/platform_font_pango.cc
@@ -240,8 +240,8 @@ NativeFont PlatformFontPango::GetNativeFont() const {
case gfx::Font::ITALIC:
pango_font_description_set_style(pfd, PANGO_STYLE_ITALIC);
break;
- case gfx::Font::UNDERLINED:
- // TODO(deanm): How to do underlined? Where do we use it? Probably have
+ case gfx::Font::UNDERLINE:
+ // TODO(deanm): How to do underline? Where do we use it? Probably have
// to paint it ourselves, see pango_font_metrics_get_underline_position.
break;
}
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
index e25395d..66649c9 100644
--- a/ui/gfx/platform_font_win.cc
+++ b/ui/gfx/platform_font_win.cc
@@ -55,7 +55,7 @@ int AdjustFontSize(int lf_height, int size_delta) {
// Sets style properties on |font_info| based on |font_style|.
void SetLogFontStyle(int font_style, LOGFONT* font_info) {
- font_info->lfUnderline = (font_style & gfx::Font::UNDERLINED) != 0;
+ font_info->lfUnderline = (font_style & gfx::Font::UNDERLINE) != 0;
font_info->lfItalic = (font_style & gfx::Font::ITALIC) != 0;
font_info->lfWeight = (font_style & gfx::Font::BOLD) ? FW_BOLD : FW_NORMAL;
}
@@ -242,7 +242,7 @@ PlatformFontWin::HFontRef* PlatformFontWin::CreateHFontRef(HFONT font) {
if (font_metrics.tmItalic)
style |= Font::ITALIC;
if (font_metrics.tmUnderlined)
- style |= Font::UNDERLINED;
+ style |= Font::UNDERLINE;
if (font_metrics.tmWeight >= kTextMetricWeightBold)
style |= Font::BOLD;
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
index d68d7aa..8f02724 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,31 @@ 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;
-#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();
- }
- 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.";
-}
-#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();
- }
- }
- // Add the new range in its sorted location.
- style_ranges->insert(i, style_range);
-}
+// 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);
// 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 +63,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 +86,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 +120,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 +234,84 @@ 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);
+
+ 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);
+}
+StyleIterator::StyleIterator(const BreakList<SkColor>& colors,
+ const std::vector<BreakList<bool> >& styles)
+ : colors_(colors),
+ styles_(styles) {
+ color_ = colors_.breaks().begin();
+ for (size_t i = 0; i < styles_.size(); ++i)
+ style_.push_back(styles_[i].breaks().begin());
+}
+
+StyleIterator::~StyleIterator() {}
+
+ui::Range StyleIterator::GetRange() const {
+ ui::Range range(colors_.GetRange(color_));
+ for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
+ range = range.Intersect(styles_[i].GetRange(style_[i]));
+ return range;
+}
-StyleRange::StyleRange()
- : foreground(SK_ColorBLACK),
- font_style(gfx::Font::NORMAL),
- strike(false),
- diagonal_strike(false),
- underline(false) {
+void StyleIterator::UpdatePosition(size_t position) {
+ color_ = colors_.GetBreak(position);
+ for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
+ style_[i] = styles_[i].GetBreak(position);
}
+} // namespace internal
+
RenderText::~RenderText() {
}
void RenderText::SetText(const string16& text) {
DCHECK(!composition_range_.IsValid());
- size_t old_text_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());
- }
-#ifndef NDEBUG
- CheckStyleRanges(style_ranges_, text_.length());
-#endif
+ // Adjust ranged styles and colors to accommodate a new text length.
+ const size_t text_length = text_.length();
+ colors_.SetMax(text_length);
+ for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
+ styles_[style].SetMax(text_length);
cached_bounds_and_offset_valid_ = false;
// Reset selection model. SetText should always followed by SetSelectionModel
@@ -582,28 +504,56 @@ 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.
+void RenderText::SetColor(SkColor value) {
+ colors_.SetValue(value);
+
+#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::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) {
+ colors_.ApplyValue(value, range);
+
+#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) {
+ styles_[style].SetValue(value);
+
+ // Only invalidate the layout on font changes; not for colors or decorations.
+ bool invalidate = (style == BOLD) || (style == ITALIC);
+#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::ApplyStyle(TextStyle style,
+ bool value,
+ const ui::Range& range) {
+ styles_[style].ApplyValue(value, range);
+
+ // Only invalidate the layout on font changes; not for colors or decorations.
+ bool invalidate = (style == BOLD) || (style == ITALIC);
+#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 +601,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,12 +702,15 @@ 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),
composition_range_(ui::Range::InvalidRange()),
+ colors_(kDefaultColor),
+ styles_(NUM_TEXT_STYLES),
+ composition_and_selection_styles_applied_(false),
obscured_(false),
fade_head_(false),
fade_tail_(false),
@@ -802,42 +755,36 @@ 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::ApplyCompositionAndSelectionStyles() {
+ // Save the underline and color breaks to undo the temporary styles later.
+ DCHECK(!composition_and_selection_styles_applied_);
+ saved_colors_ = colors_;
+ saved_underlines_ = styles_[UNDERLINE];
+
+ // Apply an underline to the composition range in |underlines|.
+ if (composition_range_.IsValid() && !composition_range_.is_empty())
+ styles_[UNDERLINE].ApplyValue(true, composition_range_);
+
+ // Apply the selected text color to the [un-reversed] selection range.
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());
+ colors_.ApplyValue(selection_color_, range);
}
- // 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
+ // Apply the selected text color to the cursor range in overtype mode.
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);
+ colors_.ApplyValue(selection_color_, ui::Range(cursor, next));
}
+ composition_and_selection_styles_applied_ = true;
+}
+
+void RenderText::UndoCompositionAndSelectionStyles() {
+ // Restore the underline and color breaks to undo the temporary styles.
+ DCHECK(composition_and_selection_styles_applied_);
+ colors_ = saved_colors_;
+ styles_[UNDERLINE] = saved_underlines_;
+ composition_and_selection_styles_applied_ = false;
}
Vector2d RenderText::GetTextOffset() {
@@ -899,9 +846,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 +860,18 @@ 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);
+ // TODO(msw): Use the actual text colors corresponding to each faded part.
+ skia::RefPtr<SkShader> shader = CreateFadeShader(
+ text_rect, left_part, right_part, colors_.breaks().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());
}
@@ -999,7 +945,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;
}
diff --git a/ui/gfx/render_text.h b/ui/gfx/render_text.h
index dd86fc5..f45fd101 100644
--- a/ui/gfx/render_text.h
+++ b/ui/gfx/render_text.h
@@ -18,6 +18,7 @@
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkRect.h"
#include "ui/base/range/range.h"
+#include "ui/gfx/break_list.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
@@ -37,7 +38,6 @@ namespace gfx {
class Canvas;
class Font;
class RenderTextTest;
-struct StyleRange;
namespace internal {
@@ -62,7 +62,14 @@ class SkiaTextRenderer {
void DrawPosText(const SkPoint* pos,
const uint16* glyphs,
size_t glyph_count);
- void DrawDecorations(int x, int y, int width, const StyleRange& style);
+ // Draw underline and strike-through text decorations.
+ // Based on |SkCanvas::DrawTextDecorations()| and constants from:
+ // third_party/skia/src/core/SkTextFormatParams.h
+ void DrawDecorations(int x, int y, int width, bool underline, bool strike,
+ bool diagonal_strike);
+ void DrawUnderline(int x, int y, int width);
+ void DrawStrike(int x, int y, int width) const;
+ void DrawDiagonalStrike(int x, int y, int width) const;
private:
SkCanvas* canvas_skia_;
@@ -76,22 +83,34 @@ class SkiaTextRenderer {
DISALLOW_COPY_AND_ASSIGN(SkiaTextRenderer);
};
-} // namespace internal
+// Internal helper class used by derived classes to iterate colors and styles.
+class StyleIterator {
+ public:
+ StyleIterator(const BreakList<SkColor>& colors,
+ const std::vector<BreakList<bool> >& styles);
+ ~StyleIterator();
+
+ // Get the colors and styles at the current iterator position.
+ SkColor color() const { return color_->second; }
+ bool style(TextStyle s) const { return style_[s]->second; }
+
+ // Get the intersecting range of the current iterator set.
+ ui::Range GetRange() const;
+
+ // Update the iterator to point to colors and styles applicable at |position|.
+ void UpdatePosition(size_t position);
+
+ private:
+ BreakList<SkColor> colors_;
+ std::vector<BreakList<bool> > styles_;
-// A visual style applicable to a range of text.
-struct UI_EXPORT StyleRange {
- StyleRange();
-
- SkColor foreground;
- // A gfx::Font::FontStyle flag to specify bold and italic styles.
- int font_style;
- bool strike;
- bool diagonal_strike;
- bool underline;
- ui::Range range;
+ BreakList<SkColor>::const_iterator color_;
+ std::vector<BreakList<bool>::const_iterator> style_;
+
+ DISALLOW_COPY_AND_ASSIGN(StyleIterator);
};
-typedef std::vector<StyleRange> StyleRanges;
+} // namespace internal
// RenderText represents an abstract model of styled text and its corresponding
// visual layout. Support is built in for a cursor, a selection, simple styling,
@@ -157,9 +176,6 @@ class UI_EXPORT RenderText {
bool clip_to_display_rect() const { return clip_to_display_rect_; }
void set_clip_to_display_rect(bool clip) { clip_to_display_rect_ = clip; }
- const StyleRange& default_style() const { return default_style_; }
- void set_default_style(const StyleRange& style) { default_style_ = style; }
-
// In an obscured (password) field, all text is drawn as asterisks or bullets.
bool obscured() const { return obscured_; }
void SetObscured(bool obscured);
@@ -227,11 +243,16 @@ class UI_EXPORT RenderText {
const ui::Range& GetCompositionRange() const;
void SetCompositionRange(const ui::Range& composition_range);
- // Apply |style_range| to the internal style model.
- void ApplyStyleRange(const StyleRange& style_range);
+ // Set the text color over the entire text or a logical character range.
+ // The |range| should be valid, non-reversed, and within [0, text().length()].
+ void SetColor(SkColor value);
+ void ApplyColor(SkColor value, const ui::Range& range);
- // Apply |default_style_| over the entire text range.
- void ApplyDefaultStyle();
+ // Set various text styles over the entire text or a logical character range.
+ // The respective |style| is applied if |value| is true, or removed if false.
+ // The |range| should be valid, non-reversed, and within [0, text().length()].
+ void SetStyle(TextStyle style, bool value);
+ void ApplyStyle(TextStyle style, bool value, const ui::Range& range);
// Set the text directionality mode and get the text direction yielded.
void SetDirectionalityMode(DirectionalityMode mode);
@@ -295,14 +316,15 @@ class UI_EXPORT RenderText {
protected:
RenderText();
+ const BreakList<SkColor>& colors() const { return colors_; }
+ const std::vector<BreakList<bool> >& styles() const { return styles_; }
+
const Vector2d& GetUpdatedDisplayOffset();
void set_cached_bounds_and_offset_valid(bool valid) {
cached_bounds_and_offset_valid_ = valid;
}
- const StyleRanges& style_ranges() const { return style_ranges_; }
-
// Get the selection model that visually neighbors |position| by |break_type|.
// The returned value represents a cursor/caret position without a selection.
SelectionModel GetAdjacentSelectionModel(const SelectionModel& current,
@@ -364,9 +386,9 @@ class UI_EXPORT RenderText {
// Returns the text used for layout, which may be |obscured_text_|.
const string16& GetLayoutText() const;
- // Apply composition style (underline) to composition range and selection
- // style (foreground) to selection range.
- void ApplyCompositionAndSelectionStyles(StyleRanges* style_ranges);
+ // Apply (and undo) temporary composition underlines and selection colors.
+ void ApplyCompositionAndSelectionStyles();
+ void UndoCompositionAndSelectionStyles();
// Returns the text offset from the origin after applying text alignment and
// display offset.
@@ -402,11 +424,9 @@ class UI_EXPORT RenderText {
private:
friend class RenderTextTest;
-
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, DefaultStyle);
- FRIEND_TEST_ALL_PREFIXES(RenderTextTest, CustomDefaultStyle);
- FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange);
- FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust);
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, SetColorAndStyle);
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyColorAndStyle);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ObscuredText);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, GraphemePositions);
FRIEND_TEST_ALL_PREFIXES(RenderTextTest, EdgeSelectionModels);
@@ -478,10 +498,16 @@ class UI_EXPORT RenderText {
// Composition text range.
ui::Range composition_range_;
- // List of style ranges. Elements in the list never overlap each other.
- StyleRanges style_ranges_;
- // The default text style.
- StyleRange default_style_;
+ // Color and style breaks, used to color and stylize ranges of text.
+ // BreakList positions are stored with text indices, not layout indices.
+ // TODO(msw): Expand to support cursor, selection, background, etc. colors.
+ BreakList<SkColor> colors_;
+ std::vector<BreakList<bool> > styles_;
+
+ // Breaks saved without temporary composition and selection styling.
+ BreakList<SkColor> saved_colors_;
+ BreakList<bool> saved_underlines_;
+ bool composition_and_selection_styles_applied_;
// A flag and the text to display for obscured (password) fields.
// Asterisks are used instead of the actual text glyphs when true.
diff --git a/ui/gfx/render_text_linux.cc b/ui/gfx/render_text_linux.cc
index 3d3cdef..47268ae 100644
--- a/ui/gfx/render_text_linux.cc
+++ b/ui/gfx/render_text_linux.cc
@@ -325,26 +325,33 @@ void RenderTextLinux::EnsureLayout() {
void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) {
PangoAttrList* attrs = pango_attr_list_new();
- int default_font_style = font_list().GetFontStyle();
- for (StyleRanges::const_iterator i = style_ranges().begin();
- i < style_ranges().end(); ++i) {
- // In Pango, different fonts means different runs, and it breaks Arabic
- // shaping across run boundaries. So, set font only when it is different
- // from the default font.
- // TODO(xji): We'll eventually need to split up StyleRange into components
- // (ColorRange, FontRange, etc.) so that we can combine adjacent ranges
- // with the same Fonts (to avoid unnecessarily splitting up runs).
- if (i->font_style != default_font_style) {
- FontList derived_font_list = font_list().DeriveFontList(i->font_style);
+ // Splitting text runs to accommodate styling can break Arabic glyph shaping.
+ // Only split text runs as needed for bold and italic font styles changes.
+ BreakList<bool>::const_iterator bold = styles()[BOLD].breaks().begin();
+ BreakList<bool>::const_iterator italic = styles()[ITALIC].breaks().begin();
+ while (bold != styles()[BOLD].breaks().end() &&
+ italic != styles()[ITALIC].breaks().end()) {
+ const int style = (bold->second ? Font::BOLD : 0) |
+ (italic->second ? Font::ITALIC : 0);
+ const size_t bold_end = styles()[BOLD].GetRange(bold).end();
+ const size_t italic_end = styles()[ITALIC].GetRange(italic).end();
+ const size_t style_end = std::min(bold_end, italic_end);
+ if (style != font_list().GetFontStyle()) {
+ FontList derived_font_list = font_list().DeriveFontList(style);
ScopedPangoFontDescription desc(pango_font_description_from_string(
derived_font_list.GetFontDescriptionString().c_str()));
PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get());
- pango_attr->start_index = TextIndexToLayoutIndex(i->range.start());
- pango_attr->end_index = TextIndexToLayoutIndex(i->range.end());
+ pango_attr->start_index =
+ TextIndexToLayoutIndex(std::max(bold->first, italic->first));
+ pango_attr->end_index = TextIndexToLayoutIndex(style_end);
pango_attr_list_insert(attrs, pango_attr);
}
+ bold += bold_end == style_end ? 1 : 0;
+ italic += italic_end == style_end ? 1 : 0;
}
+ DCHECK(bold == styles()[BOLD].breaks().end());
+ DCHECK(italic == styles()[ITALIC].breaks().end());
pango_layout_set_attributes(layout, attrs);
pango_attr_list_unref(attrs);
@@ -363,20 +370,6 @@ void RenderTextLinux::DrawVisualText(Canvas* canvas) {
std::vector<SkPoint> pos;
std::vector<uint16> glyphs;
- StyleRanges styles(style_ranges());
- ApplyCompositionAndSelectionStyles(&styles);
-
- // Pre-calculate UTF8 indices from UTF16 indices.
- // TODO(asvitkine): Can we cache these?
- std::vector<ui::Range> style_ranges_utf8;
- style_ranges_utf8.reserve(styles.size());
- size_t start_index = 0;
- for (size_t i = 0; i < styles.size(); ++i) {
- size_t end_index = TextIndexToLayoutIndex(styles[i].range.end());
- style_ranges_utf8.push_back(ui::Range(start_index, end_index));
- start_index = end_index;
- }
-
internal::SkiaTextRenderer renderer(canvas);
ApplyFadeEffects(&renderer);
ApplyTextShadows(&renderer);
@@ -391,27 +384,16 @@ void RenderTextLinux::DrawVisualText(Canvas* canvas) {
render_params.antialiasing,
use_subpixel_rendering && !background_is_transparent());
+ // Temporarily apply composition underlines and selection colors.
+ ApplyCompositionAndSelectionStyles();
+
+ internal::StyleIterator style(colors(), styles());
for (GSList* it = current_line_->runs; it; it = it->next) {
PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data);
int glyph_count = run->glyphs->num_glyphs;
if (glyph_count == 0)
continue;
- size_t run_start = run->item->offset;
- size_t first_glyph_byte_index = run_start + run->glyphs->log_clusters[0];
- size_t style_increment = IsForwardMotion(CURSOR_RIGHT, run->item) ? 1 : -1;
-
- // Find the initial style for this run.
- // TODO(asvitkine): Can we avoid looping here, e.g. by caching this per run?
- int style = -1;
- for (size_t i = 0; i < style_ranges_utf8.size(); ++i) {
- if (IndexInRange(style_ranges_utf8[i], first_glyph_byte_index)) {
- style = i;
- break;
- }
- }
- DCHECK_GE(style, 0);
-
ScopedPangoFontDescription desc(
pango_font_describe(run->item->analysis.font));
@@ -419,60 +401,59 @@ void RenderTextLinux::DrawVisualText(Canvas* canvas) {
pango_font_description_get_family(desc.get());
renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get()));
- SkScalar glyph_x = x;
- SkScalar start_x = x;
- int start = 0;
-
glyphs.resize(glyph_count);
pos.resize(glyph_count);
- for (int i = 0; i < glyph_count; ++i) {
- const PangoGlyphInfo& glyph = run->glyphs->glyphs[i];
- glyphs[i] = static_cast<uint16>(glyph.glyph);
- // Use pango_units_to_double() rather than PANGO_PIXELS() here so that
- // units won't get rounded to the pixel grid if we're using subpixel
- // positioning.
- pos[i].set(glyph_x + pango_units_to_double(glyph.geometry.x_offset),
- y + pango_units_to_double(glyph.geometry.y_offset));
-
- // If this glyph is beyond the current style, draw the glyphs so far and
- // advance to the next style.
- size_t glyph_byte_index = run_start + run->glyphs->log_clusters[i];
- DCHECK_GE(style, 0);
- DCHECK_LT(style, static_cast<int>(styles.size()));
- if (!IndexInRange(style_ranges_utf8[style], glyph_byte_index)) {
+ // Track the current glyph and the glyph at the start of its styled range.
+ int glyph_index = 0;
+ int style_start_glyph_index = glyph_index;
+
+ // Track the x-coordinates for each styled range (|x| marks the current).
+ SkScalar style_start_x = x;
+
+ // Track the current style and its text (not layout) index range.
+ style.UpdatePosition(GetGlyphTextIndex(run, style_start_glyph_index));
+ ui::Range style_range = style.GetRange();
+
+ do {
+ const PangoGlyphInfo& glyph = run->glyphs->glyphs[glyph_index];
+ glyphs[glyph_index] = static_cast<uint16>(glyph.glyph);
+ // Use pango_units_to_double() rather than PANGO_PIXELS() here, so units
+ // are not rounded to the pixel grid if subpixel positioning is enabled.
+ pos[glyph_index].set(x + pango_units_to_double(glyph.geometry.x_offset),
+ y + pango_units_to_double(glyph.geometry.y_offset));
+ x += pango_units_to_double(glyph.geometry.width);
+
+ ++glyph_index;
+ const size_t glyph_text_index = (glyph_index == glyph_count) ?
+ style_range.end() : GetGlyphTextIndex(run, glyph_index);
+ if (!IndexInRange(style_range, glyph_text_index)) {
// TODO(asvitkine): For cases like "fi", where "fi" is a single glyph
// but can span multiple styles, Pango splits the
// styles evenly over the glyph. We can do this too by
// clipping and drawing the glyph several times.
- renderer.SetForegroundColor(styles[style].foreground);
- renderer.SetFontFamilyWithStyle(family_name, styles[style].font_style);
- renderer.DrawPosText(&pos[start], &glyphs[start], i - start);
- if (styles[style].underline)
+ renderer.SetForegroundColor(style.color());
+ const int font_style = (style.style(BOLD) ? Font::BOLD : 0) |
+ (style.style(ITALIC) ? Font::ITALIC : 0);
+ renderer.SetFontFamilyWithStyle(family_name, font_style);
+ renderer.DrawPosText(&pos[style_start_glyph_index],
+ &glyphs[style_start_glyph_index],
+ glyph_index - style_start_glyph_index);
+ if (style.style(UNDERLINE))
SetPangoUnderlineMetrics(desc.get(), &renderer);
- renderer.DrawDecorations(start_x, y, glyph_x - start_x, styles[style]);
-
- start = i;
- start_x = glyph_x;
- // Loop to find the next style, in case the glyph spans multiple styles.
- do {
- style += style_increment;
- } while (style >= 0 && style < static_cast<int>(styles.size()) &&
- !IndexInRange(style_ranges_utf8[style], glyph_byte_index));
+ renderer.DrawDecorations(style_start_x, y, x - style_start_x,
+ style.style(UNDERLINE), style.style(STRIKE),
+ style.style(DIAGONAL_STRIKE));
+ style.UpdatePosition(glyph_text_index);
+ style_range = style.GetRange();
+ style_start_glyph_index = glyph_index;
+ style_start_x = x;
}
-
- glyph_x += pango_units_to_double(glyph.geometry.width);
- }
-
- // Draw the remaining glyphs.
- renderer.SetForegroundColor(styles[style].foreground);
- renderer.SetFontFamilyWithStyle(family_name, styles[style].font_style);
- renderer.DrawPosText(&pos[start], &glyphs[start], glyph_count - start);
- if (styles[style].underline)
- SetPangoUnderlineMetrics(desc.get(), &renderer);
- renderer.DrawDecorations(start_x, y, glyph_x - start_x, styles[style]);
- x = glyph_x;
+ } while (glyph_index < glyph_count);
}
+
+ // Undo the temporarily applied composition underlines and selection colors.
+ UndoCompositionAndSelectionStyles();
}
GSList* RenderTextLinux::GetRunContainingCaret(
@@ -538,6 +519,12 @@ std::vector<Rect> RenderTextLinux::GetSelectionBounds() {
return selection_visual_bounds_;
}
+size_t RenderTextLinux::GetGlyphTextIndex(PangoLayoutRun* run,
+ int glyph_index) const {
+ return LayoutIndexToTextIndex(run->item->offset +
+ run->glyphs->log_clusters[glyph_index]);
+}
+
RenderText* RenderText::CreateInstance() {
return new RenderTextLinux;
}
diff --git a/ui/gfx/render_text_linux.h b/ui/gfx/render_text_linux.h
index 12f61ba..2abdf2b 100644
--- a/ui/gfx/render_text_linux.h
+++ b/ui/gfx/render_text_linux.h
@@ -45,6 +45,9 @@ class RenderTextLinux : public RenderText {
virtual void DrawVisualText(Canvas* canvas) OVERRIDE;
private:
+ friend class RenderTextTest;
+ FRIEND_TEST_ALL_PREFIXES(RenderTextTest, PangoAttributes);
+
// Returns the run that contains the character attached to the caret in the
// given selection model. Return NULL if not found.
GSList* GetRunContainingCaret(const SelectionModel& caret) const;
@@ -71,6 +74,9 @@ class RenderTextLinux : public RenderText {
// Get the visual bounds of the logical selection.
std::vector<Rect> GetSelectionBounds();
+ // Get the text index corresponding to the |run|'s |glyph_index|.
+ size_t GetGlyphTextIndex(PangoLayoutRun* run, int glyph_index) const;
+
// Pango Layout.
PangoLayout* layout_;
// A single line layout resulting from laying out via |layout_|.
diff --git a/ui/gfx/render_text_mac.cc b/ui/gfx/render_text_mac.cc
index a636714..5eafdb0 100644
--- a/ui/gfx/render_text_mac.cc
+++ b/ui/gfx/render_text_mac.cc
@@ -158,7 +158,7 @@ void RenderTextMac::DrawVisualText(Canvas* canvas) {
renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
run.glyphs.size());
renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
- run.style);
+ run.underline, run.strike, run.diagonal_strike);
}
}
@@ -168,7 +168,10 @@ RenderTextMac::TextRun::TextRun()
width(0),
font_style(Font::NORMAL),
text_size(0),
- foreground(SK_ColorBLACK) {
+ foreground(SK_ColorBLACK),
+ underline(false),
+ strike(false),
+ diagonal_strike(false) {
}
RenderTextMac::TextRun::~TextRun() {
@@ -176,44 +179,40 @@ RenderTextMac::TextRun::~TextRun() {
void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
CTFontRef font) {
- // Clear attributes and reserve space to hold the maximum number of entries,
- // which is at most three per style range per the code below.
- attributes_.reset(CFArrayCreateMutable(NULL, 3 * style_ranges().size(),
- &kCFTypeArrayCallBacks));
+ // Temporarily apply composition underlines and selection colors.
+ ApplyCompositionAndSelectionStyles();
- // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
- for (size_t i = 0; i < style_ranges().size(); ++i) {
- const StyleRange& style = style_ranges()[i];
- const CFRange range = CFRangeMake(style.range.start(),
- style.range.length());
-
- // Note: CFAttributedStringSetAttribute() does not appear to retain the
- // values passed in, as can be verified via CFGetRetainCount(). To ensure
- // the attribute objects do not leak, they are saved to |attributes_|.
+ // Note: CFAttributedStringSetAttribute() does not appear to retain the values
+ // passed in, as can be verified via CFGetRetainCount(). To ensure the
+ // attribute objects do not leak, they are saved to |attributes_|.
+ // Clear the attributes storage.
+ attributes_.reset(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
+ // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
+ internal::StyleIterator style(colors(), styles());
+ const size_t layout_text_length = GetLayoutText().length();
+ for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
+ end = TextIndexToLayoutIndex(style.GetRange().end());
+ const CFRange range = CFRangeMake(i, end - i);
base::mac::ScopedCFTypeRef<CGColorRef> foreground(
- gfx::CGColorCreateFromSkColor(style.foreground));
+ gfx::CGColorCreateFromSkColor(style.color()));
CFAttributedStringSetAttribute(attr_string, range,
- kCTForegroundColorAttributeName,
- foreground);
+ kCTForegroundColorAttributeName, foreground);
CFArrayAppendValue(attributes_, foreground);
- if (style.underline) {
+ if (style.style(UNDERLINE)) {
CTUnderlineStyle value = kCTUnderlineStyleSingle;
- base::mac::ScopedCFTypeRef<CFNumberRef> underline(
+ base::mac::ScopedCFTypeRef<CFNumberRef> underline_value(
CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
CFAttributedStringSetAttribute(attr_string, range,
kCTUnderlineStyleAttributeName,
- underline);
- CFArrayAppendValue(attributes_, underline);
+ underline_value);
+ CFArrayAppendValue(attributes_, underline_value);
}
- if (style.font_style & (Font::BOLD | Font::ITALIC)) {
- int traits = 0;
- if (style.font_style & Font::BOLD)
- traits |= kCTFontBoldTrait;
- if (style.font_style & Font::ITALIC)
- traits |= kCTFontItalicTrait;
+ const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
+ (style.style(ITALIC) ? kCTFontItalicTrait : 0);
+ if (traits != 0) {
base::mac::ScopedCFTypeRef<CTFontRef> styled_font(
CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits));
// TODO(asvitkine): Handle |styled_font| == NULL case better.
@@ -223,7 +222,12 @@ void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
CFArrayAppendValue(attributes_, styled_font);
}
}
+
+ style.UpdatePosition(LayoutIndexToTextIndex(end));
}
+
+ // Undo the temporarily applied composition underlines and selection colors.
+ UndoCompositionAndSelectionStyles();
}
void RenderTextMac::ComputeRuns() {
@@ -283,7 +287,7 @@ void RenderTextMac::ComputeRuns() {
}
// TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
- // this better.
+ // this better. Also, support strike and diagonal_strike.
CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
CTFontRef ct_font =
base::mac::GetValueFromDictionary<CTFontRef>(attributes,
@@ -310,7 +314,7 @@ void RenderTextMac::ComputeRuns() {
attributes, kCTUnderlineStyleAttributeName);
CTUnderlineStyle value = kCTUnderlineStyleNone;
if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
- run->style.underline = (value == kCTUnderlineStyleSingle);
+ run->underline = (value == kCTUnderlineStyleSingle);
run_origin.offset(run_width, 0);
}
diff --git a/ui/gfx/render_text_mac.h b/ui/gfx/render_text_mac.h
index 3bbfae4..ba73945 100644
--- a/ui/gfx/render_text_mac.h
+++ b/ui/gfx/render_text_mac.h
@@ -61,7 +61,9 @@ class RenderTextMac : public RenderText {
int font_style;
SkScalar text_size;
SkColor foreground;
- StyleRange style;
+ bool underline;
+ bool strike;
+ bool diagonal_strike;
TextRun();
~TextRun();
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index e27135e..3935f7f 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -7,12 +7,16 @@
#include "base/memory/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/text_constants.h"
+#include "ui/gfx/break_list.h"
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
+#if defined(OS_LINUX)
+#include "ui/gfx/render_text_linux.h"
+#endif
+
#if defined(TOOLKIT_GTK)
#include <gtk/gtk.h>
#endif
@@ -56,267 +60,146 @@ class RenderTextTest : public testing::Test {
};
TEST_F(RenderTextTest, DefaultStyle) {
- // Defaults to empty text with no styles.
+ // Check the default styles applied to new instances and adjusted text.
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
EXPECT_TRUE(render_text->text().empty());
- EXPECT_TRUE(render_text->style_ranges().empty());
-
- // Test that the built-in default style is applied for new text.
- render_text->SetText(ASCIIToUTF16("abc"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- StyleRange style;
- EXPECT_EQ(style.foreground, render_text->style_ranges()[0].foreground);
- EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
- EXPECT_EQ(style.strike, render_text->style_ranges()[0].strike);
- EXPECT_EQ(style.underline, render_text->style_ranges()[0].underline);
-
- // Test that clearing the text also clears the styles.
- render_text->SetText(string16());
- EXPECT_TRUE(render_text->text().empty());
- EXPECT_TRUE(render_text->style_ranges().empty());
+ const wchar_t* const cases[] = { kWeak, kLtr, L"Hello", kRtl, L"", L"" };
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ EXPECT_TRUE(render_text->colors().EqualsValueForTesting(SK_ColorBLACK));
+ for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
+ EXPECT_TRUE(render_text->styles()[style].EqualsValueForTesting(false));
+ render_text->SetText(WideToUTF16(cases[i]));
+ }
}
-TEST_F(RenderTextTest, CustomDefaultStyle) {
- // Test a custom default style.
+TEST_F(RenderTextTest, SetColorAndStyle) {
+ // Ensure custom default styles persist across setting and clearing text.
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
- StyleRange color;
- color.foreground = SK_ColorRED;
- render_text->set_default_style(color);
- render_text->SetText(ASCIIToUTF16("abc"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(color.foreground, render_text->style_ranges()[0].foreground);
-
- // Test that the custom default style persists across clearing text.
- render_text->SetText(string16());
- EXPECT_TRUE(render_text->style_ranges().empty());
- render_text->SetText(ASCIIToUTF16("abc"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(color.foreground, render_text->style_ranges()[0].foreground);
-
- // Test ApplyDefaultStyle after setting a new default.
- StyleRange strike;
- strike.strike = true;
- render_text->set_default_style(strike);
- render_text->ApplyDefaultStyle();
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_TRUE(render_text->style_ranges()[0].strike);
- EXPECT_EQ(strike.foreground, render_text->style_ranges()[0].foreground);
+ const SkColor color = SK_ColorRED;
+ render_text->SetColor(color);
+ render_text->SetStyle(BOLD, true);
+ render_text->SetStyle(UNDERLINE, false);
+ const wchar_t* const cases[] = { kWeak, kLtr, L"Hello", kRtl, L"", L"" };
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ EXPECT_TRUE(render_text->colors().EqualsValueForTesting(color));
+ EXPECT_TRUE(render_text->styles()[BOLD].EqualsValueForTesting(true));
+ EXPECT_TRUE(render_text->styles()[UNDERLINE].EqualsValueForTesting(false));
+ render_text->SetText(WideToUTF16(cases[i]));
+
+ // Ensure custom default styles can be applied after text has been set.
+ if (i == 1)
+ render_text->SetStyle(STRIKE, true);
+ if (i >= 1)
+ EXPECT_TRUE(render_text->styles()[STRIKE].EqualsValueForTesting(true));
+ }
}
-TEST_F(RenderTextTest, ApplyStyleRange) {
+TEST_F(RenderTextTest, ApplyColorAndStyle) {
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
+ render_text->SetText(ASCIIToUTF16("012345678"));
+
+ // Apply a ranged color and style and check the resulting breaks.
+ render_text->ApplyColor(SK_ColorRED, ui::Range(1, 4));
+ render_text->ApplyStyle(BOLD, true, ui::Range(2, 5));
+ std::vector<std::pair<size_t, SkColor> > expected_color;
+ expected_color.push_back(std::pair<size_t, SkColor>(0, SK_ColorBLACK));
+ expected_color.push_back(std::pair<size_t, SkColor>(1, SK_ColorRED));
+ expected_color.push_back(std::pair<size_t, SkColor>(4, SK_ColorBLACK));
+ EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color));
+ std::vector<std::pair<size_t, bool> > expected_style;
+ expected_style.push_back(std::pair<size_t, bool>(0, false));
+ expected_style.push_back(std::pair<size_t, bool>(2, true));
+ expected_style.push_back(std::pair<size_t, bool>(5, false));
+ EXPECT_TRUE(render_text->styles()[BOLD].EqualsForTesting(expected_style));
+
+ // Ensure setting a color and style overrides the ranged colors and styles.
+ render_text->SetColor(SK_ColorBLUE);
+ EXPECT_TRUE(render_text->colors().EqualsValueForTesting(SK_ColorBLUE));
+ render_text->SetStyle(BOLD, false);
+ EXPECT_TRUE(render_text->styles()[BOLD].EqualsValueForTesting(false));
+
+ // Apply a color and style over the text end and check the resulting breaks.
+ // (INT_MAX should be used instead of the text length for the range end)
+ const size_t text_length = render_text->text().length();
+ render_text->ApplyColor(SK_ColorRED, ui::Range(0, text_length));
+ render_text->ApplyStyle(BOLD, true, ui::Range(2, text_length));
+ std::vector<std::pair<size_t, SkColor> > expected_color_end;
+ expected_color_end.push_back(std::pair<size_t, SkColor>(0, SK_ColorRED));
+ EXPECT_TRUE(render_text->colors().EqualsForTesting(expected_color_end));
+ std::vector<std::pair<size_t, bool> > expected_style_end;
+ expected_style_end.push_back(std::pair<size_t, bool>(0, false));
+ expected_style_end.push_back(std::pair<size_t, bool>(2, true));
+ EXPECT_TRUE(render_text->styles()[BOLD].EqualsForTesting(expected_style_end));
+
+ // Ensure ranged values adjust to accommodate text length changes.
+ render_text->ApplyStyle(ITALIC, true, ui::Range(0, 2));
+ render_text->ApplyStyle(ITALIC, true, ui::Range(3, 6));
+ render_text->ApplyStyle(ITALIC, true, ui::Range(7, text_length));
+ std::vector<std::pair<size_t, bool> > expected_italic;
+ expected_italic.push_back(std::pair<size_t, bool>(0, true));
+ expected_italic.push_back(std::pair<size_t, bool>(2, false));
+ expected_italic.push_back(std::pair<size_t, bool>(3, true));
+ expected_italic.push_back(std::pair<size_t, bool>(6, false));
+ expected_italic.push_back(std::pair<size_t, bool>(7, true));
+ EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
+
+ // Truncating the text should trim any corresponding breaks.
+ render_text->SetText(ASCIIToUTF16("0123456"));
+ expected_italic.resize(4);
+ EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
render_text->SetText(ASCIIToUTF16("01234"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
-
- // Test ApplyStyleRange (no-op on empty range).
- StyleRange empty;
- empty.range = ui::Range(1, 1);
- render_text->ApplyStyleRange(empty);
- EXPECT_EQ(1U, render_text->style_ranges().size());
-
- // Test ApplyStyleRange (no-op on invalid range).
- StyleRange invalid;
- invalid.range = ui::Range::InvalidRange();
- render_text->ApplyStyleRange(invalid);
- EXPECT_EQ(1U, render_text->style_ranges().size());
-
- // Apply a style with a range contained by an existing range.
- StyleRange underline;
- underline.underline = true;
- underline.range = ui::Range(2, 3);
- render_text->ApplyStyleRange(underline);
- EXPECT_EQ(3U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
- EXPECT_FALSE(render_text->style_ranges()[0].underline);
- EXPECT_EQ(ui::Range(2, 3), render_text->style_ranges()[1].range);
- EXPECT_TRUE(render_text->style_ranges()[1].underline);
- EXPECT_EQ(ui::Range(3, 5), render_text->style_ranges()[2].range);
- EXPECT_FALSE(render_text->style_ranges()[2].underline);
-
- // Apply a style with a range equal to another range.
- StyleRange color;
- color.foreground = SK_ColorWHITE;
- color.range = ui::Range(2, 3);
- render_text->ApplyStyleRange(color);
- EXPECT_EQ(3U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
- EXPECT_NE(SK_ColorWHITE, render_text->style_ranges()[0].foreground);
- EXPECT_FALSE(render_text->style_ranges()[0].underline);
- EXPECT_EQ(ui::Range(2, 3), render_text->style_ranges()[1].range);
- EXPECT_EQ(SK_ColorWHITE, render_text->style_ranges()[1].foreground);
- EXPECT_FALSE(render_text->style_ranges()[1].underline);
- EXPECT_EQ(ui::Range(3, 5), render_text->style_ranges()[2].range);
- EXPECT_NE(SK_ColorWHITE, render_text->style_ranges()[2].foreground);
- EXPECT_FALSE(render_text->style_ranges()[2].underline);
-
- // Apply a style with a range containing an existing range.
- // This new style also overlaps portions of neighboring ranges.
- StyleRange strike;
- strike.strike = true;
- strike.range = ui::Range(1, 4);
- render_text->ApplyStyleRange(strike);
- EXPECT_EQ(3U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 1), render_text->style_ranges()[0].range);
- EXPECT_FALSE(render_text->style_ranges()[0].strike);
- EXPECT_EQ(ui::Range(1, 4), render_text->style_ranges()[1].range);
- EXPECT_TRUE(render_text->style_ranges()[1].strike);
- EXPECT_EQ(ui::Range(4, 5), render_text->style_ranges()[2].range);
- EXPECT_FALSE(render_text->style_ranges()[2].strike);
-
- // Apply a style overlapping all ranges.
- StyleRange strike_underline;
- strike_underline.strike = true;
- strike_underline.underline = true;
- strike_underline.range = ui::Range(0, render_text->text().length());
- render_text->ApplyStyleRange(strike_underline);
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 5), render_text->style_ranges()[0].range);
- EXPECT_TRUE(render_text->style_ranges()[0].underline);
- EXPECT_TRUE(render_text->style_ranges()[0].strike);
-
- // Apply the default style.
- render_text->ApplyDefaultStyle();
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 5), render_text->style_ranges()[0].range);
- EXPECT_FALSE(render_text->style_ranges()[0].underline);
- EXPECT_FALSE(render_text->style_ranges()[0].strike);
-
- // Apply new style range that contains the 2nd last old style range.
- render_text->SetText(ASCIIToUTF16("abcdefghi"));
- underline.range = ui::Range(0, 3);
- render_text->ApplyStyleRange(underline);
- color.range = ui::Range(3, 6);
- render_text->ApplyStyleRange(color);
- strike.range = ui::Range(6, 9);
- render_text->ApplyStyleRange(strike);
- EXPECT_EQ(3U, render_text->style_ranges().size());
-
- color.foreground = SK_ColorRED;
- color.range = ui::Range(2, 8);
- render_text->ApplyStyleRange(color);
- EXPECT_EQ(3U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
- EXPECT_TRUE(render_text->style_ranges()[0].underline);
- EXPECT_EQ(ui::Range(2, 8), render_text->style_ranges()[1].range);
- EXPECT_EQ(SK_ColorRED, render_text->style_ranges()[1].foreground);
- EXPECT_EQ(ui::Range(8, 9), render_text->style_ranges()[2].range);
- EXPECT_TRUE(render_text->style_ranges()[2].strike);
-
- // Apply new style range that contains multiple old style ranges.
- render_text->SetText(ASCIIToUTF16("abcdefghiopq"));
- underline.range = ui::Range(0, 3);
- render_text->ApplyStyleRange(underline);
- color.range = ui::Range(3, 6);
- render_text->ApplyStyleRange(color);
- strike.range = ui::Range(6, 9);
- render_text->ApplyStyleRange(strike);
- strike_underline.range = ui::Range(9, 12);
- render_text->ApplyStyleRange(strike_underline);
- EXPECT_EQ(4U, render_text->style_ranges().size());
-
- color.foreground = SK_ColorRED;
- color.range = ui::Range(2, 10);
- render_text->ApplyStyleRange(color);
- EXPECT_EQ(3U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
- EXPECT_TRUE(render_text->style_ranges()[0].underline);
- EXPECT_EQ(ui::Range(2, 10), render_text->style_ranges()[1].range);
- EXPECT_EQ(SK_ColorRED, render_text->style_ranges()[1].foreground);
- EXPECT_EQ(ui::Range(10, 12), render_text->style_ranges()[2].range);
- EXPECT_TRUE(render_text->style_ranges()[2].underline);
- EXPECT_TRUE(render_text->style_ranges()[2].strike);
-}
-
-static void SetTextWith2ExtraStyles(RenderText* render_text) {
- render_text->SetText(ASCIIToUTF16("abcdefghi"));
+ expected_italic.resize(3);
+ EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
- StyleRange strike;
- strike.strike = true;
- strike.range = ui::Range(0, 3);
- render_text->ApplyStyleRange(strike);
-
- StyleRange underline;
- underline.underline = true;
- underline.range = ui::Range(3, 6);
- render_text->ApplyStyleRange(underline);
+ // Appending text should extend the terminal styles without changing breaks.
+ render_text->SetText(ASCIIToUTF16("012345678"));
+ EXPECT_TRUE(render_text->styles()[ITALIC].EqualsForTesting(expected_italic));
}
-TEST_F(RenderTextTest, StyleRangesAdjust) {
- // Test that style ranges adjust to the text size.
+#if defined(OS_LINUX)
+TEST_F(RenderTextTest, PangoAttributes) {
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
- render_text->SetText(ASCIIToUTF16("abcdef"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 6), render_text->style_ranges()[0].range);
+ render_text->SetText(ASCIIToUTF16("012345678"));
- // Test that the range is clipped to the length of shorter text.
- render_text->SetText(ASCIIToUTF16("abc"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
-
- // Test that the last range extends to the length of longer text.
- StyleRange strike;
- strike.strike = true;
- strike.range = ui::Range(2, 3);
- render_text->ApplyStyleRange(strike);
- render_text->SetText(ASCIIToUTF16("abcdefghi"));
- EXPECT_EQ(2U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
- EXPECT_EQ(ui::Range(2, 9), render_text->style_ranges()[1].range);
- EXPECT_TRUE(render_text->style_ranges()[1].strike);
-
- // Test that ranges are removed if they're outside the range of shorter text.
- render_text->SetText(ASCIIToUTF16("ab"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 2), render_text->style_ranges()[0].range);
- EXPECT_FALSE(render_text->style_ranges()[0].strike);
-
- // Test that previously removed ranges don't return.
- render_text->SetText(ASCIIToUTF16("abcdef"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 6), render_text->style_ranges()[0].range);
- EXPECT_FALSE(render_text->style_ranges()[0].strike);
-
- // Test that ranges are removed correctly if they are outside the range of
- // shorter text.
- SetTextWith2ExtraStyles(render_text.get());
- EXPECT_EQ(3U, render_text->style_ranges().size());
-
- render_text->SetText(ASCIIToUTF16("abcdefg"));
- EXPECT_EQ(3U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
- EXPECT_EQ(ui::Range(3, 6), render_text->style_ranges()[1].range);
- EXPECT_EQ(ui::Range(6, 7), render_text->style_ranges()[2].range);
+ // Apply ranged BOLD/ITALIC styles and check the resulting Pango attributes.
+ render_text->ApplyStyle(BOLD, true, ui::Range(2, 4));
+ render_text->ApplyStyle(ITALIC, true, ui::Range(1, 3));
- SetTextWith2ExtraStyles(render_text.get());
- EXPECT_EQ(3U, render_text->style_ranges().size());
-
- render_text->SetText(ASCIIToUTF16("abcdef"));
- EXPECT_EQ(2U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
- EXPECT_EQ(ui::Range(3, 6), render_text->style_ranges()[1].range);
-
- SetTextWith2ExtraStyles(render_text.get());
- EXPECT_EQ(3U, render_text->style_ranges().size());
-
- render_text->SetText(ASCIIToUTF16("abcde"));
- EXPECT_EQ(2U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
- EXPECT_EQ(ui::Range(3, 5), render_text->style_ranges()[1].range);
-
- SetTextWith2ExtraStyles(render_text.get());
- EXPECT_EQ(3U, render_text->style_ranges().size());
-
- render_text->SetText(ASCIIToUTF16("abc"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 3), render_text->style_ranges()[0].range);
-
- SetTextWith2ExtraStyles(render_text.get());
- EXPECT_EQ(3U, render_text->style_ranges().size());
+ struct {
+ int start;
+ int end;
+ bool bold;
+ bool italic;
+ } cases[] = {
+ { 0, 1, false, false },
+ { 1, 2, false, true },
+ { 2, 3, true, true },
+ { 3, 4, true, false },
+ { 4, INT_MAX, false, false },
+ };
- render_text->SetText(ASCIIToUTF16("a"));
- EXPECT_EQ(1U, render_text->style_ranges().size());
- EXPECT_EQ(ui::Range(0, 1), render_text->style_ranges()[0].range);
+ int start = 0, end = 0;
+ RenderTextLinux* rt_linux = static_cast<RenderTextLinux*>(render_text.get());
+ rt_linux->EnsureLayout();
+ PangoAttrList* attributes = pango_layout_get_attributes(rt_linux->layout_);
+ PangoAttrIterator* iter = pango_attr_list_get_iterator(attributes);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ pango_attr_iterator_range(iter, &start, &end);
+ EXPECT_EQ(cases[i].start, start);
+ EXPECT_EQ(cases[i].end, end);
+ PangoFontDescription* font = pango_font_description_new();
+ pango_attr_iterator_get_font(iter, font, NULL, NULL);
+ const string16 desc = ASCIIToUTF16(pango_font_description_to_string(font));
+ const bool bold = desc.find(ASCIIToUTF16("Bold")) != std::string::npos;
+ EXPECT_EQ(cases[i].bold, bold);
+ const bool italic = desc.find(ASCIIToUTF16("Italic")) != std::string::npos;
+ EXPECT_EQ(cases[i].italic, italic);
+ pango_attr_iterator_next(iter);
+ pango_font_description_free(font);
+ }
+ EXPECT_FALSE(pango_attr_iterator_next(iter));
}
+#endif
// TODO(asvitkine): Cursor movements tests disabled on Mac because RenderTextMac
// does not implement this yet. http://crbug.com/131618
@@ -1051,33 +934,23 @@ TEST_F(RenderTextTest, StringSizeBoldWidth) {
EXPECT_GT(plain_width, 0);
// Apply a bold style and check that the new width is greater.
- StyleRange bold;
- bold.font_style |= Font::BOLD;
- render_text->set_default_style(bold);
- render_text->ApplyDefaultStyle();
-
+ render_text->SetStyle(gfx::BOLD, true);
const int bold_width = render_text->GetStringSize().width();
EXPECT_GT(bold_width, plain_width);
// Now, apply a plain style over the first word only.
- StyleRange plain;
- plain.font_style = Font::NORMAL;
- plain.range = ui::Range(0, 5);
- render_text->ApplyStyleRange(plain);
-
+ render_text->ApplyStyle(gfx::BOLD, false, ui::Range(0, 5));
const int plain_bold_width = render_text->GetStringSize().width();
EXPECT_GT(plain_bold_width, plain_width);
EXPECT_LT(plain_bold_width, bold_width);
}
TEST_F(RenderTextTest, StringSizeHeight) {
- struct {
- string16 text;
- } cases[] = {
- { WideToUTF16(L"Hello World!") }, // English
- { WideToUTF16(L"\x6328\x62f6") }, // Japanese
- { WideToUTF16(L"\x0915\x093f") }, // Hindi
- { WideToUTF16(L"\x05e0\x05b8") }, // Hebrew
+ string16 cases[] = {
+ WideToUTF16(L"Hello World!"), // English
+ WideToUTF16(L"\x6328\x62f6"), // Japanese
+ WideToUTF16(L"\x0915\x093f"), // Hindi
+ WideToUTF16(L"\x05e0\x05b8"), // Hebrew
};
Font default_font;
@@ -1087,7 +960,7 @@ TEST_F(RenderTextTest, StringSizeHeight) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
scoped_ptr<RenderText> render_text(RenderText::CreateInstance());
render_text->SetFont(default_font);
- render_text->SetText(cases[i].text);
+ render_text->SetText(cases[i]);
const int height1 = render_text->GetStringSize().height();
EXPECT_GT(height1, 0);
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index d17baee..89a0f87 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -111,7 +111,7 @@ void DeriveFontIfNecessary(int font_size,
const int current_style = (font->GetStyle() & kStyleMask);
const int current_size = font->GetFontSize();
if (current_style != target_style || current_size != font_size)
- *font = font->DeriveFont(font_size - current_size, font_style);
+ *font = font->DeriveFont(font_size - current_size, target_style);
}
// Returns true if |c| is a Unicode BiDi control character.
@@ -334,11 +334,9 @@ SelectionModel RenderTextWin::AdjacentWordSelectionModel(
void RenderTextWin::SetSelectionModel(const SelectionModel& model) {
RenderText::SetSelectionModel(model);
- // TODO(xji): The styles are applied to text inside ItemizeLogicalText(). So,
- // we need to update layout here in order for the styles, such as selection
- // foreground, to be picked up. Eventually, we should separate styles from
- // layout by applying foreground, strike, and underline styles during
- // DrawVisualText as what RenderTextLinux does.
+ // TODO(xji|msw): The text selection color is applied in ItemizeLogicalText().
+ // So, the layout must be updated in order to draw the proper selection range.
+ // Colors should be applied in DrawVisualText(), as done by RenderTextLinux.
ResetLayout();
}
@@ -487,15 +485,8 @@ void RenderTextWin::DrawVisualText(Canvas* canvas) {
renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style);
renderer.SetForegroundColor(run->foreground);
renderer.DrawPosText(&pos[0], run->glyphs.get(), run->glyph_count);
- // TODO(oshima|msw): Consider refactoring StyleRange into Style
- // class and StyleRange containing Style, and use Style class in
- // TextRun class. This may conflict with msw's comment in
- // TextRun, so please consult with msw when refactoring.
- StyleRange style;
- style.strike = run->strike;
- style.diagonal_strike = run->diagonal_strike;
- style.underline = run->underline;
- renderer.DrawDecorations(x, y, run->width, style);
+ renderer.DrawDecorations(x, y, run->width, run->underline, run->strike,
+ run->diagonal_strike);
x = glyph_x;
}
@@ -516,7 +507,7 @@ void RenderTextWin::ItemizeLogicalText() {
HRESULT hr = E_OUTOFMEMORY;
int script_items_count = 0;
std::vector<SCRIPT_ITEM> script_items;
- const int text_length = GetLayoutText().length();
+ const size_t text_length = GetLayoutText().length();
for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
// Derive the array of Uniscribe script items from the logical text.
// ScriptItemize always adds a terminal array item so that the length of the
@@ -535,36 +526,41 @@ void RenderTextWin::ItemizeLogicalText() {
if (script_items_count <= 0)
return;
- // Build the list of runs, merge font/underline styles.
- // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
- StyleRanges styles(style_ranges());
- ApplyCompositionAndSelectionStyles(&styles);
- StyleRanges::const_iterator style = styles.begin();
+ // Temporarily apply composition underlines and selection colors.
+ ApplyCompositionAndSelectionStyles();
+
+ // Build the list of runs from the script items and ranged colors/styles.
+ // TODO(msw): Only break for bold/italic, not color etc. See TextRun comment.
+ internal::StyleIterator style(colors(), styles());
SCRIPT_ITEM* script_item = &script_items[0];
- for (int run_break = 0; run_break < text_length;) {
+ const size_t layout_text_length = GetLayoutText().length();
+ for (size_t run_break = 0; run_break < layout_text_length;) {
internal::TextRun* run = new internal::TextRun();
run->range.set_start(run_break);
run->font = GetFont();
- run->font_style = style->font_style;
+ run->font_style = (style.style(BOLD) ? Font::BOLD : 0) |
+ (style.style(ITALIC) ? Font::ITALIC : 0);
DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(),
run->font_style, &run->font);
- run->foreground = style->foreground;
- run->strike = style->strike;
- run->diagonal_strike = style->diagonal_strike;
- run->underline = style->underline;
+ run->foreground = style.color();
+ run->strike = style.style(STRIKE);
+ run->diagonal_strike = style.style(DIAGONAL_STRIKE);
+ run->underline = style.style(UNDERLINE);
run->script_analysis = script_item->a;
- // Find the range end and advance the structures as needed.
- const int script_item_end = (script_item + 1)->iCharPos;
- const int style_range_end = TextIndexToLayoutIndex(style->range.end());
- run_break = std::min(script_item_end, style_range_end);
- if (script_item_end <= style_range_end)
+ // Find the next break and advance the iterators as needed.
+ const size_t script_item_break = (script_item + 1)->iCharPos;
+ run_break = std::min(script_item_break,
+ TextIndexToLayoutIndex(style.GetRange().end()));
+ style.UpdatePosition(LayoutIndexToTextIndex(run_break));
+ if (script_item_break == run_break)
script_item++;
- if (script_item_end >= style_range_end)
- style++;
run->range.set_end(run_break);
runs_.push_back(run);
}
+
+ // Undo the temporarily applied composition underlines and selection colors.
+ UndoCompositionAndSelectionStyles();
}
void RenderTextWin::LayoutVisualText() {
diff --git a/ui/gfx/render_text_win.h b/ui/gfx/render_text_win.h
index 8b42bc9..a419669 100644
--- a/ui/gfx/render_text_win.h
+++ b/ui/gfx/render_text_win.h
@@ -25,14 +25,14 @@ struct TextRun {
ui::Range range;
Font font;
- // TODO(msw): Disambiguate color, strike, etc. from TextRuns.
- // Otherwise, this breaks the glyph shaping process.
- // See the example at: http://www.catch22.net/tuts/neatpad/12.
- SkColor foreground;
// A gfx::Font::FontStyle flag to specify bold and italic styles.
// Supersedes |font.GetFontStyle()|. Stored separately to avoid calling
// |font.DeriveFont()|, which is expensive on Windows.
int font_style;
+
+ // TODO(msw): Disambiguate color/style from TextRuns for proper glyph shaping.
+ // See an example: http://www.catch22.net/tuts/uniscribe-mysteries
+ SkColor foreground;
bool strike;
bool diagonal_strike;
bool underline;
diff --git a/ui/gfx/text_constants.h b/ui/gfx/text_constants.h
index 3c644ab..4ac788e 100644
--- a/ui/gfx/text_constants.h
+++ b/ui/gfx/text_constants.h
@@ -40,6 +40,17 @@ enum DirectionalityMode {
DIRECTIONALITY_FORCE_RTL,
};
+// Text styles and adornments.
+// TODO(msw): Merge with gfx::Font::FontStyle.
+enum TextStyle {
+ BOLD = 0,
+ ITALIC,
+ STRIKE,
+ DIAGONAL_STRIKE,
+ UNDERLINE,
+ NUM_TEXT_STYLES,
+};
+
} // namespace gfx
#endif // UI_GFX_TEXT_CONSTANTS_H_
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 1ebda0e..a3f93d7 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -332,6 +332,7 @@
'gfx/android/window_android.h',
'gfx/blit.cc',
'gfx/blit.h',
+ 'gfx/break_list.h',
'gfx/canvas.cc',
'gfx/canvas.h',
'gfx/canvas_android.cc',
diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi
index 617589d..8a5c9f2 100644
--- a/ui/ui_unittests.gypi
+++ b/ui/ui_unittests.gypi
@@ -124,6 +124,7 @@
'base/text/utf16_indexing_unittest.cc',
'base/view_prop_unittest.cc',
'gfx/blit_unittest.cc',
+ 'gfx/break_list_unittest.cc',
'gfx/canvas_unittest.cc',
'gfx/codec/jpeg_codec_unittest.cc',
'gfx/color_analysis_unittest.cc',
diff --git a/ui/views/controls/link.cc b/ui/views/controls/link.cc
index 04c83ac..94aaa60 100644
--- a/ui/views/controls/link.cc
+++ b/ui/views/controls/link.cc
@@ -204,14 +204,12 @@ void Link::SetPressed(bool pressed) {
}
void Link::RecalculateFont() {
- // The font should be underlined iff the link is enabled and |underline_| is
- // true.
- if ((enabled() && underline_) ==
- !(font().GetStyle() & gfx::Font::UNDERLINED)) {
- Label::SetFont(font().DeriveFont(0, enabled() && underline_ ?
- (font().GetStyle() | gfx::Font::UNDERLINED) :
- (font().GetStyle() & ~gfx::Font::UNDERLINED)));
- }
+ // Underline the link iff it is enabled and |underline_| is true.
+ const int style = font().GetStyle();
+ const int intended_style = (enabled() && underline_) ?
+ (style | gfx::Font::UNDERLINE) : (style & ~gfx::Font::UNDERLINE);
+ if (style != intended_style)
+ Label::SetFont(font().DeriveFont(0, intended_style));
}
} // namespace views
diff --git a/ui/views/controls/textfield/native_textfield_views.cc b/ui/views/controls/textfield/native_textfield_views.cc
index be33761..c8cfb99 100644
--- a/ui/views/controls/textfield/native_textfield_views.cc
+++ b/ui/views/controls/textfield/native_textfield_views.cc
@@ -441,11 +441,7 @@ void NativeTextfieldViews::UpdateBorderColor() {
}
void NativeTextfieldViews::UpdateTextColor() {
- gfx::StyleRange default_style(GetRenderText()->default_style());
- default_style.foreground = textfield_->GetTextColor();
- GetRenderText()->set_default_style(default_style);
- GetRenderText()->ApplyDefaultStyle();
- SchedulePaint();
+ SetColor(textfield_->GetTextColor());
}
void NativeTextfieldViews::UpdateBackgroundColor() {
@@ -698,13 +694,25 @@ void NativeTextfieldViews::ExecuteCommand(int command_id) {
OnAfterUserAction();
}
-void NativeTextfieldViews::ApplyStyleRange(const gfx::StyleRange& style) {
- GetRenderText()->ApplyStyleRange(style);
+void NativeTextfieldViews::SetColor(SkColor value) {
+ GetRenderText()->SetColor(value);
+ SchedulePaint();
+}
+
+void NativeTextfieldViews::ApplyColor(SkColor value, const ui::Range& range) {
+ GetRenderText()->ApplyColor(value, range);
+ SchedulePaint();
+}
+
+void NativeTextfieldViews::SetStyle(gfx::TextStyle style, bool value) {
+ GetRenderText()->SetStyle(style, value);
SchedulePaint();
}
-void NativeTextfieldViews::ApplyDefaultStyle() {
- GetRenderText()->ApplyDefaultStyle();
+void NativeTextfieldViews::ApplyStyle(gfx::TextStyle style,
+ bool value,
+ const ui::Range& range) {
+ GetRenderText()->ApplyStyle(style, value, range);
SchedulePaint();
}
diff --git a/ui/views/controls/textfield/native_textfield_views.h b/ui/views/controls/textfield/native_textfield_views.h
index df878a9..80259d8 100644
--- a/ui/views/controls/textfield/native_textfield_views.h
+++ b/ui/views/controls/textfield/native_textfield_views.h
@@ -130,8 +130,12 @@ class VIEWS_EXPORT NativeTextfieldViews : public TouchSelectionClientView,
virtual void HandleFocus() OVERRIDE;
virtual void HandleBlur() OVERRIDE;
virtual ui::TextInputClient* GetTextInputClient() OVERRIDE;
- virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE;
- virtual void ApplyDefaultStyle() OVERRIDE;
+ virtual void SetColor(SkColor value) OVERRIDE;
+ virtual void ApplyColor(SkColor value, const ui::Range& range) OVERRIDE;
+ virtual void SetStyle(gfx::TextStyle style, bool value) OVERRIDE;
+ virtual void ApplyStyle(gfx::TextStyle style,
+ bool value,
+ const ui::Range& range) OVERRIDE;
virtual void ClearEditHistory() OVERRIDE;
virtual int GetFontHeight() OVERRIDE;
virtual int GetTextfieldBaseline() const OVERRIDE;
diff --git a/ui/views/controls/textfield/native_textfield_win.cc b/ui/views/controls/textfield/native_textfield_win.cc
index 3655f65..48a3fa1 100644
--- a/ui/views/controls/textfield/native_textfield_win.cc
+++ b/ui/views/controls/textfield/native_textfield_win.cc
@@ -413,11 +413,21 @@ ui::TextInputClient* NativeTextfieldWin::GetTextInputClient() {
return NULL;
}
-void NativeTextfieldWin::ApplyStyleRange(const gfx::StyleRange& style) {
+void NativeTextfieldWin::SetColor(SkColor value) {
NOTREACHED();
}
-void NativeTextfieldWin::ApplyDefaultStyle() {
+void NativeTextfieldWin::ApplyColor(SkColor value, const ui::Range& range) {
+ NOTREACHED();
+}
+
+void NativeTextfieldWin::SetStyle(gfx::TextStyle style, bool value) {
+ NOTREACHED();
+}
+
+void NativeTextfieldWin::ApplyStyle(gfx::TextStyle style,
+ bool value,
+ const ui::Range& range) {
NOTREACHED();
}
diff --git a/ui/views/controls/textfield/native_textfield_win.h b/ui/views/controls/textfield/native_textfield_win.h
index 7e072ae..c4a2161 100644
--- a/ui/views/controls/textfield/native_textfield_win.h
+++ b/ui/views/controls/textfield/native_textfield_win.h
@@ -101,8 +101,12 @@ class NativeTextfieldWin
virtual void HandleFocus() OVERRIDE;
virtual void HandleBlur() OVERRIDE;
virtual ui::TextInputClient* GetTextInputClient() OVERRIDE;
- virtual void ApplyStyleRange(const gfx::StyleRange& style) OVERRIDE;
- virtual void ApplyDefaultStyle() OVERRIDE;
+ virtual void SetColor(SkColor value) OVERRIDE;
+ virtual void ApplyColor(SkColor value, const ui::Range& range) OVERRIDE;
+ virtual void SetStyle(gfx::TextStyle style, bool value) OVERRIDE;
+ virtual void ApplyStyle(gfx::TextStyle style,
+ bool value,
+ const ui::Range& range) OVERRIDE;
virtual void ClearEditHistory() OVERRIDE;
virtual int GetFontHeight() OVERRIDE;
virtual int GetTextfieldBaseline() const OVERRIDE;
diff --git a/ui/views/controls/textfield/native_textfield_wrapper.h b/ui/views/controls/textfield/native_textfield_wrapper.h
index 53e4c6c..69dfa6e 100644
--- a/ui/views/controls/textfield/native_textfield_wrapper.h
+++ b/ui/views/controls/textfield/native_textfield_wrapper.h
@@ -8,12 +8,12 @@
#include "base/string16.h"
#include "base/i18n/rtl.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/text_constants.h"
#include "ui/views/views_export.h"
namespace gfx {
class Insets;
class SelectionModel;
-struct StyleRange;
} // namespace gfx
namespace ui {
@@ -148,12 +148,15 @@ class VIEWS_EXPORT NativeTextfieldWrapper {
// support text input.
virtual ui::TextInputClient* GetTextInputClient() = 0;
- // Applies the |style| to the text specified by its range.
- // See |Textfield::ApplyStyleRange| for detail.
- virtual void ApplyStyleRange(const gfx::StyleRange& style) = 0;
+ // Set the text colors; see the corresponding Textfield functions for details.
+ virtual void SetColor(SkColor value) = 0;
+ virtual void ApplyColor(SkColor value, const ui::Range& range) = 0;
- // Applies the default style to the textfield.
- virtual void ApplyDefaultStyle() = 0;
+ // Set the text styles; see the corresponding Textfield functions for details.
+ virtual void SetStyle(gfx::TextStyle style, bool value) = 0;
+ virtual void ApplyStyle(gfx::TextStyle style,
+ bool value,
+ const ui::Range& range) = 0;
// Clears Edit history.
virtual void ClearEditHistory() = 0;
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 191ed35..78304aa 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -368,14 +368,26 @@ size_t Textfield::GetCursorPosition() const {
return native_wrapper_->GetCursorPosition();
}
-void Textfield::ApplyStyleRange(const gfx::StyleRange& style) {
+void Textfield::SetColor(SkColor value) {
DCHECK(native_wrapper_);
- return native_wrapper_->ApplyStyleRange(style);
+ return native_wrapper_->SetColor(value);
}
-void Textfield::ApplyDefaultStyle() {
+void Textfield::ApplyColor(SkColor value, const ui::Range& range) {
DCHECK(native_wrapper_);
- native_wrapper_->ApplyDefaultStyle();
+ return native_wrapper_->ApplyColor(value, range);
+}
+
+void Textfield::SetStyle(gfx::TextStyle style, bool value) {
+ DCHECK(native_wrapper_);
+ return native_wrapper_->SetStyle(style, value);
+}
+
+void Textfield::ApplyStyle(gfx::TextStyle style,
+ bool value,
+ const ui::Range& range) {
+ DCHECK(native_wrapper_);
+ return native_wrapper_->ApplyStyle(style, value, range);
}
void Textfield::ClearEditHistory() {
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index 57509ea..33101b1 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -16,6 +16,7 @@
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/gfx/font.h"
#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/text_constants.h"
#include "ui/views/controls/textfield/native_textfield_wrapper.h"
#include "ui/views/view.h"
@@ -23,10 +24,6 @@
#include "base/logging.h"
#endif
-namespace gfx {
-struct StyleRange;
-} // namespace gfx
-
namespace ui {
class Range;
class TextInputClient;
@@ -209,14 +206,18 @@ class VIEWS_EXPORT Textfield : public View {
// only and has to be called after the wrapper is created.
size_t GetCursorPosition() const;
- // Applies |style| to the text specified by its range. The style will be
- // ignored if range is empty or invalid. This is views-implementation only and
+ // Set the text color over the entire text or a logical character range.
+ // Empty and invalid ranges are ignored. This is views-implementation only and
// has to be called after the wrapper is created.
- void ApplyStyleRange(const gfx::StyleRange& style);
+ void SetColor(SkColor value);
+ void ApplyColor(SkColor value, const ui::Range& range);
- // Applies the default style to the textfield. This is views-implementation
- // only and has to be called after the wrapper is created.
- void ApplyDefaultStyle();
+ // Set various text styles over the entire text or a logical character range.
+ // The respective |style| is applied if |value| is true, or removed if false.
+ // Empty and invalid ranges are ignored. This is views-implementation only and
+ // has to be called after the wrapper is created.
+ void SetStyle(gfx::TextStyle style, bool value);
+ void ApplyStyle(gfx::TextStyle style, bool value, const ui::Range& range);
// Clears Edit history.
void ClearEditHistory();
diff --git a/ui/views/examples/text_example.cc b/ui/views/examples/text_example.cc
index 5fb6662..6ceed4a 100644
--- a/ui/views/examples/text_example.cc
+++ b/ui/views/examples/text_example.cc
@@ -256,7 +256,7 @@ void TextExample::ButtonPressed(Button* button, const ui::Event& event) {
SetFlagFromCheckbox(break_checkbox_, &flags, gfx::Canvas::CHARACTER_BREAK);
SetFlagFromCheckbox(bold_checkbox_, &style, gfx::Font::BOLD);
SetFlagFromCheckbox(italic_checkbox_, &style, gfx::Font::ITALIC);
- SetFlagFromCheckbox(underline_checkbox_, &style, gfx::Font::UNDERLINED);
+ SetFlagFromCheckbox(underline_checkbox_, &style, gfx::Font::UNDERLINE);
text_view_->set_halo(halo_checkbox_->checked());
text_view_->set_text_flags(flags);
text_view_->SetFontStyle(style);
diff --git a/ui/views/examples/textfield_example.cc b/ui/views/examples/textfield_example.cc
index e06a4d6..bdf03d3 100644
--- a/ui/views/examples/textfield_example.cc
+++ b/ui/views/examples/textfield_example.cc
@@ -93,24 +93,21 @@ void TextfieldExample::ButtonPressed(Button* sender, const ui::Event& event) {
name_->SetText(ASCIIToUTF16("[set]"));
} else if (sender == set_style_) {
if (!name_->text().empty()) {
- gfx::StyleRange color;
- color.foreground = SK_ColorYELLOW;
- color.range = ui::Range(0, name_->text().length());
- name_->ApplyStyleRange(color);
+ name_->SetColor(SK_ColorGREEN);
+ name_->SetStyle(gfx::BOLD, true);
if (name_->text().length() >= 5) {
size_t fifth = name_->text().length() / 5;
- gfx::StyleRange underline;
- underline.underline = true;
- underline.foreground = SK_ColorBLUE;
- underline.range = ui::Range(1 * fifth, 4 * fifth);
- name_->ApplyStyleRange(underline);
+ const ui::Range big_range(1 * fifth, 4 * fifth);
+ name_->ApplyStyle(gfx::BOLD, false, big_range);
+ name_->ApplyStyle(gfx::UNDERLINE, true, big_range);
+ name_->ApplyColor(SK_ColorBLUE, big_range);
- gfx::StyleRange strike;
- strike.strike = true;
- strike.foreground = SK_ColorRED;
- strike.range = ui::Range(2 * fifth, 3 * fifth);
- name_->ApplyStyleRange(strike);
+ const ui::Range small_range(2 * fifth, 3 * fifth);
+ name_->ApplyStyle(gfx::ITALIC, true, small_range);
+ name_->ApplyStyle(gfx::UNDERLINE, false, small_range);
+ name_->ApplyStyle(gfx::DIAGONAL_STRIKE, true, small_range);
+ name_->ApplyColor(SK_ColorRED, small_range);
}
}
}