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