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 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |