OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/logging.h" | 9 #include "base/logging.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
138 TextRun::~TextRun() { | 138 TextRun::~TextRun() { |
139 ScriptFreeCache(&script_cache); | 139 ScriptFreeCache(&script_cache); |
140 } | 140 } |
141 | 141 |
142 } // namespace internal | 142 } // namespace internal |
143 | 143 |
144 RenderTextWin::RenderTextWin() | 144 RenderTextWin::RenderTextWin() |
145 : RenderText(), | 145 : RenderText(), |
146 script_control_(), | 146 script_control_(), |
147 script_state_(), | 147 script_state_(), |
148 string_width_(0) { | 148 string_width_(0) { |
msw
2011/11/28 21:15:46
Init needs_layout_
Alexei Svitkine (slow)
2011/11/28 21:58:13
Embarrassing! Done.
| |
149 // Omitting default constructors for script_* would leave POD uninitialized. | 149 // Omitting default constructors for script_* would leave POD uninitialized. |
150 HRESULT hr = 0; | 150 HRESULT hr = 0; |
151 | 151 |
152 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message. | 152 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message. |
153 // TODO(msw): Use Chrome/profile locale/language settings? | 153 // TODO(msw): Use Chrome/profile locale/language settings? |
154 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_); | 154 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_); |
155 DCHECK(SUCCEEDED(hr)); | 155 DCHECK(SUCCEEDED(hr)); |
156 | 156 |
157 hr = ScriptApplyDigitSubstitution(&digit_substitute_, | 157 hr = ScriptApplyDigitSubstitution(&digit_substitute_, |
158 &script_control_, | 158 &script_control_, |
159 &script_state_); | 159 &script_state_); |
160 DCHECK(SUCCEEDED(hr)); | 160 DCHECK(SUCCEEDED(hr)); |
161 script_control_.fMergeNeutralItems = true; | 161 script_control_.fMergeNeutralItems = true; |
162 | 162 |
163 MoveCursorTo(LeftEndSelectionModel()); | 163 MoveCursorTo(LeftEndSelectionModel()); |
164 } | 164 } |
165 | 165 |
166 RenderTextWin::~RenderTextWin() { | 166 RenderTextWin::~RenderTextWin() { |
167 STLDeleteContainerPointers(runs_.begin(), runs_.end()); | 167 STLDeleteContainerPointers(runs_.begin(), runs_.end()); |
168 } | 168 } |
169 | 169 |
170 int RenderTextWin::GetStringWidth() { | 170 int RenderTextWin::GetStringWidth() { |
171 EnsureLayout(); | |
171 return string_width_; | 172 return string_width_; |
172 } | 173 } |
173 | 174 |
174 void RenderTextWin::Draw(Canvas* canvas) { | 175 void RenderTextWin::Draw(Canvas* canvas) { |
176 EnsureLayout(); | |
175 DrawSelection(canvas); | 177 DrawSelection(canvas); |
176 DrawVisualText(canvas); | 178 DrawVisualText(canvas); |
177 DrawCursor(canvas); | 179 DrawCursor(canvas); |
178 } | 180 } |
179 | 181 |
180 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { | 182 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { |
181 if (text().empty()) | 183 if (text().empty()) |
182 return SelectionModel(); | 184 return SelectionModel(); |
183 | 185 |
186 EnsureLayout(); | |
184 // Find the run that contains the point and adjust the argument location. | 187 // Find the run that contains the point and adjust the argument location. |
185 Point p(ToTextPoint(point)); | 188 Point p(ToTextPoint(point)); |
186 size_t run_index = GetRunContainingPoint(p); | 189 size_t run_index = GetRunContainingPoint(p); |
187 if (run_index == runs_.size()) | 190 if (run_index == runs_.size()) |
188 return (p.x() < 0) ? LeftEndSelectionModel() : RightEndSelectionModel(); | 191 return (p.x() < 0) ? LeftEndSelectionModel() : RightEndSelectionModel(); |
189 internal::TextRun* run = runs_[run_index]; | 192 internal::TextRun* run = runs_[run_index]; |
190 | 193 |
191 int position = 0, trailing = 0; | 194 int position = 0, trailing = 0; |
192 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths, | 195 HRESULT hr = ScriptXtoCP(p.x() - run->preceding_run_widths, |
193 run->range.length(), | 196 run->range.length(), |
194 run->glyph_count, | 197 run->glyph_count, |
195 run->logical_clusters.get(), | 198 run->logical_clusters.get(), |
196 run->visible_attributes.get(), | 199 run->visible_attributes.get(), |
197 run->advance_widths.get(), | 200 run->advance_widths.get(), |
198 &(run->script_analysis), | 201 &(run->script_analysis), |
199 &position, | 202 &position, |
200 &trailing); | 203 &trailing); |
201 DCHECK(SUCCEEDED(hr)); | 204 DCHECK(SUCCEEDED(hr)); |
202 position += run->range.start(); | 205 position += run->range.start(); |
203 | 206 |
204 size_t cursor = position + trailing; | 207 size_t cursor = position + trailing; |
205 DCHECK_GE(cursor, 0U); | 208 DCHECK_GE(cursor, 0U); |
206 DCHECK_LE(cursor, text().length()); | 209 DCHECK_LE(cursor, text().length()); |
207 return SelectionModel(cursor, position, | 210 return SelectionModel(cursor, position, |
208 (trailing > 0) ? SelectionModel::TRAILING : SelectionModel::LEADING); | 211 (trailing > 0) ? SelectionModel::TRAILING : SelectionModel::LEADING); |
209 } | 212 } |
210 | 213 |
211 Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection, | 214 Rect RenderTextWin::GetCursorBounds(const SelectionModel& selection, |
212 bool insert_mode) { | 215 bool insert_mode) { |
216 EnsureLayout(); | |
217 | |
213 // Highlight the logical cursor (selection end) when not in insert mode. | 218 // Highlight the logical cursor (selection end) when not in insert mode. |
214 size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end(); | 219 size_t pos = insert_mode ? selection.caret_pos() : selection.selection_end(); |
215 size_t run_index = GetRunContainingPosition(pos); | 220 size_t run_index = GetRunContainingPosition(pos); |
216 internal::TextRun* run = run_index == runs_.size() ? NULL : runs_[run_index]; | 221 internal::TextRun* run = run_index == runs_.size() ? NULL : runs_[run_index]; |
217 | 222 |
218 int start_x = 0, end_x = 0; | 223 int start_x = 0, end_x = 0; |
219 if (run) { | 224 if (run) { |
220 HRESULT hr = 0; | 225 HRESULT hr = 0; |
221 hr = ScriptCPtoX(pos - run->range.start(), | 226 hr = ScriptCPtoX(pos - run->range.start(), |
222 false, | 227 false, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
255 rect.set_x(rect.right()); | 260 rect.set_x(rect.right()); |
256 rect.set_width(0); | 261 rect.set_width(0); |
257 } | 262 } |
258 rect.set_origin(ToViewPoint(rect.origin())); | 263 rect.set_origin(ToViewPoint(rect.origin())); |
259 return rect; | 264 return rect; |
260 } | 265 } |
261 | 266 |
262 SelectionModel RenderTextWin::GetLeftSelectionModel( | 267 SelectionModel RenderTextWin::GetLeftSelectionModel( |
263 const SelectionModel& selection, | 268 const SelectionModel& selection, |
264 BreakType break_type) { | 269 BreakType break_type) { |
270 EnsureLayout(); | |
271 | |
265 if (break_type == LINE_BREAK || text().empty()) | 272 if (break_type == LINE_BREAK || text().empty()) |
266 return LeftEndSelectionModel(); | 273 return LeftEndSelectionModel(); |
267 if (break_type == CHARACTER_BREAK) | 274 if (break_type == CHARACTER_BREAK) |
268 return LeftSelectionModel(selection); | 275 return LeftSelectionModel(selection); |
269 // TODO(msw): Implement word breaking. | 276 // TODO(msw): Implement word breaking. |
270 return RenderText::GetLeftSelectionModel(selection, break_type); | 277 return RenderText::GetLeftSelectionModel(selection, break_type); |
271 } | 278 } |
272 | 279 |
273 SelectionModel RenderTextWin::GetRightSelectionModel( | 280 SelectionModel RenderTextWin::GetRightSelectionModel( |
274 const SelectionModel& selection, | 281 const SelectionModel& selection, |
275 BreakType break_type) { | 282 BreakType break_type) { |
283 EnsureLayout(); | |
284 | |
276 if (break_type == LINE_BREAK || text().empty()) | 285 if (break_type == LINE_BREAK || text().empty()) |
277 return RightEndSelectionModel(); | 286 return RightEndSelectionModel(); |
278 if (break_type == CHARACTER_BREAK) | 287 if (break_type == CHARACTER_BREAK) |
279 return RightSelectionModel(selection); | 288 return RightSelectionModel(selection); |
280 // TODO(msw): Implement word breaking. | 289 // TODO(msw): Implement word breaking. |
281 return RenderText::GetRightSelectionModel(selection, break_type); | 290 return RenderText::GetRightSelectionModel(selection, break_type); |
282 } | 291 } |
283 | 292 |
284 SelectionModel RenderTextWin::LeftEndSelectionModel() { | 293 SelectionModel RenderTextWin::LeftEndSelectionModel() { |
msw
2011/11/28 21:15:46
Do we need EnsureLayout on these protected methods
Alexei Svitkine (slow)
2011/11/28 21:58:13
Done.
| |
285 if (text().empty()) | 294 if (text().empty()) |
286 return SelectionModel(0, 0, SelectionModel::LEADING); | 295 return SelectionModel(0, 0, SelectionModel::LEADING); |
287 size_t cursor = base::i18n::IsRTL() ? text().length() : 0; | 296 size_t cursor = base::i18n::IsRTL() ? text().length() : 0; |
288 internal::TextRun* run = runs_[visual_to_logical_[0]]; | 297 internal::TextRun* run = runs_[visual_to_logical_[0]]; |
289 bool rtl = run->script_analysis.fRTL; | 298 bool rtl = run->script_analysis.fRTL; |
290 size_t caret = rtl ? run->range.end() - 1 : run->range.start(); | 299 size_t caret = rtl ? run->range.end() - 1 : run->range.start(); |
291 SelectionModel::CaretPlacement placement = | 300 SelectionModel::CaretPlacement placement = |
292 rtl ? SelectionModel::TRAILING : SelectionModel::LEADING; | 301 rtl ? SelectionModel::TRAILING : SelectionModel::LEADING; |
293 return SelectionModel(cursor, caret, placement); | 302 return SelectionModel(cursor, caret, placement); |
294 } | 303 } |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
356 bounds.push_back(rect); | 365 bounds.push_back(rect); |
357 } | 366 } |
358 } | 367 } |
359 return bounds; | 368 return bounds; |
360 } | 369 } |
361 | 370 |
362 bool RenderTextWin::IsCursorablePosition(size_t position) { | 371 bool RenderTextWin::IsCursorablePosition(size_t position) { |
363 if (position == 0 || position == text().length()) | 372 if (position == 0 || position == text().length()) |
364 return true; | 373 return true; |
365 | 374 |
375 EnsureLayout(); | |
366 size_t run_index = GetRunContainingPosition(position); | 376 size_t run_index = GetRunContainingPosition(position); |
367 if (run_index >= runs_.size()) | 377 if (run_index >= runs_.size()) |
368 return false; | 378 return false; |
369 | 379 |
370 internal::TextRun* run = runs_[run_index]; | 380 internal::TextRun* run = runs_[run_index]; |
371 size_t start = run->range.start(); | 381 size_t start = run->range.start(); |
372 if (position == start) | 382 if (position == start) |
373 return true; | 383 return true; |
374 return run->logical_clusters[position - start] != | 384 return run->logical_clusters[position - start] != |
375 run->logical_clusters[position - start - 1]; | 385 run->logical_clusters[position - start - 1]; |
376 } | 386 } |
377 | 387 |
378 void RenderTextWin::UpdateLayout() { | 388 void RenderTextWin::UpdateLayout() { |
389 needs_layout_ = true; | |
msw
2011/11/28 21:15:46
Add a simple comment like "Layout is performed laz
Alexei Svitkine (slow)
2011/11/28 21:58:13
Done.
| |
390 } | |
391 | |
392 void RenderTextWin::EnsureLayout() { | |
msw
2011/11/28 21:15:46
This should be below IndexOfAdjacentGrapheme to ma
Alexei Svitkine (slow)
2011/11/28 21:58:13
Done.
| |
393 if (!needs_layout_) | |
394 return; | |
379 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. | 395 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. |
380 ItemizeLogicalText(); | 396 ItemizeLogicalText(); |
381 if (!runs_.empty()) | 397 if (!runs_.empty()) |
382 LayoutVisualText(); | 398 LayoutVisualText(); |
msw
2011/11/28 21:15:46
I think we actually need to reset |string_width_|
Alexei Svitkine (slow)
2011/11/28 21:58:13
Good catch! Done.
| |
399 needs_layout_ = false; | |
383 } | 400 } |
384 | 401 |
385 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { | 402 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { |
403 EnsureLayout(); | |
386 size_t run_index = GetRunContainingPosition(index); | 404 size_t run_index = GetRunContainingPosition(index); |
387 internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL; | 405 internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL; |
388 int start = run ? run->range.start() : 0; | 406 int start = run ? run->range.start() : 0; |
389 int length = run ? run->range.length() : text().length(); | 407 int length = run ? run->range.length() : text().length(); |
390 int ch = index - start; | 408 int ch = index - start; |
391 WORD cluster = run ? run->logical_clusters[ch] : 0; | 409 WORD cluster = run ? run->logical_clusters[ch] : 0; |
392 | 410 |
393 if (!next) { | 411 if (!next) { |
394 do { | 412 do { |
395 ch--; | 413 ch--; |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
549 internal::TextRun* run = runs_[visual_to_logical_[i]]; | 567 internal::TextRun* run = runs_[visual_to_logical_[i]]; |
550 run->preceding_run_widths = preceding_run_widths; | 568 run->preceding_run_widths = preceding_run_widths; |
551 const ABC& abc = run->abc_widths; | 569 const ABC& abc = run->abc_widths; |
552 run->width = abc.abcA + abc.abcB + abc.abcC; | 570 run->width = abc.abcA + abc.abcB + abc.abcC; |
553 preceding_run_widths += run->width; | 571 preceding_run_widths += run->width; |
554 } | 572 } |
555 string_width_ = preceding_run_widths; | 573 string_width_ = preceding_run_widths; |
556 } | 574 } |
557 | 575 |
558 size_t RenderTextWin::GetRunContainingPosition(size_t position) const { | 576 size_t RenderTextWin::GetRunContainingPosition(size_t position) const { |
577 DCHECK(!needs_layout_); | |
559 // Find the text run containing the argument position. | 578 // Find the text run containing the argument position. |
560 size_t run = 0; | 579 size_t run = 0; |
561 for (; run < runs_.size(); ++run) | 580 for (; run < runs_.size(); ++run) |
562 if (runs_[run]->range.start() <= position && | 581 if (runs_[run]->range.start() <= position && |
563 runs_[run]->range.end() > position) | 582 runs_[run]->range.end() > position) |
564 break; | 583 break; |
565 return run; | 584 return run; |
566 } | 585 } |
567 | 586 |
568 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { | 587 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { |
588 DCHECK(!needs_layout_); | |
569 // Find the text run containing the argument point (assumed already offset). | 589 // Find the text run containing the argument point (assumed already offset). |
570 size_t run = 0; | 590 size_t run = 0; |
571 for (; run < runs_.size(); ++run) | 591 for (; run < runs_.size(); ++run) |
572 if (runs_[run]->preceding_run_widths <= point.x() && | 592 if (runs_[run]->preceding_run_widths <= point.x() && |
573 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) | 593 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) |
574 break; | 594 break; |
575 return run; | 595 return run; |
576 } | 596 } |
577 | 597 |
578 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( | 598 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( |
msw
2011/11/28 21:15:46
Should these private methods |DCHECK(!needs_layout
Alexei Svitkine (slow)
2011/11/28 21:58:13
As they currently are, they don't need it. However
| |
579 internal::TextRun* run) { | 599 internal::TextRun* run) { |
580 size_t caret = run->range.start(); | 600 size_t caret = run->range.start(); |
581 size_t cursor = IndexOfAdjacentGrapheme(caret, true); | 601 size_t cursor = IndexOfAdjacentGrapheme(caret, true); |
582 return SelectionModel(cursor, caret, SelectionModel::TRAILING); | 602 return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
583 } | 603 } |
584 | 604 |
585 SelectionModel RenderTextWin::LastSelectionModelInsideRun( | 605 SelectionModel RenderTextWin::LastSelectionModelInsideRun( |
586 internal::TextRun* run) { | 606 internal::TextRun* run) { |
587 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false); | 607 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false); |
588 return SelectionModel(caret, caret, SelectionModel::LEADING); | 608 return SelectionModel(caret, caret, SelectionModel::LEADING); |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
734 Rect r(GetUpdatedCursorBounds()); | 754 Rect r(GetUpdatedCursorBounds()); |
735 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height()); | 755 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height()); |
736 } | 756 } |
737 } | 757 } |
738 | 758 |
739 RenderText* RenderText::CreateRenderText() { | 759 RenderText* RenderText::CreateRenderText() { |
740 return new RenderTextWin; | 760 return new RenderTextWin; |
741 } | 761 } |
742 | 762 |
743 } // namespace gfx | 763 } // namespace gfx |
OLD | NEW |