OLD | NEW |
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 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 glyph_count(0), | 131 glyph_count(0), |
132 script_cache(NULL) { | 132 script_cache(NULL) { |
133 memset(&script_analysis, 0, sizeof(script_analysis)); | 133 memset(&script_analysis, 0, sizeof(script_analysis)); |
134 memset(&abc_widths, 0, sizeof(abc_widths)); | 134 memset(&abc_widths, 0, sizeof(abc_widths)); |
135 } | 135 } |
136 | 136 |
137 TextRun::~TextRun() { | 137 TextRun::~TextRun() { |
138 ScriptFreeCache(&script_cache); | 138 ScriptFreeCache(&script_cache); |
139 } | 139 } |
140 | 140 |
| 141 // Returns the X coordinate of the leading or |trailing| edge of the glyph |
| 142 // starting at |index|, relative to the left of the text (not the view). |
| 143 int GetGlyphXBoundary(internal::TextRun* run, size_t index, bool trailing) { |
| 144 DCHECK_GE(index, run->range.start()) |
| 145 DCHECK_LT(index, run->range.end()) |
| 146 int x = 0; |
| 147 HRESULT hr = ScriptCPtoX( |
| 148 index - run->range.start(), |
| 149 trailing, |
| 150 run->range.length(), |
| 151 run->glyph_count, |
| 152 run->logical_clusters.get(), |
| 153 run->visible_attributes.get(), |
| 154 run->advance_widths.get(), |
| 155 &run->script_analysis, |
| 156 &x); |
| 157 DCHECK(SUCCEEDED(hr)); |
| 158 return run->preceding_run_widths + x; |
| 159 } |
| 160 |
141 } // namespace internal | 161 } // namespace internal |
142 | 162 |
143 // static | 163 // static |
144 HDC RenderTextWin::cached_hdc_ = NULL; | 164 HDC RenderTextWin::cached_hdc_ = NULL; |
145 | 165 |
146 // static | 166 // static |
147 std::map<std::string, std::vector<Font> > RenderTextWin::cached_linked_fonts_; | 167 std::map<std::string, std::vector<Font> > RenderTextWin::cached_linked_fonts_; |
148 | 168 |
149 RenderTextWin::RenderTextWin() | 169 RenderTextWin::RenderTextWin() |
150 : RenderText(), | 170 : RenderText(), |
(...skipping 11 matching lines...) Expand all Loading... |
162 } | 182 } |
163 | 183 |
164 base::i18n::TextDirection RenderTextWin::GetTextDirection() { | 184 base::i18n::TextDirection RenderTextWin::GetTextDirection() { |
165 // TODO(benrg): Code moved from RenderText::GetTextDirection. Needs to be | 185 // TODO(benrg): Code moved from RenderText::GetTextDirection. Needs to be |
166 // replaced by a correct Windows implementation. | 186 // replaced by a correct Windows implementation. |
167 if (base::i18n::IsRTL()) | 187 if (base::i18n::IsRTL()) |
168 return base::i18n::RIGHT_TO_LEFT; | 188 return base::i18n::RIGHT_TO_LEFT; |
169 return base::i18n::LEFT_TO_RIGHT; | 189 return base::i18n::LEFT_TO_RIGHT; |
170 } | 190 } |
171 | 191 |
172 int RenderTextWin::GetStringWidth() { | 192 Size RenderTextWin::GetStringSize() { |
173 EnsureLayout(); | 193 EnsureLayout(); |
174 return string_width_; | 194 // TODO(msw): Use the largest font instead of the default font? |
| 195 return Size(string_width_, GetFont().GetHeight()); |
175 } | 196 } |
176 | 197 |
177 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { | 198 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { |
178 if (text().empty()) | 199 if (text().empty()) |
179 return SelectionModel(); | 200 return SelectionModel(); |
180 | 201 |
181 EnsureLayout(); | 202 EnsureLayout(); |
182 // Find the run that contains the point and adjust the argument location. | 203 // Find the run that contains the point and adjust the argument location. |
183 Point p(ToTextPoint(point)); | 204 Point p(ToTextPoint(point)); |
184 size_t run_index = GetRunContainingPoint(p); | 205 size_t run_index = GetRunContainingPoint(p); |
185 if (run_index == runs_.size()) | 206 if (run_index == runs_.size()) |
186 return EdgeSelectionModel((p.x() < 0) ? CURSOR_LEFT : CURSOR_RIGHT); | 207 return EdgeSelectionModel((p.x() < 0) ? CURSOR_LEFT : CURSOR_RIGHT); |
187 internal::TextRun* run = runs_[run_index]; | 208 internal::TextRun* run = runs_[run_index]; |
188 | 209 |
189 int position = 0, trailing = 0; | 210 int position = 0, trailing = 0; |
190 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths, | 211 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths, |
191 run->range.length(), | 212 run->range.length(), |
192 run->glyph_count, | 213 run->glyph_count, |
193 run->logical_clusters.get(), | 214 run->logical_clusters.get(), |
194 run->visible_attributes.get(), | 215 run->visible_attributes.get(), |
195 run->advance_widths.get(), | 216 run->advance_widths.get(), |
196 &(run->script_analysis), | 217 &(run->script_analysis), |
197 &position, | 218 &position, |
198 &trailing); | 219 &trailing); |
199 DCHECK(SUCCEEDED(hr)); | 220 DCHECK(SUCCEEDED(hr)); |
| 221 DCHECK_GE(trailing, 0); |
200 position += run->range.start(); | 222 position += run->range.start(); |
201 | |
202 size_t cursor = position + trailing; | 223 size_t cursor = position + trailing; |
203 DCHECK_GE(cursor, 0U); | |
204 DCHECK_LE(cursor, text().length()); | 224 DCHECK_LE(cursor, text().length()); |
205 return SelectionModel(cursor, position, | 225 return SelectionModel(cursor, trailing ? CURSOR_BACKWARD : CURSOR_FORWARD); |
206 (trailing > 0) ? SelectionModel::TRAILING : SelectionModel::LEADING); | |
207 } | |
208 | |
209 Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection, | |
210 bool insert_mode) { | |
211 EnsureLayout(); | |
212 | |
213 // Highlight the logical cursor (selection end) when not in insert mode. | |
214 size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end(); | |
215 size_t run_index = GetRunContainingPosition(pos); | |
216 internal::TextRun* run = run_index == runs_.size() ? NULL : runs_[run_index]; | |
217 | |
218 int start_x = 0, end_x = 0; | |
219 if (run) { | |
220 HRESULT hr = 0; | |
221 hr = ScriptCPtoX(pos - run->range.start(), | |
222 false, | |
223 run->range.length(), | |
224 run->glyph_count, | |
225 run->logical_clusters.get(), | |
226 run->visible_attributes.get(), | |
227 run->advance_widths.get(), | |
228 &(run->script_analysis), | |
229 &start_x); | |
230 DCHECK(SUCCEEDED(hr)); | |
231 hr = ScriptCPtoX(pos - run->range.start(), | |
232 true, | |
233 run->range.length(), | |
234 run->glyph_count, | |
235 run->logical_clusters.get(), | |
236 run->visible_attributes.get(), | |
237 run->advance_widths.get(), | |
238 &(run->script_analysis), | |
239 &end_x); | |
240 DCHECK(SUCCEEDED(hr)); | |
241 } | |
242 // TODO(msw): Use the last visual run's font instead of the default font? | |
243 int height = run ? run->font.GetHeight() : GetFont().GetHeight(); | |
244 Rect rect(std::min(start_x, end_x), 0, std::abs(end_x - start_x), height); | |
245 // Offset to the run start or the right/left end for an out of bounds index. | |
246 // Also center the rect vertically in the display area. | |
247 int text_end_offset = base::i18n::IsRTL() ? 0 : GetStringWidth(); | |
248 rect.Offset((run ? run->preceding_run_widths : text_end_offset), | |
249 (display_rect().height() - rect.height()) / 2); | |
250 // Adjust for leading/trailing in insert mode. | |
251 if (insert_mode && run) { | |
252 bool leading = selection.caret_placement() == SelectionModel::LEADING; | |
253 // Adjust the x value for right-side placement. | |
254 if (run->script_analysis.fRTL == leading) | |
255 rect.set_x(rect.right()); | |
256 rect.set_width(0); | |
257 } | |
258 rect.set_origin(ToViewPoint(rect.origin())); | |
259 return rect; | |
260 } | 226 } |
261 | 227 |
262 SelectionModel RenderTextWin::AdjacentCharSelectionModel( | 228 SelectionModel RenderTextWin::AdjacentCharSelectionModel( |
263 const SelectionModel& selection, VisualCursorDirection direction) { | 229 const SelectionModel& selection, |
| 230 VisualCursorDirection direction) { |
264 DCHECK(!needs_layout_); | 231 DCHECK(!needs_layout_); |
265 size_t caret = selection.caret_pos(); | 232 internal::TextRun* run; |
266 SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); | 233 size_t run_index = GetRunContainingCaret(selection); |
267 size_t run_index = GetRunContainingPosition(caret); | 234 if (run_index == runs_.size()) { |
268 DCHECK(run_index < runs_.size()); | 235 // The cursor is not in any run: we're at the visual and logical edge. |
269 internal::TextRun* run = runs_[run_index]; | 236 SelectionModel edge = EdgeSelectionModel(direction); |
270 | 237 if (edge.caret_pos() == selection.caret_pos()) |
| 238 return edge; |
| 239 else |
| 240 run = direction == CURSOR_RIGHT ? runs_.front() : runs_.back(); |
| 241 } else { |
| 242 // If the cursor is moving within the current run, just move it by one |
| 243 // grapheme in the appropriate direction. |
| 244 run = runs_[run_index]; |
| 245 size_t caret = selection.caret_pos(); |
| 246 bool forward_motion = |
| 247 run->script_analysis.fRTL == (direction == CURSOR_LEFT); |
| 248 if (forward_motion) { |
| 249 if (caret < run->range.end()) { |
| 250 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); |
| 251 return SelectionModel(caret, CURSOR_BACKWARD); |
| 252 } |
| 253 } else { |
| 254 if (caret > run->range.start()) { |
| 255 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); |
| 256 return SelectionModel(caret, CURSOR_FORWARD); |
| 257 } |
| 258 } |
| 259 // The cursor is at the edge of a run; move to the visually adjacent run. |
| 260 int visual_index = logical_to_visual_[run_index]; |
| 261 visual_index += (direction == CURSOR_LEFT) ? -1 : 1; |
| 262 if (visual_index < 0 || visual_index >= runs_.size()) |
| 263 return EdgeSelectionModel(direction); |
| 264 run = runs_[visual_to_logical_[visual_index]]; |
| 265 } |
271 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); | 266 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); |
272 if (forward_motion) { | 267 return forward_motion ? FirstSelectionModelInsideRun(run) : |
273 if (caret_placement == SelectionModel::LEADING) { | 268 LastSelectionModelInsideRun(run); |
274 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); | |
275 return SelectionModel(cursor, caret, SelectionModel::TRAILING); | |
276 } else if (selection.selection_end() < run->range.end()) { | |
277 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); | |
278 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); | |
279 return SelectionModel(cursor, caret, SelectionModel::TRAILING); | |
280 } | |
281 } else { | |
282 if (caret_placement == SelectionModel::TRAILING) | |
283 return SelectionModel(caret, caret, SelectionModel::LEADING); | |
284 else if (caret > run->range.start()) { | |
285 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); | |
286 return SelectionModel(caret, caret, SelectionModel::LEADING); | |
287 } | |
288 } | |
289 | |
290 // The character is at the beginning/end of its run; go to the previous/next | |
291 // visual run. | |
292 size_t visual_index = logical_to_visual_[run_index]; | |
293 if (visual_index == (direction == CURSOR_LEFT ? 0 : runs_.size() - 1)) | |
294 return EdgeSelectionModel(direction); | |
295 internal::TextRun* adjacent = runs_[visual_to_logical_[ | |
296 direction == CURSOR_LEFT ? visual_index - 1 : visual_index + 1]]; | |
297 forward_motion = adjacent->script_analysis.fRTL == (direction == CURSOR_LEFT); | |
298 return forward_motion ? FirstSelectionModelInsideRun(adjacent) : | |
299 LastSelectionModelInsideRun(adjacent); | |
300 } | 269 } |
301 | 270 |
302 // TODO(msw): Implement word breaking for Windows. | 271 // TODO(msw): Implement word breaking for Windows. |
303 SelectionModel RenderTextWin::AdjacentWordSelectionModel( | 272 SelectionModel RenderTextWin::AdjacentWordSelectionModel( |
304 const SelectionModel& selection, | 273 const SelectionModel& selection, |
305 VisualCursorDirection direction) { | 274 VisualCursorDirection direction) { |
306 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 275 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
307 bool success = iter.Init(); | 276 bool success = iter.Init(); |
308 DCHECK(success); | 277 DCHECK(success); |
309 if (!success) | 278 if (!success) |
310 return selection; | 279 return selection; |
311 | 280 |
312 size_t pos; | 281 size_t pos; |
313 if (direction == CURSOR_RIGHT) { | 282 if (direction == CURSOR_RIGHT) { |
314 pos = std::min(selection.selection_end() + 1, text().length()); | 283 pos = std::min(selection.caret_pos() + 1, text().length()); |
315 while (iter.Advance()) { | 284 while (iter.Advance()) { |
316 pos = iter.pos(); | 285 pos = iter.pos(); |
317 if (iter.IsWord() && pos > selection.selection_end()) | 286 if (iter.IsWord() && pos > selection.caret_pos()) |
318 break; | 287 break; |
319 } | 288 } |
320 } else { // direction == CURSOR_LEFT | 289 } else { // direction == CURSOR_LEFT |
321 // Notes: We always iterate words from the beginning. | 290 // Notes: We always iterate words from the beginning. |
322 // This is probably fast enough for our usage, but we may | 291 // This is probably fast enough for our usage, but we may |
323 // want to modify WordIterator so that it can start from the | 292 // want to modify WordIterator so that it can start from the |
324 // middle of string and advance backwards. | 293 // middle of string and advance backwards. |
325 pos = std::max<int>(selection.selection_end() - 1, 0); | 294 pos = std::max<int>(selection.caret_pos() - 1, 0); |
326 while (iter.Advance()) { | 295 while (iter.Advance()) { |
327 if (iter.IsWord()) { | 296 if (iter.IsWord()) { |
328 size_t begin = iter.pos() - iter.GetString().length(); | 297 size_t begin = iter.pos() - iter.GetString().length(); |
329 if (begin == selection.selection_end()) { | 298 if (begin == selection.caret_pos()) { |
330 // The cursor is at the beginning of a word. | 299 // The cursor is at the beginning of a word. |
331 // Move to previous word. | 300 // Move to previous word. |
332 break; | 301 break; |
333 } else if (iter.pos() >= selection.selection_end()) { | 302 } else if (iter.pos() >= selection.caret_pos()) { |
334 // The cursor is in the middle or at the end of a word. | 303 // The cursor is in the middle or at the end of a word. |
335 // Move to the top of current word. | 304 // Move to the top of current word. |
336 pos = begin; | 305 pos = begin; |
337 break; | 306 break; |
338 } else { | 307 } else { |
339 pos = iter.pos() - iter.GetString().length(); | 308 pos = iter.pos() - iter.GetString().length(); |
340 } | 309 } |
341 } | 310 } |
342 } | 311 } |
343 } | 312 } |
344 return SelectionModel(pos, pos, SelectionModel::LEADING); | 313 return SelectionModel(pos, CURSOR_FORWARD); |
345 } | 314 } |
346 | 315 |
347 SelectionModel RenderTextWin::EdgeSelectionModel( | 316 void RenderTextWin::GetGlyphBounds(size_t index, |
348 VisualCursorDirection direction) { | 317 ui::Range* xspan, |
349 if (text().empty()) | 318 int* height) { |
350 return SelectionModel(0, 0, SelectionModel::LEADING); | 319 size_t run_index = |
351 | 320 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
352 EnsureLayout(); | 321 DCHECK_LT(run_index, runs_.size()); |
353 size_t cursor = (direction == GetVisualDirectionOfLogicalEnd()) ? | 322 internal::TextRun* run = runs_[run_index]; |
354 text().length() : 0; | 323 xspan->set_start(GetGlyphXBoundary(run, index, false)); |
355 internal::TextRun* run = runs_[ | 324 xspan->set_end(GetGlyphXBoundary(run, index, true)); |
356 visual_to_logical_[direction == CURSOR_RIGHT ? runs_.size() - 1 : 0]]; | 325 *height = run->font.GetHeight(); |
357 size_t caret; | |
358 SelectionModel::CaretPlacement placement; | |
359 if (run->script_analysis.fRTL == (direction == CURSOR_RIGHT)) { | |
360 caret = run->range.start(); | |
361 placement = SelectionModel::LEADING; | |
362 } else { | |
363 caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD); | |
364 placement = SelectionModel::TRAILING; | |
365 } | |
366 return SelectionModel(cursor, caret, placement); | |
367 } | 326 } |
368 | 327 |
369 std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) { | 328 std::vector<Rect> RenderTextWin::GetSubstringBounds(ui::Range range) { |
370 DCHECK(!needs_layout_); | 329 DCHECK(!needs_layout_); |
371 ui::Range range(from, to); | |
372 DCHECK(ui::Range(0, text().length()).Contains(range)); | 330 DCHECK(ui::Range(0, text().length()).Contains(range)); |
373 Point display_offset(GetUpdatedDisplayOffset()); | 331 Point display_offset(GetUpdatedDisplayOffset()); |
374 HRESULT hr = 0; | 332 HRESULT hr = 0; |
375 | 333 |
376 std::vector<Rect> bounds; | 334 std::vector<Rect> bounds; |
377 if (from == to) | 335 if (range.is_empty()) |
378 return bounds; | 336 return bounds; |
379 | 337 |
380 // Add a Rect for each run/selection intersection. | 338 // Add a Rect for each run/selection intersection. |
381 // TODO(msw): The bounds should probably not always be leading the range ends. | 339 // TODO(msw): The bounds should probably not always be leading the range ends. |
382 for (size_t i = 0; i < runs_.size(); ++i) { | 340 for (size_t i = 0; i < runs_.size(); ++i) { |
383 internal::TextRun* run = runs_[visual_to_logical_[i]]; | 341 internal::TextRun* run = runs_[visual_to_logical_[i]]; |
384 ui::Range intersection = run->range.Intersect(range); | 342 ui::Range intersection = run->range.Intersect(range); |
385 if (intersection.IsValid()) { | 343 if (intersection.IsValid()) { |
386 DCHECK(!intersection.is_reversed()); | 344 DCHECK(!intersection.is_reversed()); |
387 int start_offset = 0; | 345 ui::Range range(GetGlyphXBoundary(run, intersection.start(), false), |
388 hr = ScriptCPtoX(intersection.start() - run->range.start(), | 346 GetGlyphXBoundary(run, intersection.end(), false)); |
389 false, | 347 Rect rect(range.GetMin(), 0, range.length(), run->font.GetHeight()); |
390 run->range.length(), | |
391 run->glyph_count, | |
392 run->logical_clusters.get(), | |
393 run->visible_attributes.get(), | |
394 run->advance_widths.get(), | |
395 &(run->script_analysis), | |
396 &start_offset); | |
397 DCHECK(SUCCEEDED(hr)); | |
398 int end_offset = 0; | |
399 hr = ScriptCPtoX(intersection.end() - run->range.start(), | |
400 false, | |
401 run->range.length(), | |
402 run->glyph_count, | |
403 run->logical_clusters.get(), | |
404 run->visible_attributes.get(), | |
405 run->advance_widths.get(), | |
406 &(run->script_analysis), | |
407 &end_offset); | |
408 DCHECK(SUCCEEDED(hr)); | |
409 if (start_offset > end_offset) | |
410 std::swap(start_offset, end_offset); | |
411 Rect rect(run->preceding_run_widths + start_offset, 0, | |
412 end_offset - start_offset, run->font.GetHeight()); | |
413 // Center the rect vertically in the display area. | 348 // Center the rect vertically in the display area. |
414 rect.Offset(0, (display_rect().height() - rect.height()) / 2); | 349 rect.Offset(0, (display_rect().height() - rect.height()) / 2); |
415 rect.set_origin(ToViewPoint(rect.origin())); | 350 rect.set_origin(ToViewPoint(rect.origin())); |
416 // Union this with the last rect if they're adjacent. | 351 // Union this with the last rect if they're adjacent. |
417 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { | 352 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { |
418 rect = rect.Union(bounds.back()); | 353 rect = rect.Union(bounds.back()); |
419 bounds.pop_back(); | 354 bounds.pop_back(); |
420 } | 355 } |
421 bounds.push_back(rect); | 356 bounds.push_back(rect); |
422 } | 357 } |
423 } | 358 } |
424 return bounds; | 359 return bounds; |
425 } | 360 } |
426 | 361 |
427 void RenderTextWin::SetSelectionModel(const SelectionModel& model) { | 362 void RenderTextWin::SetSelectionModel(const SelectionModel& model) { |
428 RenderText::SetSelectionModel(model); | 363 RenderText::SetSelectionModel(model); |
429 // TODO(xji): The styles are applied to text inside ItemizeLogicalText(). So, | 364 // TODO(xji): The styles are applied to text inside ItemizeLogicalText(). So, |
430 // we need to update layout here in order for the styles, such as selection | 365 // we need to update layout here in order for the styles, such as selection |
431 // foreground, to be picked up. Eventually, we should separate styles from | 366 // foreground, to be picked up. Eventually, we should separate styles from |
432 // layout by applying foreground, strike, and underline styles during | 367 // layout by applying foreground, strike, and underline styles during |
433 // DrawVisualText as what RenderTextLinux does. | 368 // DrawVisualText as what RenderTextLinux does. |
434 UpdateLayout(); | 369 UpdateLayout(); |
435 } | 370 } |
436 | 371 |
437 bool RenderTextWin::IsCursorablePosition(size_t position) { | 372 bool RenderTextWin::IsCursorablePosition(size_t position) { |
438 if (position == 0 || position == text().length()) | 373 if (position == 0 || position == text().length()) |
439 return true; | 374 return true; |
440 | 375 |
441 EnsureLayout(); | 376 EnsureLayout(); |
442 size_t run_index = GetRunContainingPosition(position); | 377 size_t run_index = |
| 378 GetRunContainingCaret(SelectionModel(position, CURSOR_FORWARD)); |
443 if (run_index >= runs_.size()) | 379 if (run_index >= runs_.size()) |
444 return false; | 380 return false; |
445 | 381 |
446 internal::TextRun* run = runs_[run_index]; | 382 internal::TextRun* run = runs_[run_index]; |
447 size_t start = run->range.start(); | 383 size_t start = run->range.start(); |
448 if (position == start) | 384 if (position == start) |
449 return true; | 385 return true; |
450 return run->logical_clusters[position - start] != | 386 return run->logical_clusters[position - start] != |
451 run->logical_clusters[position - start - 1]; | 387 run->logical_clusters[position - start - 1]; |
452 } | 388 } |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
532 return text().length(); | 468 return text().length(); |
533 } else { | 469 } else { |
534 // The requested |index| is at the end of the text. Use the index of the | 470 // The requested |index| is at the end of the text. Use the index of the |
535 // last character to find the grapheme. | 471 // last character to find the grapheme. |
536 index = text().length() - 1; | 472 index = text().length() - 1; |
537 if (IsCursorablePosition(index)) | 473 if (IsCursorablePosition(index)) |
538 return index; | 474 return index; |
539 } | 475 } |
540 } | 476 } |
541 | 477 |
542 size_t run_index = GetRunContainingPosition(index); | 478 size_t run_index = |
| 479 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
543 DCHECK(run_index < runs_.size()); | 480 DCHECK(run_index < runs_.size()); |
544 internal::TextRun* run = runs_[run_index]; | 481 internal::TextRun* run = runs_[run_index]; |
545 size_t start = run->range.start(); | 482 size_t start = run->range.start(); |
546 size_t ch = index - start; | 483 size_t ch = index - start; |
547 | 484 |
548 if (direction == CURSOR_BACKWARD) { | 485 if (direction == CURSOR_BACKWARD) { |
549 // If |ch| is the start of the run, use the preceding run, if any. | 486 // If |ch| is the start of the run, use the preceding run, if any. |
550 if (ch == 0) { | 487 if (ch == 0) { |
551 if (run_index == 0) | 488 if (run_index == 0) |
552 return 0; | 489 return 0; |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
781 cached_linked_fonts_.find(font_name); | 718 cached_linked_fonts_.find(font_name); |
782 if (it != cached_linked_fonts_.end()) | 719 if (it != cached_linked_fonts_.end()) |
783 return &it->second; | 720 return &it->second; |
784 | 721 |
785 cached_linked_fonts_[font_name] = std::vector<Font>(); | 722 cached_linked_fonts_[font_name] = std::vector<Font>(); |
786 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name]; | 723 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name]; |
787 QueryLinkedFontsFromRegistry(font, linked_fonts); | 724 QueryLinkedFontsFromRegistry(font, linked_fonts); |
788 return linked_fonts; | 725 return linked_fonts; |
789 } | 726 } |
790 | 727 |
791 size_t RenderTextWin::GetRunContainingPosition(size_t position) const { | 728 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const { |
792 DCHECK(!needs_layout_); | 729 DCHECK(!needs_layout_); |
793 // Find the text run containing the argument position. | 730 size_t position = caret.caret_pos(); |
| 731 LogicalCursorDirection affinity = caret.caret_affinity(); |
794 size_t run = 0; | 732 size_t run = 0; |
795 for (; run < runs_.size(); ++run) | 733 for (; run < runs_.size(); ++run) |
796 if (runs_[run]->range.start() <= position && | 734 if (RangeContainsCaret(runs_[run]->range, position, affinity)) |
797 runs_[run]->range.end() > position) | |
798 break; | 735 break; |
799 return run; | 736 return run; |
800 } | 737 } |
801 | 738 |
802 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { | 739 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { |
803 DCHECK(!needs_layout_); | 740 DCHECK(!needs_layout_); |
804 // Find the text run containing the argument point (assumed already offset). | 741 // Find the text run containing the argument point (assumed already offset). |
805 size_t run = 0; | 742 size_t run = 0; |
806 for (; run < runs_.size(); ++run) | 743 for (; run < runs_.size(); ++run) |
807 if (runs_[run]->preceding_run_widths <= point.x() && | 744 if (runs_[run]->preceding_run_widths <= point.x() && |
808 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) | 745 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) |
809 break; | 746 break; |
810 return run; | 747 return run; |
811 } | 748 } |
812 | 749 |
813 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( | 750 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( |
814 internal::TextRun* run) { | 751 internal::TextRun* run) { |
815 size_t caret = run->range.start(); | 752 size_t cursor = IndexOfAdjacentGrapheme(run->range.start(), CURSOR_FORWARD); |
816 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); | 753 return SelectionModel(cursor, CURSOR_BACKWARD); |
817 return SelectionModel(cursor, caret, SelectionModel::TRAILING); | |
818 } | 754 } |
819 | 755 |
820 SelectionModel RenderTextWin::LastSelectionModelInsideRun( | 756 SelectionModel RenderTextWin::LastSelectionModelInsideRun( |
821 internal::TextRun* run) { | 757 internal::TextRun* run) { |
822 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD); | 758 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD); |
823 return SelectionModel(caret, caret, SelectionModel::LEADING); | 759 return SelectionModel(caret, CURSOR_FORWARD); |
824 } | 760 } |
825 | 761 |
826 RenderText* RenderText::CreateRenderText() { | 762 RenderText* RenderText::CreateRenderText() { |
827 return new RenderTextWin; | 763 return new RenderTextWin; |
828 } | 764 } |
829 | 765 |
830 } // namespace gfx | 766 } // namespace gfx |
OLD | NEW |