| 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() + (trailing ? 0 : 1)); |
| 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 run = direction == CURSOR_RIGHT ? runs_.front() : runs_.back(); |
| 240 } else { |
| 241 // If the cursor is moving within the current run, just move it by one |
| 242 // grapheme in the appropriate direction. |
| 243 run = runs_[run_index]; |
| 244 size_t caret = selection.caret_pos(); |
| 245 bool forward_motion = |
| 246 run->script_analysis.fRTL == (direction == CURSOR_LEFT); |
| 247 if (forward_motion) { |
| 248 if (caret < run->range.end()) { |
| 249 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); |
| 250 return SelectionModel(caret, CURSOR_BACKWARD); |
| 251 } |
| 252 } else { |
| 253 if (caret > run->range.start()) { |
| 254 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); |
| 255 return SelectionModel(caret, CURSOR_FORWARD); |
| 256 } |
| 257 } |
| 258 // The cursor is at the edge of a run; move to the visually adjacent run. |
| 259 int visual_index = logical_to_visual_[run_index]; |
| 260 visual_index += (direction == CURSOR_LEFT) ? -1 : 1; |
| 261 if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size())) |
| 262 return EdgeSelectionModel(direction); |
| 263 run = runs_[visual_to_logical_[visual_index]]; |
| 264 } |
| 271 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); | 265 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); |
| 272 if (forward_motion) { | 266 return forward_motion ? FirstSelectionModelInsideRun(run) : |
| 273 if (caret_placement == SelectionModel::LEADING) { | 267 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 } | 268 } |
| 301 | 269 |
| 302 // TODO(msw): Implement word breaking for Windows. | 270 // TODO(msw): Implement word breaking for Windows. |
| 303 SelectionModel RenderTextWin::AdjacentWordSelectionModel( | 271 SelectionModel RenderTextWin::AdjacentWordSelectionModel( |
| 304 const SelectionModel& selection, | 272 const SelectionModel& selection, |
| 305 VisualCursorDirection direction) { | 273 VisualCursorDirection direction) { |
| 306 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | 274 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 307 bool success = iter.Init(); | 275 bool success = iter.Init(); |
| 308 DCHECK(success); | 276 DCHECK(success); |
| 309 if (!success) | 277 if (!success) |
| 310 return selection; | 278 return selection; |
| 311 | 279 |
| 312 size_t pos; | 280 size_t pos; |
| 313 if (direction == CURSOR_RIGHT) { | 281 if (direction == CURSOR_RIGHT) { |
| 314 pos = std::min(selection.selection_end() + 1, text().length()); | 282 pos = std::min(selection.caret_pos() + 1, text().length()); |
| 315 while (iter.Advance()) { | 283 while (iter.Advance()) { |
| 316 pos = iter.pos(); | 284 pos = iter.pos(); |
| 317 if (iter.IsWord() && pos > selection.selection_end()) | 285 if (iter.IsWord() && pos > selection.caret_pos()) |
| 318 break; | 286 break; |
| 319 } | 287 } |
| 320 } else { // direction == CURSOR_LEFT | 288 } else { // direction == CURSOR_LEFT |
| 321 // Notes: We always iterate words from the beginning. | 289 // Notes: We always iterate words from the beginning. |
| 322 // This is probably fast enough for our usage, but we may | 290 // This is probably fast enough for our usage, but we may |
| 323 // want to modify WordIterator so that it can start from the | 291 // want to modify WordIterator so that it can start from the |
| 324 // middle of string and advance backwards. | 292 // middle of string and advance backwards. |
| 325 pos = std::max<int>(selection.selection_end() - 1, 0); | 293 pos = std::max<int>(selection.caret_pos() - 1, 0); |
| 326 while (iter.Advance()) { | 294 while (iter.Advance()) { |
| 327 if (iter.IsWord()) { | 295 if (iter.IsWord()) { |
| 328 size_t begin = iter.pos() - iter.GetString().length(); | 296 size_t begin = iter.pos() - iter.GetString().length(); |
| 329 if (begin == selection.selection_end()) { | 297 if (begin == selection.caret_pos()) { |
| 330 // The cursor is at the beginning of a word. | 298 // The cursor is at the beginning of a word. |
| 331 // Move to previous word. | 299 // Move to previous word. |
| 332 break; | 300 break; |
| 333 } else if (iter.pos() >= selection.selection_end()) { | 301 } else if (iter.pos() >= selection.caret_pos()) { |
| 334 // The cursor is in the middle or at the end of a word. | 302 // The cursor is in the middle or at the end of a word. |
| 335 // Move to the top of current word. | 303 // Move to the top of current word. |
| 336 pos = begin; | 304 pos = begin; |
| 337 break; | 305 break; |
| 338 } else { | 306 } else { |
| 339 pos = iter.pos() - iter.GetString().length(); | 307 pos = iter.pos() - iter.GetString().length(); |
| 340 } | 308 } |
| 341 } | 309 } |
| 342 } | 310 } |
| 343 } | 311 } |
| 344 return SelectionModel(pos, pos, SelectionModel::LEADING); | 312 return SelectionModel(pos, CURSOR_FORWARD); |
| 345 } | 313 } |
| 346 | 314 |
| 347 SelectionModel RenderTextWin::EdgeSelectionModel( | 315 void RenderTextWin::GetGlyphBounds(size_t index, |
| 348 VisualCursorDirection direction) { | 316 ui::Range* xspan, |
| 349 if (text().empty()) | 317 int* height) { |
| 350 return SelectionModel(0, 0, SelectionModel::LEADING); | 318 size_t run_index = |
| 351 | 319 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
| 352 EnsureLayout(); | 320 DCHECK_LT(run_index, runs_.size()); |
| 353 size_t cursor = (direction == GetVisualDirectionOfLogicalEnd()) ? | 321 internal::TextRun* run = runs_[run_index]; |
| 354 text().length() : 0; | 322 xspan->set_start(GetGlyphXBoundary(run, index, false)); |
| 355 internal::TextRun* run = runs_[ | 323 xspan->set_end(GetGlyphXBoundary(run, index, true)); |
| 356 visual_to_logical_[direction == CURSOR_RIGHT ? runs_.size() - 1 : 0]]; | 324 *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 } | 325 } |
| 368 | 326 |
| 369 std::vector<Rect> RenderTextWin::GetSubstringBounds(size_t from, size_t to) { | 327 std::vector<Rect> RenderTextWin::GetSubstringBounds(ui::Range range) { |
| 370 DCHECK(!needs_layout_); | 328 DCHECK(!needs_layout_); |
| 371 ui::Range range(from, to); | |
| 372 DCHECK(ui::Range(0, text().length()).Contains(range)); | 329 DCHECK(ui::Range(0, text().length()).Contains(range)); |
| 373 Point display_offset(GetUpdatedDisplayOffset()); | 330 Point display_offset(GetUpdatedDisplayOffset()); |
| 374 HRESULT hr = 0; | 331 HRESULT hr = 0; |
| 375 | 332 |
| 376 std::vector<Rect> bounds; | 333 std::vector<Rect> bounds; |
| 377 if (from == to) | 334 if (range.is_empty()) |
| 378 return bounds; | 335 return bounds; |
| 379 | 336 |
| 380 // Add a Rect for each run/selection intersection. | 337 // Add a Rect for each run/selection intersection. |
| 381 // TODO(msw): The bounds should probably not always be leading the range ends. | 338 // TODO(msw): The bounds should probably not always be leading the range ends. |
| 382 for (size_t i = 0; i < runs_.size(); ++i) { | 339 for (size_t i = 0; i < runs_.size(); ++i) { |
| 383 internal::TextRun* run = runs_[visual_to_logical_[i]]; | 340 internal::TextRun* run = runs_[visual_to_logical_[i]]; |
| 384 ui::Range intersection = run->range.Intersect(range); | 341 ui::Range intersection = run->range.Intersect(range); |
| 385 if (intersection.IsValid()) { | 342 if (intersection.IsValid()) { |
| 386 DCHECK(!intersection.is_reversed()); | 343 DCHECK(!intersection.is_reversed()); |
| 387 int start_offset = 0; | 344 ui::Range range(GetGlyphXBoundary(run, intersection.start(), false), |
| 388 hr = ScriptCPtoX(intersection.start() - run->range.start(), | 345 GetGlyphXBoundary(run, intersection.end(), false)); |
| 389 false, | 346 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. | 347 // Center the rect vertically in the display area. |
| 414 rect.Offset(0, (display_rect().height() - rect.height()) / 2); | 348 rect.Offset(0, (display_rect().height() - rect.height()) / 2); |
| 415 rect.set_origin(ToViewPoint(rect.origin())); | 349 rect.set_origin(ToViewPoint(rect.origin())); |
| 416 // Union this with the last rect if they're adjacent. | 350 // Union this with the last rect if they're adjacent. |
| 417 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { | 351 if (!bounds.empty() && rect.SharesEdgeWith(bounds.back())) { |
| 418 rect = rect.Union(bounds.back()); | 352 rect = rect.Union(bounds.back()); |
| 419 bounds.pop_back(); | 353 bounds.pop_back(); |
| 420 } | 354 } |
| 421 bounds.push_back(rect); | 355 bounds.push_back(rect); |
| 422 } | 356 } |
| 423 } | 357 } |
| 424 return bounds; | 358 return bounds; |
| 425 } | 359 } |
| 426 | 360 |
| 427 void RenderTextWin::SetSelectionModel(const SelectionModel& model) { | 361 void RenderTextWin::SetSelectionModel(const SelectionModel& model) { |
| 428 RenderText::SetSelectionModel(model); | 362 RenderText::SetSelectionModel(model); |
| 429 // TODO(xji): The styles are applied to text inside ItemizeLogicalText(). So, | 363 // 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 | 364 // 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 | 365 // foreground, to be picked up. Eventually, we should separate styles from |
| 432 // layout by applying foreground, strike, and underline styles during | 366 // layout by applying foreground, strike, and underline styles during |
| 433 // DrawVisualText as what RenderTextLinux does. | 367 // DrawVisualText as what RenderTextLinux does. |
| 434 UpdateLayout(); | 368 UpdateLayout(); |
| 435 } | 369 } |
| 436 | 370 |
| 437 bool RenderTextWin::IsCursorablePosition(size_t position) { | 371 bool RenderTextWin::IsCursorablePosition(size_t position) { |
| 438 if (position == 0 || position == text().length()) | 372 if (position == 0 || position == text().length()) |
| 439 return true; | 373 return true; |
| 440 | 374 |
| 441 EnsureLayout(); | 375 EnsureLayout(); |
| 442 size_t run_index = GetRunContainingPosition(position); | 376 size_t run_index = |
| 377 GetRunContainingCaret(SelectionModel(position, CURSOR_FORWARD)); |
| 443 if (run_index >= runs_.size()) | 378 if (run_index >= runs_.size()) |
| 444 return false; | 379 return false; |
| 445 | 380 |
| 446 internal::TextRun* run = runs_[run_index]; | 381 internal::TextRun* run = runs_[run_index]; |
| 447 size_t start = run->range.start(); | 382 size_t start = run->range.start(); |
| 448 if (position == start) | 383 if (position == start) |
| 449 return true; | 384 return true; |
| 450 return run->logical_clusters[position - start] != | 385 return run->logical_clusters[position - start] != |
| 451 run->logical_clusters[position - start - 1]; | 386 run->logical_clusters[position - start - 1]; |
| 452 } | 387 } |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 532 return text().length(); | 467 return text().length(); |
| 533 } else { | 468 } else { |
| 534 // The requested |index| is at the end of the text. Use the index of the | 469 // The requested |index| is at the end of the text. Use the index of the |
| 535 // last character to find the grapheme. | 470 // last character to find the grapheme. |
| 536 index = text().length() - 1; | 471 index = text().length() - 1; |
| 537 if (IsCursorablePosition(index)) | 472 if (IsCursorablePosition(index)) |
| 538 return index; | 473 return index; |
| 539 } | 474 } |
| 540 } | 475 } |
| 541 | 476 |
| 542 size_t run_index = GetRunContainingPosition(index); | 477 size_t run_index = |
| 478 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); |
| 543 DCHECK(run_index < runs_.size()); | 479 DCHECK(run_index < runs_.size()); |
| 544 internal::TextRun* run = runs_[run_index]; | 480 internal::TextRun* run = runs_[run_index]; |
| 545 size_t start = run->range.start(); | 481 size_t start = run->range.start(); |
| 546 size_t ch = index - start; | 482 size_t ch = index - start; |
| 547 | 483 |
| 548 if (direction == CURSOR_BACKWARD) { | 484 if (direction == CURSOR_BACKWARD) { |
| 549 // If |ch| is the start of the run, use the preceding run, if any. | 485 // If |ch| is the start of the run, use the preceding run, if any. |
| 550 if (ch == 0) { | 486 if (ch == 0) { |
| 551 if (run_index == 0) | 487 if (run_index == 0) |
| 552 return 0; | 488 return 0; |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 781 cached_linked_fonts_.find(font_name); | 717 cached_linked_fonts_.find(font_name); |
| 782 if (it != cached_linked_fonts_.end()) | 718 if (it != cached_linked_fonts_.end()) |
| 783 return &it->second; | 719 return &it->second; |
| 784 | 720 |
| 785 cached_linked_fonts_[font_name] = std::vector<Font>(); | 721 cached_linked_fonts_[font_name] = std::vector<Font>(); |
| 786 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name]; | 722 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name]; |
| 787 QueryLinkedFontsFromRegistry(font, linked_fonts); | 723 QueryLinkedFontsFromRegistry(font, linked_fonts); |
| 788 return linked_fonts; | 724 return linked_fonts; |
| 789 } | 725 } |
| 790 | 726 |
| 791 size_t RenderTextWin::GetRunContainingPosition(size_t position) const { | 727 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const { |
| 792 DCHECK(!needs_layout_); | 728 DCHECK(!needs_layout_); |
| 793 // Find the text run containing the argument position. | 729 size_t position = caret.caret_pos(); |
| 730 LogicalCursorDirection affinity = caret.caret_affinity(); |
| 794 size_t run = 0; | 731 size_t run = 0; |
| 795 for (; run < runs_.size(); ++run) | 732 for (; run < runs_.size(); ++run) |
| 796 if (runs_[run]->range.start() <= position && | 733 if (RangeContainsCaret(runs_[run]->range, position, affinity)) |
| 797 runs_[run]->range.end() > position) | |
| 798 break; | 734 break; |
| 799 return run; | 735 return run; |
| 800 } | 736 } |
| 801 | 737 |
| 802 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { | 738 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { |
| 803 DCHECK(!needs_layout_); | 739 DCHECK(!needs_layout_); |
| 804 // Find the text run containing the argument point (assumed already offset). | 740 // Find the text run containing the argument point (assumed already offset). |
| 805 size_t run = 0; | 741 size_t run = 0; |
| 806 for (; run < runs_.size(); ++run) | 742 for (; run < runs_.size(); ++run) |
| 807 if (runs_[run]->preceding_run_widths <= point.x() && | 743 if (runs_[run]->preceding_run_widths <= point.x() && |
| 808 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) | 744 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) |
| 809 break; | 745 break; |
| 810 return run; | 746 return run; |
| 811 } | 747 } |
| 812 | 748 |
| 813 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( | 749 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( |
| 814 internal::TextRun* run) { | 750 internal::TextRun* run) { |
| 815 size_t caret = run->range.start(); | 751 size_t cursor = IndexOfAdjacentGrapheme(run->range.start(), CURSOR_FORWARD); |
| 816 size_t cursor = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); | 752 return SelectionModel(cursor, CURSOR_BACKWARD); |
| 817 return SelectionModel(cursor, caret, SelectionModel::TRAILING); | |
| 818 } | 753 } |
| 819 | 754 |
| 820 SelectionModel RenderTextWin::LastSelectionModelInsideRun( | 755 SelectionModel RenderTextWin::LastSelectionModelInsideRun( |
| 821 internal::TextRun* run) { | 756 internal::TextRun* run) { |
| 822 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD); | 757 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), CURSOR_BACKWARD); |
| 823 return SelectionModel(caret, caret, SelectionModel::LEADING); | 758 return SelectionModel(caret, CURSOR_FORWARD); |
| 824 } | 759 } |
| 825 | 760 |
| 826 RenderText* RenderText::CreateRenderText() { | 761 RenderText* RenderText::CreateRenderText() { |
| 827 return new RenderTextWin; | 762 return new RenderTextWin; |
| 828 } | 763 } |
| 829 | 764 |
| 830 } // namespace gfx | 765 } // namespace gfx |
| OLD | NEW |