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

Unified Diff: ui/gfx/render_text.cc

Issue 7265011: RenderText API Outline. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add some placeholder functionality on Windows. Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/render_text.cc
diff --git a/ui/gfx/render_text.cc b/ui/gfx/render_text.cc
new file mode 100755
index 0000000000000000000000000000000000000000..594ee75d8a57640fa46286e985d62949e2c9f99f
--- /dev/null
+++ b/ui/gfx/render_text.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2011 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/render_text.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util-inl.h"
+
+namespace {
+
+#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++) {
+ ui::Range& former = style_ranges[i]->range;
+ ui::Range& latter = style_ranges[i + 1]->range;
+ DCHECK(!former.is_empty()) << "Empty range at " << i << ":" << former;
+ DCHECK(former.IsValid()) << "Invalid range at " << i << ":" << former;
+ DCHECK(!former.is_reversed()) << "Reversed range at " << i << ":" << former;
+ DCHECK(former.end() == latter.start()) << "Ranges gap/overlap/unsorted." <<
+ "former:" << former << ", latter:" << latter;
+ }
+ 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
+
+} // namespace
+
+namespace gfx {
+
+void RenderText::SetText(const string16& text) {
+ // TODO(msw): Allow text and styles to get out of sync? Repair on draw?
+ // Update the style ranges as needed.
+ if (text.empty()) {
+ style_ranges_.clear();
+ } else if (style_ranges_.empty()) {
+ StyleRange* style = new StyleRange();
+ style->font = default_font_;
+ style->foreground = default_color_;
+ style->range.set_end(text.length());
+ style_ranges_.push_back(style);
+ } else if (text.length() > text_.length()) {
+ style_ranges_.back()->range.set_end(text.length());
+ } else if (text.length() < text_.length()) {
+ StyleRanges::const_iterator i;
+ for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
+ StyleRange* style = *i;
+ if (style->range.start() > text.length()) {
+ style_ranges_.erase(i);
+ delete style;
+ } else if (style->range.end() > text.length()) {
+ style->range.set_end(text.length());
+ }
+ }
+ }
+#ifndef NDEBUG
+ CheckStyleRanges(style_ranges_, text.length());
+#endif
+
+ text_ = text;
+ // TODO(msw): Mark dirty text flag.
+}
+
+size_t RenderText::GetCursor() const {
+ return selection_range_.end();
+}
+
+void RenderText::SetCursor(const size_t position) {
+ selection_range_.set_end(position);
+ selection_range_.set_start(position);
+}
+
+void RenderText::MoveCursorLeft(bool select, bool move_by_word) {
+ size_t position = selection_range_.end();
+ // Cancelling a selection moves to the edge of the selection.
+ if (!selection_range_.is_empty() && !select) {
+ // Use the selection start if it is left of the selection end.
+ if (GetCursorBounds(selection_range_.start(), false).x() <
+ GetCursorBounds(position, false).x())
+ position = selection_range_.start();
+ // if |move_by_word|, use the nearest word boundary left of the selection.
+ if (move_by_word)
+ position = GetLeftCursorPosition(position, true);
+ } else {
+ position = GetLeftCursorPosition(position, move_by_word);
+ }
+ MoveCursorTo(position, select);
+}
+
+void RenderText::MoveCursorRight(bool select, bool move_by_word) {
+ size_t position = selection_range_.end();
+ // Cancelling a selection moves to the edge of the selection.
+ if (!selection_range_.is_empty() && !select) {
+ // Use the selection start if it is right of the selection end.
+ if (GetCursorBounds(selection_range_.start(), false).x() >
+ GetCursorBounds(position, false).x())
+ position = selection_range_.start();
+ // if |move_by_word|, use the nearest word boundary right of the selection.
+ if (move_by_word)
+ position = GetRightCursorPosition(position, true);
+ } else {
+ position = GetRightCursorPosition(position, move_by_word);
+ }
+ MoveCursorTo(position, select);
+}
+
+void RenderText::MoveCursorToLeftEnd(bool select) {
+ // TODO(msw) Bidi.
+ MoveCursorTo(0, select);
+}
+
+void RenderText::MoveCursorToRightEnd(bool select) {
+ // TODO(msw) Bidi.
+ MoveCursorTo(text().length(), select);
+}
+
+void RenderText::MoveCursorTo(size_t position, bool select) {
+ selection_range_.set_end(position);
+ if (!select)
+ selection_range_.set_start(position);
+}
+
+const ui::Range& RenderText::GetSelection() const {
+ return selection_range_;
+}
+
+void RenderText::SetSelection(const ui::Range& selection_range) {
+ selection_range_.set_end(selection_range.end());
+ selection_range_.set_start(selection_range.start());
+}
+
+void RenderText::ClearSelection() {
+ selection_range_.set_start(GetCursor());
+}
+
+void RenderText::SelectAll() {
+ SetSelection(ui::Range(0, text().length()));
+}
+
+void RenderText::SelectWord() {
+ // TODO(msw): Bidi impl?
+ size_t selection_start = GetSelection().start();
+ size_t cursor_position = GetCursor();
+ // First we setup selection_start_ and cursor_pos_. There are so many cases
+ // because we try to emulate what select-word looks like in a gtk textfield.
+ // See associated testcase for different cases.
+ if (cursor_position > 0 && cursor_position < text().length()) {
+ if (isalnum(text()[cursor_position])) {
+ selection_start = cursor_position;
+ cursor_position++;
+ } else
+ selection_start = cursor_position - 1;
+ } else if (cursor_position == 0) {
+ selection_start = cursor_position;
+ if (text().length() > 0)
+ cursor_position++;
+ } else {
+ selection_start = cursor_position - 1;
+ }
+
+ // Now we move selection_start_ to beginning of selection. Selection boundary
+ // is defined as the position where we have alpha-num character on one side
+ // and non-alpha-num char on the other side.
+ for (; selection_start > 0; selection_start--) {
+ if (IsPositionAtWordSelectionBoundary(selection_start))
+ break;
+ }
+
+ // Now we move cursor_pos_ to end of selection. Selection boundary
+ // is defined as the position where we have alpha-num character on one side
+ // and non-alpha-num char on the other side.
+ for (; cursor_position < text().length(); cursor_position++) {
+ if (IsPositionAtWordSelectionBoundary(cursor_position))
+ break;
+ }
+
+ SetSelection(ui::Range(selection_start, cursor_position));
+}
+
+const ui::Range& RenderText::GetComposition() const {
+ return composition_range_;
+}
+
+void RenderText::SetComposition(const ui::Range& composition_range)
+{
+ composition_range_.set_end(composition_range.end());
+ composition_range_.set_start(composition_range.start());
+}
+
+const StyleRanges& RenderText::GetStyleRanges() const {
+ return style_ranges_;
+}
+
+// TODO(msw): Enforce style ranges to exactly cover the text?
+// Allow 'default' style? Mismatching length from text?
+void RenderText::ApplyStyleRange(StyleRange* style_range) {
+ const ui::Range& new_range = style_range->range;
+ CHECK(new_range.IsValid());
+ CHECK(!new_range.is_empty());
+ CHECK(!new_range.is_reversed());
+
+ // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges.
+ StyleRanges::const_iterator i;
+ for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
+ StyleRange* style = *i;
+ if (style->range.start() >= new_range.end())
+ break;
+ if (new_range.Contains(style->range)) {
+ style_ranges_.erase(i);
+ delete style;
+ } else if (style->range.start() < new_range.start() &&
+ style->range.end() > new_range.end()) {
+ // Split the current style into two styles.
+ StyleRange* split_style = new StyleRange(*style);
+ split_style->range.set_end(new_range.start());
+ style_ranges_.insert(i, split_style);
+ style->range.set_start(new_range.end());
+ break;
+ } else if (style->range.start() < new_range.start()) {
+ style->range.set_end(std::min(style->range.end(), new_range.start()));
+ } else {
+ style->range.set_start(new_range.end());
+ break;
+ }
+ }
+ // Add the new range in its sorted location.
+ style_ranges_.insert(i, style_range);
+#ifndef NDEBUG
+ CheckStyleRanges(style_ranges_, text_.length());
+#endif
+}
+
+RenderText::RenderText()
+ : text_(),
+ selection_range_(),
+ is_cursor_visible_(false),
+ composition_range_(),
+ style_ranges_(),
+ display_rect_(),
+ render_offset_(),
+ default_font_(),
+ default_color_(),
+ // TODO(msw): Needed?
+ //cursor_bounds_(),
+ //selection_bounds_(),
+ flags_() {
+ // TODO(msw): default font...
+ //style_ranges_.push_back(new StyleRange(font, ui::Range()));
+}
+
+RenderText::RenderText(const string16& text,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect,
+ int flags)
+ : text_(text),
+ selection_range_(),
+ is_cursor_visible_(false),
+ composition_range_(),
+ style_ranges_(),
+ display_rect_(display_rect),
+ render_offset_(),
+ default_font_(font),
+ default_color_(color),
+ // TODO(msw): Needed?
+ //cursor_bounds_(),
+ //selection_bounds_(),
+ flags_(flags) {
+ // TODO(msw): StyleRange ctor?
+ //style_ranges_.push_back(new StyleRange(font, ui::Range(0, text_.length())));
+}
+
+RenderText::~RenderText() {
+ STLDeleteContainerPointers(style_ranges_.begin(), style_ranges_.end());
+}
+
+bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) {
+ // TODO(msw): Doesn't this crash with pos == 0?
xji 2011/07/01 18:29:44 even if it does not crash, it definitely access ou
msw 2011/07/01 21:52:15 Done.
+ return (isalnum(text()[pos - 1]) && !isalnum(text()[pos])) ||
+ (!isalnum(text()[pos - 1]) && isalnum(text()[pos]));
+}
+
+} // namespace gfx

Powered by Google App Engine
This is Rietveld 408576698