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

Side by Side 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, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/gfx/render_text.h"
6
7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "base/stl_util-inl.h"
11
12 namespace {
13
14 #ifndef NDEBUG
15 // Check StyleRanges invariant conditions: sorted and non-overlapping ranges.
16 void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) {
17 if (length == 0) {
18 DCHECK(style_ranges.empty()) << "Style ranges exist for empty text.";
19 return;
20 }
21 for (gfx::StyleRanges::size_type i = 0; i < style_ranges.size() - 1; i++) {
22 ui::Range& former = style_ranges[i]->range;
23 ui::Range& latter = style_ranges[i + 1]->range;
24 DCHECK(!former.is_empty()) << "Empty range at " << i << ":" << former;
25 DCHECK(former.IsValid()) << "Invalid range at " << i << ":" << former;
26 DCHECK(!former.is_reversed()) << "Reversed range at " << i << ":" << former;
27 DCHECK(former.end() == latter.start()) << "Ranges gap/overlap/unsorted." <<
28 "former:" << former << ", latter:" << latter;
29 }
30 const gfx::StyleRange* end_style = *style_ranges.rbegin();
31 DCHECK(!end_style->range.is_empty()) << "Empty range at end.";
32 DCHECK(end_style->range.IsValid()) << "Invalid range at end.";
33 DCHECK(!end_style->range.is_reversed()) << "Reversed range at end.";
34 DCHECK(end_style->range.end() == length) << "Style and text length mismatch.";
35 }
36 #endif
37
38 } // namespace
39
40 namespace gfx {
41
42 void RenderText::SetText(const string16& text) {
43 // TODO(msw): Allow text and styles to get out of sync? Repair on draw?
44 // Update the style ranges as needed.
45 if (text.empty()) {
46 style_ranges_.clear();
47 } else if (style_ranges_.empty()) {
48 StyleRange* style = new StyleRange();
49 style->font = default_font_;
50 style->foreground = default_color_;
51 style->range.set_end(text.length());
52 style_ranges_.push_back(style);
53 } else if (text.length() > text_.length()) {
54 style_ranges_.back()->range.set_end(text.length());
55 } else if (text.length() < text_.length()) {
56 StyleRanges::const_iterator i;
57 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
58 StyleRange* style = *i;
59 if (style->range.start() > text.length()) {
60 style_ranges_.erase(i);
61 delete style;
62 } else if (style->range.end() > text.length()) {
63 style->range.set_end(text.length());
64 }
65 }
66 }
67 #ifndef NDEBUG
68 CheckStyleRanges(style_ranges_, text.length());
69 #endif
70
71 text_ = text;
72 // TODO(msw): Mark dirty text flag.
73 }
74
75 size_t RenderText::GetCursor() const {
76 return selection_range_.end();
77 }
78
79 void RenderText::SetCursor(const size_t position) {
80 selection_range_.set_end(position);
81 selection_range_.set_start(position);
82 }
83
84 void RenderText::MoveCursorLeft(bool select, bool move_by_word) {
85 size_t position = selection_range_.end();
86 // Cancelling a selection moves to the edge of the selection.
87 if (!selection_range_.is_empty() && !select) {
88 // Use the selection start if it is left of the selection end.
89 if (GetCursorBounds(selection_range_.start(), false).x() <
90 GetCursorBounds(position, false).x())
91 position = selection_range_.start();
92 // if |move_by_word|, use the nearest word boundary left of the selection.
93 if (move_by_word)
94 position = GetLeftCursorPosition(position, true);
95 } else {
96 position = GetLeftCursorPosition(position, move_by_word);
97 }
98 MoveCursorTo(position, select);
99 }
100
101 void RenderText::MoveCursorRight(bool select, bool move_by_word) {
102 size_t position = selection_range_.end();
103 // Cancelling a selection moves to the edge of the selection.
104 if (!selection_range_.is_empty() && !select) {
105 // Use the selection start if it is right of the selection end.
106 if (GetCursorBounds(selection_range_.start(), false).x() >
107 GetCursorBounds(position, false).x())
108 position = selection_range_.start();
109 // if |move_by_word|, use the nearest word boundary right of the selection.
110 if (move_by_word)
111 position = GetRightCursorPosition(position, true);
112 } else {
113 position = GetRightCursorPosition(position, move_by_word);
114 }
115 MoveCursorTo(position, select);
116 }
117
118 void RenderText::MoveCursorToLeftEnd(bool select) {
119 // TODO(msw) Bidi.
120 MoveCursorTo(0, select);
121 }
122
123 void RenderText::MoveCursorToRightEnd(bool select) {
124 // TODO(msw) Bidi.
125 MoveCursorTo(text().length(), select);
126 }
127
128 void RenderText::MoveCursorTo(size_t position, bool select) {
129 selection_range_.set_end(position);
130 if (!select)
131 selection_range_.set_start(position);
132 }
133
134 const ui::Range& RenderText::GetSelection() const {
135 return selection_range_;
136 }
137
138 void RenderText::SetSelection(const ui::Range& selection_range) {
139 selection_range_.set_end(selection_range.end());
140 selection_range_.set_start(selection_range.start());
141 }
142
143 void RenderText::ClearSelection() {
144 selection_range_.set_start(GetCursor());
145 }
146
147 void RenderText::SelectAll() {
148 SetSelection(ui::Range(0, text().length()));
149 }
150
151 void RenderText::SelectWord() {
152 // TODO(msw): Bidi impl?
153 size_t selection_start = GetSelection().start();
154 size_t cursor_position = GetCursor();
155 // First we setup selection_start_ and cursor_pos_. There are so many cases
156 // because we try to emulate what select-word looks like in a gtk textfield.
157 // See associated testcase for different cases.
158 if (cursor_position > 0 && cursor_position < text().length()) {
159 if (isalnum(text()[cursor_position])) {
160 selection_start = cursor_position;
161 cursor_position++;
162 } else
163 selection_start = cursor_position - 1;
164 } else if (cursor_position == 0) {
165 selection_start = cursor_position;
166 if (text().length() > 0)
167 cursor_position++;
168 } else {
169 selection_start = cursor_position - 1;
170 }
171
172 // Now we move selection_start_ to beginning of selection. Selection boundary
173 // is defined as the position where we have alpha-num character on one side
174 // and non-alpha-num char on the other side.
175 for (; selection_start > 0; selection_start--) {
176 if (IsPositionAtWordSelectionBoundary(selection_start))
177 break;
178 }
179
180 // Now we move cursor_pos_ to end of selection. Selection boundary
181 // is defined as the position where we have alpha-num character on one side
182 // and non-alpha-num char on the other side.
183 for (; cursor_position < text().length(); cursor_position++) {
184 if (IsPositionAtWordSelectionBoundary(cursor_position))
185 break;
186 }
187
188 SetSelection(ui::Range(selection_start, cursor_position));
189 }
190
191 const ui::Range& RenderText::GetComposition() const {
192 return composition_range_;
193 }
194
195 void RenderText::SetComposition(const ui::Range& composition_range)
196 {
197 composition_range_.set_end(composition_range.end());
198 composition_range_.set_start(composition_range.start());
199 }
200
201 const StyleRanges& RenderText::GetStyleRanges() const {
202 return style_ranges_;
203 }
204
205 // TODO(msw): Enforce style ranges to exactly cover the text?
206 // Allow 'default' style? Mismatching length from text?
207 void RenderText::ApplyStyleRange(StyleRange* style_range) {
208 const ui::Range& new_range = style_range->range;
209 CHECK(new_range.IsValid());
210 CHECK(!new_range.is_empty());
211 CHECK(!new_range.is_reversed());
212
213 // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges.
214 StyleRanges::const_iterator i;
215 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
216 StyleRange* style = *i;
217 if (style->range.start() >= new_range.end())
218 break;
219 if (new_range.Contains(style->range)) {
220 style_ranges_.erase(i);
221 delete style;
222 } else if (style->range.start() < new_range.start() &&
223 style->range.end() > new_range.end()) {
224 // Split the current style into two styles.
225 StyleRange* split_style = new StyleRange(*style);
226 split_style->range.set_end(new_range.start());
227 style_ranges_.insert(i, split_style);
228 style->range.set_start(new_range.end());
229 break;
230 } else if (style->range.start() < new_range.start()) {
231 style->range.set_end(std::min(style->range.end(), new_range.start()));
232 } else {
233 style->range.set_start(new_range.end());
234 break;
235 }
236 }
237 // Add the new range in its sorted location.
238 style_ranges_.insert(i, style_range);
239 #ifndef NDEBUG
240 CheckStyleRanges(style_ranges_, text_.length());
241 #endif
242 }
243
244 RenderText::RenderText()
245 : text_(),
246 selection_range_(),
247 is_cursor_visible_(false),
248 composition_range_(),
249 style_ranges_(),
250 display_rect_(),
251 render_offset_(),
252 default_font_(),
253 default_color_(),
254 // TODO(msw): Needed?
255 //cursor_bounds_(),
256 //selection_bounds_(),
257 flags_() {
258 // TODO(msw): default font...
259 //style_ranges_.push_back(new StyleRange(font, ui::Range()));
260 }
261
262 RenderText::RenderText(const string16& text,
263 const gfx::Font& font,
264 const SkColor& color,
265 const gfx::Rect& display_rect,
266 int flags)
267 : text_(text),
268 selection_range_(),
269 is_cursor_visible_(false),
270 composition_range_(),
271 style_ranges_(),
272 display_rect_(display_rect),
273 render_offset_(),
274 default_font_(font),
275 default_color_(color),
276 // TODO(msw): Needed?
277 //cursor_bounds_(),
278 //selection_bounds_(),
279 flags_(flags) {
280 // TODO(msw): StyleRange ctor?
281 //style_ranges_.push_back(new StyleRange(font, ui::Range(0, text_.length())));
282 }
283
284 RenderText::~RenderText() {
285 STLDeleteContainerPointers(style_ranges_.begin(), style_ranges_.end());
286 }
287
288 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) {
289 // 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.
290 return (isalnum(text()[pos - 1]) && !isalnum(text()[pos])) ||
291 (!isalnum(text()[pos - 1]) && isalnum(text()[pos]));
292 }
293
294 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698