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

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: . 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) {
109 int x;
110 HRESULT hr = ScriptCPtoX(
111 index - run->range.start(),
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 last visual run's 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(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(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 size_t caret = selection.caret_pos();
227 SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); 202 internal::TextRun* run;
228 size_t run_index = GetRunContainingPosition(caret); 203 size_t run_index = GetRunContainingCaret(selection);
229 DCHECK(run_index < runs_.size()); 204 if (run_index == runs_.size()) {
230 internal::TextRun* run = runs_[run_index]; 205 // We're at the visual and logical edge.
231 206 SelectionModel edge = EdgeSelectionModel(direction);
207 if (text().empty() ||
208 edge == SelectionModel(caret, selection.caret_affinity()))
209 return edge;
210 else if (direction == CURSOR_RIGHT)
211 run = runs_.front();
212 else
213 run = runs_.back();
214 } else {
215 // If the cursor is moving within the current run, just move it by one
216 // grapheme in the appropriate direction.
217 run = runs_[run_index];
218 bool forward_motion =
219 run->script_analysis.fRTL == (direction == CURSOR_LEFT);
220 if (forward_motion) {
221 if (selection.caret_pos() < run->range.end()) {
222 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
223 return SelectionModel(caret, CURSOR_BACKWARD);
224 }
225 } else {
226 if (selection.caret_pos() > run->range.start()) {
227 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
228 return SelectionModel(caret, CURSOR_FORWARD);
229 }
230 }
231 // The cursor is at the edge of a run; move to the visually adjacent run.
232 size_t visual_index = logical_to_visual_[run_index];
233 // NB: exploits unsigned wraparound.
234 visual_index =
235 (direction == CURSOR_LEFT) ? visual_index - 1 : visual_index + 1;
236 if (visual_index >= runs_.size())
237 return EdgeSelectionModel(direction);
238 run = runs_[visual_to_logical_[visual_index]];
239 }
232 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); 240 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT);
233 if (forward_motion) { 241 return forward_motion ? FirstSelectionModelInsideRun(run) :
234 if (caret_placement == SelectionModel::LEADING) { 242 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 } 243 }
262 244
263 // TODO(msw): Implement word breaking for Windows. 245 // TODO(msw): Implement word breaking for Windows.
264 SelectionModel RenderTextWin::AdjacentWordSelectionModel( 246 SelectionModel RenderTextWin::AdjacentWordSelectionModel(
265 const SelectionModel& selection, 247 const SelectionModel& selection,
266 VisualCursorDirection direction) { 248 VisualCursorDirection direction) {
267 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 249 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
268 bool success = iter.Init(); 250 bool success = iter.Init();
269 DCHECK(success); 251 DCHECK(success);
270 if (!success) 252 if (!success)
271 return selection; 253 return selection;
272 254
273 size_t pos; 255 size_t pos;
274 if (direction == CURSOR_RIGHT) { 256 if (direction == CURSOR_RIGHT) {
275 pos = std::min(selection.selection_end() + 1, text().length()); 257 pos = std::min(selection.caret_pos() + 1, text().length());
276 while (iter.Advance()) { 258 while (iter.Advance()) {
277 pos = iter.pos(); 259 pos = iter.pos();
278 if (iter.IsWord() && pos > selection.selection_end()) 260 if (iter.IsWord() && pos > selection.caret_pos())
279 break; 261 break;
280 } 262 }
281 } else { // direction == CURSOR_LEFT 263 } else { // direction == CURSOR_LEFT
282 // Notes: We always iterate words from the beginning. 264 // Notes: We always iterate words from the beginning.
283 // This is probably fast enough for our usage, but we may 265 // This is probably fast enough for our usage, but we may
284 // want to modify WordIterator so that it can start from the 266 // want to modify WordIterator so that it can start from the
285 // middle of string and advance backwards. 267 // middle of string and advance backwards.
286 pos = std::max<int>(selection.selection_end() - 1, 0); 268 pos = std::max<int>(selection.caret_pos() - 1, 0);
287 while (iter.Advance()) { 269 while (iter.Advance()) {
288 if (iter.IsWord()) { 270 if (iter.IsWord()) {
289 size_t begin = iter.pos() - iter.GetString().length(); 271 size_t begin = iter.pos() - iter.GetString().length();
290 if (begin == selection.selection_end()) { 272 if (begin == selection.caret_pos()) {
291 // The cursor is at the beginning of a word. 273 // The cursor is at the beginning of a word.
292 // Move to previous word. 274 // Move to previous word.
293 break; 275 break;
294 } else if (iter.pos() >= selection.selection_end()) { 276 } else if (iter.pos() >= selection.caret_pos()) {
295 // The cursor is in the middle or at the end of a word. 277 // The cursor is in the middle or at the end of a word.
296 // Move to the top of current word. 278 // Move to the top of current word.
297 pos = begin; 279 pos = begin;
298 break; 280 break;
299 } else { 281 } else {
300 pos = iter.pos() - iter.GetString().length(); 282 pos = iter.pos() - iter.GetString().length();
301 } 283 }
302 } 284 }
303 } 285 }
304 } 286 }
305 return SelectionModel(pos, pos, SelectionModel::LEADING); 287 return SelectionModel(pos, CURSOR_FORWARD);
306 } 288 }
307 289
308 SelectionModel RenderTextWin::EdgeSelectionModel( 290 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_); 291 DCHECK(!needs_layout_);
332 ui::Range range(from, to);
333 DCHECK(ui::Range(0, text().length()).Contains(range)); 292 DCHECK(ui::Range(0, text().length()).Contains(range));
334 Point display_offset(GetUpdatedDisplayOffset()); 293 Point display_offset(GetUpdatedDisplayOffset());
335 HRESULT hr = 0; 294 HRESULT hr = 0;
336 295
337 std::vector<Rect> bounds; 296 std::vector<Rect> bounds;
338 if (from == to) 297 if (range.is_empty())
339 return bounds; 298 return bounds;
340 299
341 // Add a Rect for each run/selection intersection. 300 // Add a Rect for each run/selection intersection.
342 // TODO(msw): The bounds should probably not always be leading the range ends. 301 // TODO(msw): The bounds should probably not always be leading the range ends.
343 for (size_t i = 0; i < runs_.size(); ++i) { 302 for (size_t i = 0; i < runs_.size(); ++i) {
344 internal::TextRun* run = runs_[visual_to_logical_[i]]; 303 internal::TextRun* run = runs_[visual_to_logical_[i]];
345 ui::Range intersection = run->range.Intersect(range); 304 ui::Range intersection = run->range.Intersect(range);
346 if (intersection.IsValid()) { 305 if (intersection.IsValid()) {
347 DCHECK(!intersection.is_reversed()); 306 DCHECK(!intersection.is_reversed());
348 int start_offset = 0; 307 ui::Range range(GetGlyphXBoundary(run, intersection.start(), false),
349 hr = ScriptCPtoX(intersection.start() - run->range.start(), 308 GetGlyphXBoundary(run, intersection.end(), false));
350 false, 309 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. 310 // Center the rect vertically in the display area.
375 rect.Offset(0, (display_rect().height() - rect.height()) / 2); 311 rect.Offset(0, (display_rect().height() - rect.height()) / 2);
376 rect.set_origin(ToViewPoint(rect.origin())); 312 rect.set_origin(ToViewPoint(rect.origin()));
377 // Union this with the last rect if they're adjacent. 313 // Union this with the last rect if they're adjacent.
378 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { 314 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) {
379 rect = rect.Union(bounds.back()); 315 rect = rect.Union(bounds.back());
380 bounds.pop_back(); 316 bounds.pop_back();
381 } 317 }
382 bounds.push_back(rect); 318 bounds.push_back(rect);
383 } 319 }
384 } 320 }
385 return bounds; 321 return bounds;
386 } 322 }
387 323
388 void RenderTextWin::SetSelectionModel(const SelectionModel& model) { 324 void RenderTextWin::SetSelectionModel(const SelectionModel& model) {
389 RenderText::SetSelectionModel(model); 325 RenderText::SetSelectionModel(model);
390 // TODO(xji): The styles are applied to text inside ItemizeLogicalText(). So, 326 // 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 327 // 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 328 // foreground, to be picked up. Eventually, we should separate styles from
393 // layout by applying foreground, strike, and underline styles during 329 // layout by applying foreground, strike, and underline styles during
394 // DrawVisualText as what RenderTextLinux does. 330 // DrawVisualText as what RenderTextLinux does.
395 UpdateLayout(); 331 UpdateLayout();
396 } 332 }
397 333
398 bool RenderTextWin::IsCursorablePosition(size_t position) { 334 bool RenderTextWin::IsCursorablePosition(size_t position) {
399 if (position == 0 || position == text().length()) 335 if (position == 0 || position == text().length())
400 return true; 336 return true;
401 337
402 EnsureLayout(); 338 EnsureLayout();
403 size_t run_index = GetRunContainingPosition(position); 339 size_t run_index =
340 GetRunContainingCaret(SelectionModel(position, CURSOR_FORWARD));
404 if (run_index >= runs_.size()) 341 if (run_index >= runs_.size())
405 return false; 342 return false;
406 343
407 internal::TextRun* run = runs_[run_index]; 344 internal::TextRun* run = runs_[run_index];
408 size_t start = run->range.start(); 345 size_t start = run->range.start();
409 if (position == start) 346 if (position == start)
410 return true; 347 return true;
411 return run->logical_clusters[position - start] != 348 return run->logical_clusters[position - start] !=
412 run->logical_clusters[position - start - 1]; 349 run->logical_clusters[position - start - 1];
413 } 350 }
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
486 return text().length(); 423 return text().length();
487 } else { 424 } else {
488 // The requested |index| is at the end of the text. Use the index of the 425 // The requested |index| is at the end of the text. Use the index of the
489 // last character to find the grapheme. 426 // last character to find the grapheme.
490 index = text().length() - 1; 427 index = text().length() - 1;
491 if (IsCursorablePosition(index)) 428 if (IsCursorablePosition(index))
492 return index; 429 return index;
493 } 430 }
494 } 431 }
495 432
496 size_t run_index = GetRunContainingPosition(index); 433 size_t run_index =
434 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
497 DCHECK(run_index < runs_.size()); 435 DCHECK(run_index < runs_.size());
498 internal::TextRun* run = runs_[run_index]; 436 internal::TextRun* run = runs_[run_index];
499 size_t start = run->range.start(); 437 size_t start = run->range.start();
500 size_t ch = index - start; 438 size_t ch = index - start;
501 439
502 if (direction == CURSOR_BACKWARD) { 440 if (direction == CURSOR_BACKWARD) {
503 // If |ch| is the start of the run, use the preceding run, if any. 441 // If |ch| is the start of the run, use the preceding run, if any.
504 if (ch == 0) { 442 if (ch == 0) {
505 if (run_index == 0) 443 if (run_index == 0)
506 return 0; 444 return 0;
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
685 for (size_t i = 0; i < runs_.size(); ++i) { 623 for (size_t i = 0; i < runs_.size(); ++i) {
686 internal::TextRun* run = runs_[visual_to_logical_[i]]; 624 internal::TextRun* run = runs_[visual_to_logical_[i]];
687 run->preceding_run_widths = preceding_run_widths; 625 run->preceding_run_widths = preceding_run_widths;
688 const ABC& abc = run->abc_widths; 626 const ABC& abc = run->abc_widths;
689 run->width = abc.abcA + abc.abcB + abc.abcC; 627 run->width = abc.abcA + abc.abcB + abc.abcC;
690 preceding_run_widths += run->width; 628 preceding_run_widths += run->width;
691 } 629 }
692 string_width_ = preceding_run_widths; 630 string_width_ = preceding_run_widths;
693 } 631 }
694 632
695 size_t RenderTextWin::GetRunContainingPosition(size_t position) const { 633 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const {
696 DCHECK(!needs_layout_); 634 DCHECK(!needs_layout_);
697 // Find the text run containing the argument position. 635 size_t position = caret.caret_pos();
636 // NB: exploits unsigned wraparound (0 - 1)
637 ui::Range pos(position, caret.caret_affinity() == CURSOR_BACKWARD ?
638 position - 1 : position + 1);
698 size_t run = 0; 639 size_t run = 0;
699 for (; run < runs_.size(); ++run) 640 for (; run < runs_.size(); ++run)
700 if (runs_[run]->range.start() <= position && 641 if (runs_[run]->range.Contains(pos))
701 runs_[run]->range.end() > position)
702 break; 642 break;
703 return run; 643 return run;
704 } 644 }
705 645
706 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { 646 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
707 DCHECK(!needs_layout_); 647 DCHECK(!needs_layout_);
708 // Find the text run containing the argument point (assumed already offset). 648 // Find the text run containing the argument point (assumed already offset).
709 size_t run = 0; 649 size_t run = 0;
710 for (; run < runs_.size(); ++run) 650 for (; run < runs_.size(); ++run)
711 if (runs_[run]->preceding_run_widths <= point.x() && 651 if (runs_[run]->preceding_run_widths <= point.x() &&
712 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) 652 runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
713 break; 653 break;
714 return run; 654 return run;
715 } 655 }
716 656
717 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( 657 SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
718 internal::TextRun* run) { 658 internal::TextRun* run) {
719 size_t caret = run->range.start(); 659 size_t cursor = IndexOfAdjacentGrapheme(run->range.start(), CURSOR_FORWARD);
720 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); 660 return SelectionModel(cursor, CURSOR_BACKWARD);
721 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
722 } 661 }
723 662
724 SelectionModel RenderTextWin::LastSelectionModelInsideRun( 663 SelectionModel RenderTextWin::LastSelectionModelInsideRun(
725 internal::TextRun* run) { 664 internal::TextRun* run) {
726 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD); 665 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD);
727 return SelectionModel(caret, caret, SelectionModel::LEADING); 666 return SelectionModel(caret, CURSOR_FORWARD);
728 } 667 }
729 668
730 RenderText* RenderText::CreateRenderText() { 669 RenderText* RenderText::CreateRenderText() {
731 return new RenderTextWin; 670 return new RenderTextWin;
732 } 671 }
733 672
734 } // namespace gfx 673 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698