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

Side by Side Diff: ui/gfx/render_text_win.cc

Issue 9390022: Simplify handling of BiDi cursor movement (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: make set_selection_start private, fix accidental inclusion of local hacks Created 8 years, 10 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/render_text_win.h" 5 #include "ui/gfx/render_text_win.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/i18n/break_iterator.h" 9 #include "base/i18n/break_iterator.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 glyph_count(0), 98 glyph_count(0),
99 script_cache(NULL) { 99 script_cache(NULL) {
100 memset(&script_analysis, 0, sizeof(script_analysis)); 100 memset(&script_analysis, 0, sizeof(script_analysis));
101 memset(&abc_widths, 0, sizeof(abc_widths)); 101 memset(&abc_widths, 0, sizeof(abc_widths));
102 } 102 }
103 103
104 TextRun::~TextRun() { 104 TextRun::~TextRun() {
105 ScriptFreeCache(&script_cache); 105 ScriptFreeCache(&script_cache);
106 } 106 }
107 107
108 // Returns the X coordinate of the leading or |trailing| edge of the glyph
109 // starting at |index|, relative to the left of the text (not the view).
110 int GetGlyphXBoundary(internal::TextRun* run, size_t index, bool trailing) {
111 DCHECK_GE(index, run->range.start())
112 DCHECK_LT(index, run->range.end())
113 int x = 0;
114 HRESULT hr = ScriptCPtoX(
115 index - run->range.start(),
116 trailing,
117 run->range.length(),
118 run->glyph_count,
119 run->logical_clusters.get(),
120 run->visible_attributes.get(),
121 run->advance_widths.get(),
122 &run->script_analysis,
123 &x);
124 DCHECK(SUCCEEDED(hr));
125 return run->preceding_run_widths + x;
126 }
127
108 } // namespace internal 128 } // namespace internal
109 129
110 RenderTextWin::RenderTextWin() 130 RenderTextWin::RenderTextWin()
111 : RenderText(), 131 : RenderText(),
112 string_width_(0), 132 string_width_(0),
113 needs_layout_(false) { 133 needs_layout_(false) {
114 memset(&script_control_, 0, sizeof(script_control_)); 134 memset(&script_control_, 0, sizeof(script_control_));
115 memset(&script_state_, 0, sizeof(script_state_)); 135 memset(&script_state_, 0, sizeof(script_state_));
116 script_control_.fMergeNeutralItems = true; 136 script_control_.fMergeNeutralItems = true;
117 137
118 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); 138 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT));
119 } 139 }
120 140
121 RenderTextWin::~RenderTextWin() { 141 RenderTextWin::~RenderTextWin() {
122 STLDeleteContainerPointers(runs_.begin(), runs_.end()); 142 STLDeleteContainerPointers(runs_.begin(), runs_.end());
123 } 143 }
124 144
125 base::i18n::TextDirection RenderTextWin::GetTextDirection() { 145 base::i18n::TextDirection RenderTextWin::GetTextDirection() {
126 // TODO(benrg): Code moved from RenderText::GetTextDirection. Needs to be 146 // TODO(benrg): Code moved from RenderText::GetTextDirection. Needs to be
127 // replaced by a correct Windows implementation. 147 // replaced by a correct Windows implementation.
128 if (base::i18n::IsRTL()) 148 if (base::i18n::IsRTL())
129 return base::i18n::RIGHT_TO_LEFT; 149 return base::i18n::RIGHT_TO_LEFT;
130 return base::i18n::LEFT_TO_RIGHT; 150 return base::i18n::LEFT_TO_RIGHT;
131 } 151 }
132 152
133 int RenderTextWin::GetStringWidth() { 153 Size RenderTextWin::GetStringSize() {
134 EnsureLayout(); 154 EnsureLayout();
135 return string_width_; 155 // TODO(msw): Use the largest font instead of the default font?
156 return Size(string_width_, GetFont().GetHeight());
136 } 157 }
137 158
138 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { 159 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
139 if (text().empty()) 160 if (text().empty())
140 return SelectionModel(); 161 return SelectionModel();
141 162
142 EnsureLayout(); 163 EnsureLayout();
143 // Find the run that contains the point and adjust the argument location. 164 // Find the run that contains the point and adjust the argument location.
144 Point p(ToTextPoint(point)); 165 Point p(ToTextPoint(point));
145 size_t run_index = GetRunContainingPoint(p); 166 size_t run_index = GetRunContainingPoint(p);
146 if (run_index == runs_.size()) 167 if (run_index == runs_.size())
147 return EdgeSelectionModel((p.x() < 0) ? CURSOR_LEFT : CURSOR_RIGHT); 168 return EdgeSelectionModel((p.x() < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
148 internal::TextRun* run = runs_[run_index]; 169 internal::TextRun* run = runs_[run_index];
149 170
150 int position = 0, trailing = 0; 171 int position = 0, trailing = 0;
151 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths, 172 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths,
152 run->range.length(), 173 run->range.length(),
153 run->glyph_count, 174 run->glyph_count,
154 run->logical_clusters.get(), 175 run->logical_clusters.get(),
155 run->visible_attributes.get(), 176 run->visible_attributes.get(),
156 run->advance_widths.get(), 177 run->advance_widths.get(),
157 &(run->script_analysis), 178 &(run->script_analysis),
158 &position, 179 &position,
159 &trailing); 180 &trailing);
160 DCHECK(SUCCEEDED(hr)); 181 DCHECK(SUCCEEDED(hr));
182 DCHECK_GE(trailing, 0);
161 position += run->range.start(); 183 position += run->range.start();
162
163 size_t cursor = position + trailing; 184 size_t cursor = position + trailing;
164 DCHECK_GE(cursor, 0U);
165 DCHECK_LE(cursor, text().length()); 185 DCHECK_LE(cursor, text().length());
166 return SelectionModel(cursor, position, 186 return SelectionModel(cursor, trailing ? CURSOR_BACKWARD : CURSOR_FORWARD);
167 (trailing > 0) ? SelectionModel::TRAILING : SelectionModel::LEADING);
168 }
169
170 Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection,
171 bool insert_mode) {
172 EnsureLayout();
173
174 // Highlight the logical cursor (selection end) when not in insert mode.
175 size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end();
176 size_t run_index = GetRunContainingPosition(pos);
177 internal::TextRun* run = run_index == runs_.size() ? NULL : runs_[run_index];
178
179 int start_x = 0, end_x = 0;
180 if (run) {
181 HRESULT hr = 0;
182 hr = ScriptCPtoX(pos - run->range.start(),
183 false,
184 run->range.length(),
185 run->glyph_count,
186 run->logical_clusters.get(),
187 run->visible_attributes.get(),
188 run->advance_widths.get(),
189 &(run->script_analysis),
190 &start_x);
191 DCHECK(SUCCEEDED(hr));
192 hr = ScriptCPtoX(pos - run->range.start(),
193 true,
194 run->range.length(),
195 run->glyph_count,
196 run->logical_clusters.get(),
197 run->visible_attributes.get(),
198 run->advance_widths.get(),
199 &(run->script_analysis),
200 &end_x);
201 DCHECK(SUCCEEDED(hr));
202 }
203 // TODO(msw): Use the last visual run's font instead of the default font?
204 int height = run ? run->font.GetHeight() : GetFont().GetHeight();
205 Rect rect(std::min(start_x, end_x), 0, std::abs(end_x - start_x), height);
206 // Offset to the run start or the right/left end for an out of bounds index.
207 // Also center the rect vertically in the display area.
208 int text_end_offset = base::i18n::IsRTL() ? 0 : GetStringWidth();
209 rect.Offset((run ? run->preceding_run_widths : text_end_offset),
210 (display_rect().height() - rect.height()) / 2);
211 // Adjust for leading/trailing in insert mode.
212 if (insert_mode && run) {
213 bool leading = selection.caret_placement() == SelectionModel::LEADING;
214 // Adjust the x value for right-side placement.
215 if (run->script_analysis.fRTL == leading)
216 rect.set_x(rect.right());
217 rect.set_width(0);
218 }
219 rect.set_origin(ToViewPoint(rect.origin()));
220 return rect;
221 } 187 }
222 188
223 SelectionModel RenderTextWin::AdjacentCharSelectionModel( 189 SelectionModel RenderTextWin::AdjacentCharSelectionModel(
224 const SelectionModel& selection, VisualCursorDirection direction) { 190 const SelectionModel& selection,
191 VisualCursorDirection direction) {
225 DCHECK(!needs_layout_); 192 DCHECK(!needs_layout_);
226 size_t caret = selection.caret_pos(); 193 internal::TextRun* run;
227 SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); 194 size_t run_index = GetRunContainingCaret(selection);
228 size_t run_index = GetRunContainingPosition(caret); 195 if (run_index == runs_.size()) {
229 DCHECK(run_index < runs_.size()); 196 // The cursor is not in any run: we're at the visual and logical edge.
230 internal::TextRun* run = runs_[run_index]; 197 SelectionModel edge = EdgeSelectionModel(direction);
231 198 if (edge.caret_pos() == selection.caret_pos())
199 return edge;
200 else
201 run = direction == CURSOR_RIGHT ? runs_.front() : runs_.back();
202 } else {
203 // If the cursor is moving within the current run, just move it by one
204 // grapheme in the appropriate direction.
205 run = runs_[run_index];
206 size_t caret = selection.caret_pos();
207 bool forward_motion =
208 run->script_analysis.fRTL == (direction == CURSOR_LEFT);
209 if (forward_motion) {
210 if (caret < run->range.end()) {
211 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
212 return SelectionModel(caret, CURSOR_BACKWARD);
213 }
214 } else {
215 if (caret > run->range.start()) {
216 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
217 return SelectionModel(caret, CURSOR_FORWARD);
218 }
219 }
220 // The cursor is at the edge of a run; move to the visually adjacent run.
221 size_t visual_index = logical_to_visual_[run_index];
222 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
msw 2012/02/28 22:51:32 nit: Just make visual_index an int and check for <
benrg 2012/03/07 01:04:44 Yeah, okay.
223 visual_index =
msw 2012/02/28 22:51:32 nit: Make this: visual_index += (direction == CURS
benrg 2012/03/07 01:04:44 Done.
224 (direction == CURSOR_LEFT) ? visual_index - 1 : visual_index + 1;
225 if (visual_index >= runs_.size())
226 return EdgeSelectionModel(direction);
227 run = runs_[visual_to_logical_[visual_index]];
228 }
232 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); 229 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT);
233 if (forward_motion) { 230 return forward_motion ? FirstSelectionModelInsideRun(run) :
234 if (caret_placement == SelectionModel::LEADING) { 231 LastSelectionModelInsideRun(run);
235 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
236 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
237 } else if (selection.selection_end() < run->range.end()) {
238 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
239 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
240 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
241 }
242 } else {
243 if (caret_placement == SelectionModel::TRAILING)
244 return SelectionModel(caret, caret, SelectionModel::LEADING);
245 else if (caret > run->range.start()) {
246 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
247 return SelectionModel(caret, caret, SelectionModel::LEADING);
248 }
249 }
250
251 // The character is at the beginning/end of its run; go to the previous/next
252 // visual run.
253 size_t visual_index = logical_to_visual_[run_index];
254 if (visual_index == (direction == CURSOR_LEFT ? 0 : runs_.size() - 1))
255 return EdgeSelectionModel(direction);
256 internal::TextRun* adjacent = runs_[visual_to_logical_[
257 direction == CURSOR_LEFT ? visual_index - 1 : visual_index + 1]];
258 forward_motion = adjacent->script_analysis.fRTL == (direction == CURSOR_LEFT);
259 return forward_motion ? FirstSelectionModelInsideRun(adjacent) :
260 LastSelectionModelInsideRun(adjacent);
261 } 232 }
262 233
263 // TODO(msw): Implement word breaking for Windows. 234 // TODO(msw): Implement word breaking for Windows.
264 SelectionModel RenderTextWin::AdjacentWordSelectionModel( 235 SelectionModel RenderTextWin::AdjacentWordSelectionModel(
265 const SelectionModel& selection, 236 const SelectionModel& selection,
266 VisualCursorDirection direction) { 237 VisualCursorDirection direction) {
267 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 238 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
268 bool success = iter.Init(); 239 bool success = iter.Init();
269 DCHECK(success); 240 DCHECK(success);
270 if (!success) 241 if (!success)
271 return selection; 242 return selection;
272 243
273 size_t pos; 244 size_t pos;
274 if (direction == CURSOR_RIGHT) { 245 if (direction == CURSOR_RIGHT) {
275 pos = std::min(selection.selection_end() + 1, text().length()); 246 pos = std::min(selection.caret_pos() + 1, text().length());
276 while (iter.Advance()) { 247 while (iter.Advance()) {
277 pos = iter.pos(); 248 pos = iter.pos();
278 if (iter.IsWord() && pos > selection.selection_end()) 249 if (iter.IsWord() && pos > selection.caret_pos())
279 break; 250 break;
280 } 251 }
281 } else { // direction == CURSOR_LEFT 252 } else { // direction == CURSOR_LEFT
282 // Notes: We always iterate words from the beginning. 253 // Notes: We always iterate words from the beginning.
283 // This is probably fast enough for our usage, but we may 254 // This is probably fast enough for our usage, but we may
284 // want to modify WordIterator so that it can start from the 255 // want to modify WordIterator so that it can start from the
285 // middle of string and advance backwards. 256 // middle of string and advance backwards.
286 pos = std::max<int>(selection.selection_end() - 1, 0); 257 pos = std::max<int>(selection.caret_pos() - 1, 0);
287 while (iter.Advance()) { 258 while (iter.Advance()) {
288 if (iter.IsWord()) { 259 if (iter.IsWord()) {
289 size_t begin = iter.pos() - iter.GetString().length(); 260 size_t begin = iter.pos() - iter.GetString().length();
290 if (begin == selection.selection_end()) { 261 if (begin == selection.caret_pos()) {
291 // The cursor is at the beginning of a word. 262 // The cursor is at the beginning of a word.
292 // Move to previous word. 263 // Move to previous word.
293 break; 264 break;
294 } else if (iter.pos() >= selection.selection_end()) { 265 } else if (iter.pos() >= selection.caret_pos()) {
295 // The cursor is in the middle or at the end of a word. 266 // The cursor is in the middle or at the end of a word.
296 // Move to the top of current word. 267 // Move to the top of current word.
297 pos = begin; 268 pos = begin;
298 break; 269 break;
299 } else { 270 } else {
300 pos = iter.pos() - iter.GetString().length(); 271 pos = iter.pos() - iter.GetString().length();
301 } 272 }
302 } 273 }
303 } 274 }
304 } 275 }
305 return SelectionModel(pos, pos, SelectionModel::LEADING); 276 return SelectionModel(pos, CURSOR_FORWARD);
306 } 277 }
307 278
308 SelectionModel RenderTextWin::EdgeSelectionModel( 279 void RenderTextWin::GetGlyphBounds(size_t index,
309 VisualCursorDirection direction) { 280 ui::Range* xspan,
310 if (text().empty()) 281 int* height) {
311 return SelectionModel(0, 0, SelectionModel::LEADING); 282 size_t run_index =
312 283 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
313 EnsureLayout(); 284 DCHECK_LT(run_index, runs_.size());
314 size_t cursor = (direction == GetVisualDirectionOfLogicalEnd()) ? 285 internal::TextRun* run = runs_[run_index];
315 text().length() : 0; 286 xspan->set_start(GetGlyphXBoundary(run, index, false));
316 internal::TextRun* run = runs_[ 287 xspan->set_end(GetGlyphXBoundary(run, index, true));
317 visual_to_logical_[direction == CURSOR_RIGHT ? runs_.size() - 1 : 0]]; 288 *height = run->font.GetHeight();
318 size_t caret;
319 SelectionModel::CaretPlacement placement;
320 if (run->script_analysis.fRTL == (direction == CURSOR_RIGHT)) {
321 caret = run->range.start();
322 placement = SelectionModel::LEADING;
323 } else {
324 caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
325 placement = SelectionModel::TRAILING;
326 }
327 return SelectionModel(cursor, caret, placement);
328 } 289 }
329 290
330 std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) { 291 std::vector<Rect> RenderTextWin::GetSubstringBounds(ui::Range range) {
331 DCHECK(!needs_layout_); 292 DCHECK(!needs_layout_);
332 ui::Range range(from, to);
333 DCHECK(ui::Range(0, text().length()).Contains(range)); 293 DCHECK(ui::Range(0, text().length()).Contains(range));
334 Point display_offset(GetUpdatedDisplayOffset()); 294 Point display_offset(GetUpdatedDisplayOffset());
335 HRESULT hr = 0; 295 HRESULT hr = 0;
336 296
337 std::vector<Rect> bounds; 297 std::vector<Rect> bounds;
338 if (from == to) 298 if (range.is_empty())
339 return bounds; 299 return bounds;
340 300
341 // Add a Rect for each run/selection intersection. 301 // Add a Rect for each run/selection intersection.
342 // TODO(msw): The bounds should probably not always be leading the range ends. 302 // TODO(msw): The bounds should probably not always be leading the range ends.
343 for (size_t i = 0; i < runs_.size(); ++i) { 303 for (size_t i = 0; i < runs_.size(); ++i) {
344 internal::TextRun* run = runs_[visual_to_logical_[i]]; 304 internal::TextRun* run = runs_[visual_to_logical_[i]];
345 ui::Range intersection = run->range.Intersect(range); 305 ui::Range intersection = run->range.Intersect(range);
346 if (intersection.IsValid()) { 306 if (intersection.IsValid()) {
347 DCHECK(!intersection.is_reversed()); 307 DCHECK(!intersection.is_reversed());
348 int start_offset = 0; 308 ui::Range range(GetGlyphXBoundary(run, intersection.start(), false),
349 hr = ScriptCPtoX(intersection.start() - run->range.start(), 309 GetGlyphXBoundary(run, intersection.end(), false));
350 false, 310 Rect rect(range.GetMin(), 0, range.length(), run->font.GetHeight());
351 run->range.length(),
352 run->glyph_count,
353 run->logical_clusters.get(),
354 run->visible_attributes.get(),
355 run->advance_widths.get(),
356 &(run->script_analysis),
357 &start_offset);
358 DCHECK(SUCCEEDED(hr));
359 int end_offset = 0;
360 hr = ScriptCPtoX(intersection.end() - run->range.start(),
361 false,
362 run->range.length(),
363 run->glyph_count,
364 run->logical_clusters.get(),
365 run->visible_attributes.get(),
366 run->advance_widths.get(),
367 &(run->script_analysis),
368 &end_offset);
369 DCHECK(SUCCEEDED(hr));
370 if (start_offset > end_offset)
371 std::swap(start_offset, end_offset);
372 Rect rect(run->preceding_run_widths + start_offset, 0,
373 end_offset - start_offset, run->font.GetHeight());
374 // Center the rect vertically in the display area. 311 // Center the rect vertically in the display area.
375 rect.Offset(0, (display_rect().height() - rect.height()) / 2); 312 rect.Offset(0, (display_rect().height() - rect.height()) / 2);
376 rect.set_origin(ToViewPoint(rect.origin())); 313 rect.set_origin(ToViewPoint(rect.origin()));
377 // Union this with the last rect if they're adjacent. 314 // Union this with the last rect if they're adjacent.
378 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { 315 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
379 rect = rect.Union(bounds.back()); 316 rect = rect.Union(bounds.back());
380 bounds.pop_back(); 317 bounds.pop_back();
381 } 318 }
382 bounds.push_back(rect); 319 bounds.push_back(rect);
383 } 320 }
384 } 321 }
385 return bounds; 322 return bounds;
386 } 323 }
387 324
388 void RenderTextWin::SetSelectionModel(const SelectionModel& model) { 325 void RenderTextWin::SetSelectionModel(const SelectionModel& model) {
389 RenderText::SetSelectionModel(model); 326 RenderText::SetSelectionModel(model);
390 // TODO(xji): The styles are applied to text inside ItemizeLogicalText(). So, 327 // TODO(xji): The styles are applied to text inside ItemizeLogicalText(). So,
391 // we need to update layout here in order for the styles, such as selection 328 // we need to update layout here in order for the styles, such as selection
392 // foreground, to be picked up. Eventually, we should separate styles from 329 // foreground, to be picked up. Eventually, we should separate styles from
393 // layout by applying foreground, strike, and underline styles during 330 // layout by applying foreground, strike, and underline styles during
394 // DrawVisualText as what RenderTextLinux does. 331 // DrawVisualText as what RenderTextLinux does.
395 UpdateLayout(); 332 UpdateLayout();
396 } 333 }
397 334
398 bool RenderTextWin::IsCursorablePosition(size_t position) { 335 bool RenderTextWin::IsCursorablePosition(size_t position) {
399 if (position == 0 || position == text().length()) 336 if (position == 0 || position == text().length())
400 return true; 337 return true;
401 338
402 EnsureLayout(); 339 EnsureLayout();
403 size_t run_index = GetRunContainingPosition(position); 340 size_t run_index =
341 GetRunContainingCaret(SelectionModel(position, CURSOR_FORWARD));
404 if (run_index >= runs_.size()) 342 if (run_index >= runs_.size())
405 return false; 343 return false;
406 344
407 internal::TextRun* run = runs_[run_index]; 345 internal::TextRun* run = runs_[run_index];
408 size_t start = run->range.start(); 346 size_t start = run->range.start();
409 if (position == start) 347 if (position == start)
410 return true; 348 return true;
411 return run->logical_clusters[position - start] != 349 return run->logical_clusters[position - start] !=
412 run->logical_clusters[position - start - 1]; 350 run->logical_clusters[position - start - 1];
413 } 351 }
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
486 return text().length(); 424 return text().length();
487 } else { 425 } else {
488 // The requested |index| is at the end of the text. Use the index of the 426 // The requested |index| is at the end of the text. Use the index of the
489 // last character to find the grapheme. 427 // last character to find the grapheme.
490 index = text().length() - 1; 428 index = text().length() - 1;
491 if (IsCursorablePosition(index)) 429 if (IsCursorablePosition(index))
492 return index; 430 return index;
493 } 431 }
494 } 432 }
495 433
496 size_t run_index = GetRunContainingPosition(index); 434 size_t run_index =
435 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
497 DCHECK(run_index < runs_.size()); 436 DCHECK(run_index < runs_.size());
498 internal::TextRun* run = runs_[run_index]; 437 internal::TextRun* run = runs_[run_index];
499 size_t start = run->range.start(); 438 size_t start = run->range.start();
500 size_t ch = index - start; 439 size_t ch = index - start;
501 440
502 if (direction == CURSOR_BACKWARD) { 441 if (direction == CURSOR_BACKWARD) {
503 // If |ch| is the start of the run, use the preceding run, if any. 442 // If |ch| is the start of the run, use the preceding run, if any.
504 if (ch == 0) { 443 if (ch == 0) {
505 if (run_index == 0) 444 if (run_index == 0)
506 return 0; 445 return 0;
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
685 for (size_t i = 0; i < runs_.size(); ++i) { 624 for (size_t i = 0; i < runs_.size(); ++i) {
686 internal::TextRun* run = runs_[visual_to_logical_[i]]; 625 internal::TextRun* run = runs_[visual_to_logical_[i]];
687 run->preceding_run_widths = preceding_run_widths; 626 run->preceding_run_widths = preceding_run_widths;
688 const ABC& abc = run->abc_widths; 627 const ABC& abc = run->abc_widths;
689 run->width = abc.abcA + abc.abcB + abc.abcC; 628 run->width = abc.abcA + abc.abcB + abc.abcC;
690 preceding_run_widths += run->width; 629 preceding_run_widths += run->width;
691 } 630 }
692 string_width_ = preceding_run_widths; 631 string_width_ = preceding_run_widths;
693 } 632 }
694 633
695 size_t RenderTextWin::GetRunContainingPosition(size_t position) const { 634 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const {
696 DCHECK(!needs_layout_); 635 DCHECK(!needs_layout_);
697 // Find the text run containing the argument position. 636 size_t position = caret.caret_pos();
637 LogicalCursorDirection affinity = caret.caret_affinity();
698 size_t run = 0; 638 size_t run = 0;
699 for (; run < runs_.size(); ++run) 639 for (; run < runs_.size(); ++run)
700 if (runs_[run]->range.start() <= position && 640 if (RangeContainsCaret(runs_[run]->range, position, affinity))
701 runs_[run]->range.end() > position)
702 break; 641 break;
703 return run; 642 return run;
704 } 643 }
705 644
706 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { 645 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
707 DCHECK(!needs_layout_); 646 DCHECK(!needs_layout_);
708 // Find the text run containing the argument point (assumed already offset). 647 // Find the text run containing the argument point (assumed already offset).
709 size_t run = 0; 648 size_t run = 0;
710 for (; run < runs_.size(); ++run) 649 for (; run < runs_.size(); ++run)
711 if (runs_[run]->preceding_run_widths <= point.x() && 650 if (runs_[run]->preceding_run_widths <= point.x() &&
712 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) 651 runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
713 break; 652 break;
714 return run; 653 return run;
715 } 654 }
716 655
717 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( 656 SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
718 internal::TextRun* run) { 657 internal::TextRun* run) {
719 size_t caret = run->range.start(); 658 size_t cursor = IndexOfAdjacentGrapheme(run->range.start(), CURSOR_FORWARD);
720 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); 659 return SelectionModel(cursor, CURSOR_BACKWARD);
721 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
722 } 660 }
723 661
724 SelectionModel RenderTextWin::LastSelectionModelInsideRun( 662 SelectionModel RenderTextWin::LastSelectionModelInsideRun(
725 internal::TextRun* run) { 663 internal::TextRun* run) {
726 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD); 664 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
727 return SelectionModel(caret, caret, SelectionModel::LEADING); 665 return SelectionModel(caret, CURSOR_FORWARD);
728 } 666 }
729 667
730 RenderText* RenderText::CreateRenderText() { 668 RenderText* RenderText::CreateRenderText() {
731 return new RenderTextWin; 669 return new RenderTextWin;
732 } 670 }
733 671
734 } // namespace gfx 672 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698