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

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

Powered by Google App Engine
This is Rietveld 408576698