Chromium Code Reviews| 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 |