| 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 |