| 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.h" | 5 #include "ui/gfx/render_text.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 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 157 } | 157 } |
| 158 } | 158 } |
| 159 style_ranges_.back().range.set_end(text_.length()); | 159 style_ranges_.back().range.set_end(text_.length()); |
| 160 } | 160 } |
| 161 #ifndef NDEBUG | 161 #ifndef NDEBUG |
| 162 CheckStyleRanges(style_ranges_, text_.length()); | 162 CheckStyleRanges(style_ranges_, text_.length()); |
| 163 #endif | 163 #endif |
| 164 cached_bounds_and_offset_valid_ = false; | 164 cached_bounds_and_offset_valid_ = false; |
| 165 } | 165 } |
| 166 | 166 |
| 167 void RenderText::SetSelectionModel(const SelectionModel& sel) { | 167 void RenderText::ToggleInsertMode() { |
| 168 size_t start = sel.selection_start(); | 168 insert_mode_ = !insert_mode_; |
| 169 size_t end = sel.selection_end(); | |
| 170 selection_model_.set_selection_start(std::min(start, text().length())); | |
| 171 selection_model_.set_selection_end(std::min(end, text().length())); | |
| 172 selection_model_.set_caret_pos(std::min(sel.caret_pos(), text().length())); | |
| 173 selection_model_.set_caret_placement(sel.caret_placement()); | |
| 174 | |
| 175 cached_bounds_and_offset_valid_ = false; | 169 cached_bounds_and_offset_valid_ = false; |
| 176 } | 170 } |
| 177 | 171 |
| 178 void RenderText::SetDisplayRect(const Rect& r) { | 172 void RenderText::SetDisplayRect(const Rect& r) { |
| 179 display_rect_ = r; | 173 display_rect_ = r; |
| 180 cached_bounds_and_offset_valid_ = false; | 174 cached_bounds_and_offset_valid_ = false; |
| 181 } | 175 } |
| 182 | 176 |
| 183 size_t RenderText::GetCursorPosition() const { | 177 size_t RenderText::GetCursorPosition() const { |
| 184 return selection_model_.selection_end(); | 178 return selection_model_.selection_end(); |
| 185 } | 179 } |
| 186 | 180 |
| 187 void RenderText::SetCursorPosition(const size_t position) { | 181 void RenderText::SetCursorPosition(size_t position) { |
| 188 SelectionModel sel(selection_model()); | 182 MoveCursorTo(position, false); |
| 189 sel.set_selection_start(position); | |
| 190 sel.set_selection_end(position); | |
| 191 sel.set_caret_pos(GetIndexOfPreviousGrapheme(position)); | |
| 192 sel.set_caret_placement(SelectionModel::TRAILING); | |
| 193 SetSelectionModel(sel); | |
| 194 } | 183 } |
| 195 | 184 |
| 196 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { | 185 void RenderText::MoveCursorLeft(BreakType break_type, bool select) { |
| 197 SelectionModel position(selection_model()); | 186 SelectionModel position(selection_model()); |
| 198 position.set_selection_start(GetCursorPosition()); | 187 position.set_selection_start(GetCursorPosition()); |
| 199 // Cancelling a selection moves to the edge of the selection. | 188 // Cancelling a selection moves to the edge of the selection. |
| 200 if (break_type != LINE_BREAK && !EmptySelection() && !select) { | 189 if (break_type != LINE_BREAK && !EmptySelection() && !select) { |
| 201 // Use the selection start if it is left of the selection end. | 190 // Use the selection start if it is left of the selection end. |
| 202 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), | 191 SelectionModel selection_start(GetSelectionStart(), GetSelectionStart(), |
| 203 SelectionModel::LEADING); | 192 SelectionModel::LEADING); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 230 if (break_type == WORD_BREAK) | 219 if (break_type == WORD_BREAK) |
| 231 position = GetRightSelectionModel(position, break_type); | 220 position = GetRightSelectionModel(position, break_type); |
| 232 } else { | 221 } else { |
| 233 position = GetRightSelectionModel(position, break_type); | 222 position = GetRightSelectionModel(position, break_type); |
| 234 } | 223 } |
| 235 if (select) | 224 if (select) |
| 236 position.set_selection_start(GetSelectionStart()); | 225 position.set_selection_start(GetSelectionStart()); |
| 237 MoveCursorTo(position); | 226 MoveCursorTo(position); |
| 238 } | 227 } |
| 239 | 228 |
| 240 bool RenderText::MoveCursorTo(const SelectionModel& selection) { | 229 bool RenderText::MoveCursorTo(const SelectionModel& selection_model) { |
| 241 bool changed = !selection.Equals(selection_model_); | 230 SelectionModel sel(selection_model); |
| 242 SetSelectionModel(selection); | 231 size_t text_length = text().length(); |
| 232 // Enforce valid selection model components. |
| 233 if (sel.selection_start() > text_length) |
| 234 sel.set_selection_start(text_length); |
| 235 if (sel.selection_end() > text_length) |
| 236 sel.set_selection_end(text_length); |
| 237 // The current model only supports caret positions at valid character indices. |
| 238 if (text_length == 0) { |
| 239 sel.set_caret_pos(0); |
| 240 sel.set_caret_placement(SelectionModel::LEADING); |
| 241 } else if (sel.caret_pos() >= text_length) { |
| 242 SelectionModel end = GetTextDirection() == base::i18n::RIGHT_TO_LEFT ? |
| 243 LeftEndSelectionModel() : RightEndSelectionModel(); |
| 244 sel.set_caret_pos(end.caret_pos()); |
| 245 sel.set_caret_placement(end.caret_placement()); |
| 246 } |
| 247 bool changed = !sel.Equals(selection_model_); |
| 248 SetSelectionModel(sel); |
| 243 return changed; | 249 return changed; |
| 244 } | 250 } |
| 245 | 251 |
| 246 bool RenderText::MoveCursorTo(const Point& point, bool select) { | 252 bool RenderText::MoveCursorTo(const Point& point, bool select) { |
| 247 SelectionModel selection = FindCursorPosition(point); | 253 SelectionModel selection = FindCursorPosition(point); |
| 248 if (select) | 254 if (select) |
| 249 selection.set_selection_start(GetSelectionStart()); | 255 selection.set_selection_start(GetSelectionStart()); |
| 250 return MoveCursorTo(selection); | 256 return MoveCursorTo(selection); |
| 251 } | 257 } |
| 252 | 258 |
| 253 bool RenderText::IsPointInSelection(const Point& point) { | 259 bool RenderText::IsPointInSelection(const Point& point) { |
| 260 if (EmptySelection()) |
| 261 return false; |
| 254 // TODO(xji): should this check whether the point is inside the visual | 262 // TODO(xji): should this check whether the point is inside the visual |
| 255 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points | 263 // selection bounds? In case of "abcFED", if "ED" is selected, |point| points |
| 256 // to the right half of 'c', is the point in selection? | 264 // to the right half of 'c', is the point in selection? |
| 257 size_t pos = FindCursorPosition(point).selection_end(); | 265 size_t pos = FindCursorPosition(point).selection_end(); |
| 258 return (pos >= MinOfSelection() && pos < MaxOfSelection()); | 266 return (pos >= MinOfSelection() && pos < MaxOfSelection()); |
| 259 } | 267 } |
| 260 | 268 |
| 261 void RenderText::ClearSelection() { | 269 void RenderText::ClearSelection() { |
| 262 SelectionModel sel(selection_model()); | 270 SelectionModel sel(selection_model()); |
| 263 sel.set_selection_start(GetCursorPosition()); | 271 sel.set_selection_start(GetCursorPosition()); |
| 264 SetSelectionModel(sel); | 272 SetSelectionModel(sel); |
| 265 } | 273 } |
| 266 | 274 |
| 267 void RenderText::SelectAll() { | 275 void RenderText::SelectAll() { |
| 268 SelectionModel sel(0, text().length(), | 276 SelectionModel sel(RightEndSelectionModel()); |
| 269 text().length(), SelectionModel::LEADING); | 277 sel.set_selection_start(LeftEndSelectionModel().selection_start()); |
| 270 SetSelectionModel(sel); | 278 SetSelectionModel(sel); |
| 271 } | 279 } |
| 272 | 280 |
| 273 // TODO(xji): it does not work for languages do not use space as word breaker, | 281 // TODO(xji): it does not work for languages do not use space as word breaker, |
| 274 // such as Chinese. Should use BreakIterator. | 282 // such as Chinese. Should use BreakIterator. |
| 275 void RenderText::SelectWord() { | 283 void RenderText::SelectWord() { |
| 276 size_t selection_start = GetSelectionStart(); | 284 size_t selection_start = GetSelectionStart(); |
| 277 size_t cursor_position = GetCursorPosition(); | 285 size_t cursor_position = GetCursorPosition(); |
| 278 // First we setup selection_start_ and selection_end_. There are so many cases | 286 // First we setup selection_start_ and selection_end_. There are so many cases |
| 279 // because we try to emulate what select-word looks like in a gtk textfield. | 287 // because we try to emulate what select-word looks like in a gtk textfield. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 301 } | 309 } |
| 302 | 310 |
| 303 // Now we move selection_end_ to end of selection. Selection boundary | 311 // Now we move selection_end_ to end of selection. Selection boundary |
| 304 // is defined as the position where we have alpha-num character on one side | 312 // is defined as the position where we have alpha-num character on one side |
| 305 // and non-alpha-num char on the other side. | 313 // and non-alpha-num char on the other side. |
| 306 for (; cursor_position < text().length(); cursor_position++) { | 314 for (; cursor_position < text().length(); cursor_position++) { |
| 307 if (IsPositionAtWordSelectionBoundary(cursor_position)) | 315 if (IsPositionAtWordSelectionBoundary(cursor_position)) |
| 308 break; | 316 break; |
| 309 } | 317 } |
| 310 | 318 |
| 311 SelectionModel sel(selection_model()); | 319 MoveCursorTo(selection_start, false); |
| 312 sel.set_selection_start(selection_start); | 320 MoveCursorTo(cursor_position, true); |
| 313 sel.set_selection_end(cursor_position); | |
| 314 sel.set_caret_pos(GetIndexOfPreviousGrapheme(cursor_position)); | |
| 315 sel.set_caret_placement(SelectionModel::TRAILING); | |
| 316 SetSelectionModel(sel); | |
| 317 } | 321 } |
| 318 | 322 |
| 319 const ui::Range& RenderText::GetCompositionRange() const { | 323 const ui::Range& RenderText::GetCompositionRange() const { |
| 320 return composition_range_; | 324 return composition_range_; |
| 321 } | 325 } |
| 322 | 326 |
| 323 void RenderText::SetCompositionRange(const ui::Range& composition_range) { | 327 void RenderText::SetCompositionRange(const ui::Range& composition_range) { |
| 324 CHECK(!composition_range.IsValid() || | 328 CHECK(!composition_range.IsValid() || |
| 325 ui::Range(0, text_.length()).Contains(composition_range)); | 329 ui::Range(0, text_.length()).Contains(composition_range)); |
| 326 composition_range_.set_end(composition_range.end()); | 330 composition_range_.set_end(composition_range.end()); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 343 | 347 |
| 344 void RenderText::ApplyDefaultStyle() { | 348 void RenderText::ApplyDefaultStyle() { |
| 345 style_ranges_.clear(); | 349 style_ranges_.clear(); |
| 346 StyleRange style = StyleRange(default_style_); | 350 StyleRange style = StyleRange(default_style_); |
| 347 style.range.set_end(text_.length()); | 351 style.range.set_end(text_.length()); |
| 348 style_ranges_.push_back(style); | 352 style_ranges_.push_back(style); |
| 349 cached_bounds_and_offset_valid_ = false; | 353 cached_bounds_and_offset_valid_ = false; |
| 350 } | 354 } |
| 351 | 355 |
| 352 base::i18n::TextDirection RenderText::GetTextDirection() const { | 356 base::i18n::TextDirection RenderText::GetTextDirection() const { |
| 353 // TODO(msw): Bidi implementation, intended to replace the functionality added | 357 if (base::i18n::IsRTL()) |
| 354 // in crrev.com/91881 (discussed in codereview.chromium.org/7324011). | 358 return base::i18n::RIGHT_TO_LEFT; |
| 355 return base::i18n::LEFT_TO_RIGHT; | 359 return base::i18n::LEFT_TO_RIGHT; |
| 356 } | 360 } |
| 357 | 361 |
| 358 int RenderText::GetStringWidth() { | 362 int RenderText::GetStringWidth() { |
| 359 return default_style_.font.GetStringWidth(text()); | 363 return default_style_.font.GetStringWidth(text()); |
| 360 } | 364 } |
| 361 | 365 |
| 362 void RenderText::Draw(Canvas* canvas) { | 366 void RenderText::Draw(Canvas* canvas) { |
| 363 // Clip the canvas to the text display area. | 367 // Clip the canvas to the text display area. |
| 364 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), | 368 canvas->ClipRectInt(display_rect_.x(), display_rect_.y(), |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 436 } else if (pivot == x) { | 440 } else if (pivot == x) { |
| 437 return SelectionModel(pivot_pos); | 441 return SelectionModel(pivot_pos); |
| 438 } else { | 442 } else { |
| 439 right = pivot; | 443 right = pivot; |
| 440 right_pos = pivot_pos; | 444 right_pos = pivot_pos; |
| 441 } | 445 } |
| 442 } | 446 } |
| 443 return SelectionModel(left_pos); | 447 return SelectionModel(left_pos); |
| 444 } | 448 } |
| 445 | 449 |
| 446 std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) { | |
| 447 size_t start = std::min(from, to); | |
| 448 size_t end = std::max(from, to); | |
| 449 const Font& font = default_style_.font; | |
| 450 int start_x = font.GetStringWidth(text().substr(0, start)); | |
| 451 int end_x = font.GetStringWidth(text().substr(0, end)); | |
| 452 Rect rect(start_x, 0, end_x - start_x, font.GetHeight()); | |
| 453 rect.Offset(display_rect_.origin()); | |
| 454 rect.Offset(GetUpdatedDisplayOffset()); | |
| 455 // Center the rect vertically in |display_rect_|. | |
| 456 rect.Offset(Point(0, (display_rect_.height() - rect.height()) / 2)); | |
| 457 return std::vector<Rect>(1, rect); | |
| 458 } | |
| 459 | |
| 460 Rect RenderText::GetCursorBounds(const SelectionModel& selection, | 450 Rect RenderText::GetCursorBounds(const SelectionModel& selection, |
| 461 bool insert_mode) { | 451 bool insert_mode) { |
| 462 size_t from = selection.selection_end(); | 452 size_t from = selection.selection_end(); |
| 463 size_t to = insert_mode ? from : std::min(text_.length(), from + 1); | 453 size_t to = insert_mode ? from : std::min(text_.length(), from + 1); |
| 464 return GetSubstringBounds(from, to)[0]; | 454 return GetSubstringBounds(from, to)[0]; |
| 465 } | 455 } |
| 466 | 456 |
| 467 const Rect& RenderText::GetUpdatedCursorBounds() { | 457 const Rect& RenderText::GetUpdatedCursorBounds() { |
| 468 UpdateCachedBoundsAndOffset(); | 458 UpdateCachedBoundsAndOffset(); |
| 469 return cursor_bounds_; | 459 return cursor_bounds_; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 542 if (!success) | 532 if (!success) |
| 543 return current; | 533 return current; |
| 544 while (iter.Advance()) { | 534 while (iter.Advance()) { |
| 545 pos = iter.pos(); | 535 pos = iter.pos(); |
| 546 if (iter.IsWord() && pos > current.selection_end()) | 536 if (iter.IsWord() && pos > current.selection_end()) |
| 547 break; | 537 break; |
| 548 } | 538 } |
| 549 return SelectionModel(pos, pos, SelectionModel::LEADING); | 539 return SelectionModel(pos, pos, SelectionModel::LEADING); |
| 550 } | 540 } |
| 551 | 541 |
| 542 SelectionModel RenderText::LeftEndSelectionModel() { |
| 543 return SelectionModel(0, 0, SelectionModel::LEADING); |
| 544 } |
| 545 |
| 546 SelectionModel RenderText::RightEndSelectionModel() { |
| 547 size_t cursor = text().length(); |
| 548 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); |
| 549 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? |
| 550 SelectionModel::LEADING : SelectionModel::TRAILING; |
| 551 return SelectionModel(cursor, caret_pos, placement); |
| 552 } |
| 553 |
| 552 size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) { | 554 size_t RenderText::GetIndexOfPreviousGrapheme(size_t position) { |
| 553 // TODO(msw): Handle complex script. | 555 // TODO(msw): Handle complex script. |
| 554 return std::max(static_cast<int>(position - 1), static_cast<int>(0)); | 556 return std::max(static_cast<long>(position - 1), static_cast<long>(0)); |
| 557 } |
| 558 |
| 559 std::vector<Rect> RenderText::GetSubstringBounds(size_t from, size_t to) { |
| 560 size_t start = std::min(from, to); |
| 561 size_t end = std::max(from, to); |
| 562 const Font& font = default_style_.font; |
| 563 int start_x = font.GetStringWidth(text().substr(0, start)); |
| 564 int end_x = font.GetStringWidth(text().substr(0, end)); |
| 565 Rect rect(start_x, 0, end_x - start_x, font.GetHeight()); |
| 566 rect.Offset(display_rect_.origin()); |
| 567 rect.Offset(GetUpdatedDisplayOffset()); |
| 568 // Center the rect vertically in |display_rect_|. |
| 569 rect.Offset(Point(0, (display_rect_.height() - rect.height()) / 2)); |
| 570 return std::vector<Rect>(1, rect); |
| 555 } | 571 } |
| 556 | 572 |
| 557 void RenderText::ApplyCompositionAndSelectionStyles( | 573 void RenderText::ApplyCompositionAndSelectionStyles( |
| 558 StyleRanges* style_ranges) const { | 574 StyleRanges* style_ranges) const { |
| 559 // TODO(msw): This pattern ought to be reconsidered; what about composition | 575 // TODO(msw): This pattern ought to be reconsidered; what about composition |
| 560 // and selection overlaps, retain existing local style features? | 576 // and selection overlaps, retain existing local style features? |
| 561 // Apply a composition style override to a copy of the style ranges. | 577 // Apply a composition style override to a copy of the style ranges. |
| 562 if (composition_range_.IsValid() && !composition_range_.is_empty()) { | 578 if (composition_range_.IsValid() && !composition_range_.is_empty()) { |
| 563 StyleRange composition_style(default_style_); | 579 StyleRange composition_style(default_style_); |
| 564 composition_style.underline = true; | 580 composition_style.underline = true; |
| 565 composition_style.range.set_start(composition_range_.start()); | 581 composition_style.range.set_start(composition_range_.start()); |
| 566 composition_style.range.set_end(composition_range_.end()); | 582 composition_style.range.set_end(composition_range_.end()); |
| 567 ApplyStyleRangeImpl(style_ranges, composition_style); | 583 ApplyStyleRangeImpl(style_ranges, composition_style); |
| 568 } | 584 } |
| 569 // Apply a selection style override to a copy of the style ranges. | 585 // Apply a selection style override to a copy of the style ranges. |
| 570 if (!EmptySelection()) { | 586 if (!EmptySelection()) { |
| 571 StyleRange selection_style(default_style_); | 587 StyleRange selection_style(default_style_); |
| 572 selection_style.foreground = kSelectedTextColor; | 588 selection_style.foreground = kSelectedTextColor; |
| 573 selection_style.range.set_start(MinOfSelection()); | 589 selection_style.range.set_start(MinOfSelection()); |
| 574 selection_style.range.set_end(MaxOfSelection()); | 590 selection_style.range.set_end(MaxOfSelection()); |
| 575 ApplyStyleRangeImpl(style_ranges, selection_style); | 591 ApplyStyleRangeImpl(style_ranges, selection_style); |
| 576 } | 592 } |
| 577 } | 593 } |
| 578 | 594 |
| 595 Point RenderText::ToTextPoint(const Point& point) { |
| 596 Point p(point.Subtract(display_rect().origin())); |
| 597 p = p.Subtract(GetUpdatedDisplayOffset()); |
| 598 if (base::i18n::IsRTL()) |
| 599 p.Offset(GetStringWidth() - display_rect().width() + 1, 0); |
| 600 return p; |
| 601 } |
| 602 |
| 603 Point RenderText::ToViewPoint(const Point& point) { |
| 604 Point p(point.Add(display_rect().origin())); |
| 605 p = p.Add(GetUpdatedDisplayOffset()); |
| 606 if (base::i18n::IsRTL()) |
| 607 p.Offset(display_rect().width() - GetStringWidth() - 1, 0); |
| 608 return p; |
| 609 } |
| 610 |
| 611 void RenderText::SetSelectionModel(const SelectionModel& selection_model) { |
| 612 DCHECK_LE(selection_model.selection_start(), text().length()); |
| 613 selection_model_.set_selection_start(selection_model.selection_start()); |
| 614 DCHECK_LE(selection_model.selection_end(), text().length()); |
| 615 selection_model_.set_selection_end(selection_model.selection_end()); |
| 616 DCHECK_LT(selection_model.caret_pos(), |
| 617 std::max(text().length(), static_cast<size_t>(1))); |
| 618 selection_model_.set_caret_pos(selection_model.caret_pos()); |
| 619 selection_model_.set_caret_placement(selection_model.caret_placement()); |
| 620 |
| 621 cached_bounds_and_offset_valid_ = false; |
| 622 } |
| 623 |
| 624 void RenderText::MoveCursorTo(size_t position, bool select) { |
| 625 size_t cursor = std::min(position, text().length()); |
| 626 size_t caret_pos = GetIndexOfPreviousGrapheme(cursor); |
| 627 SelectionModel::CaretPlacement placement = (caret_pos == cursor) ? |
| 628 SelectionModel::LEADING : SelectionModel::TRAILING; |
| 629 size_t selection_start = select ? GetSelectionStart() : cursor; |
| 630 SelectionModel sel(selection_start, cursor, caret_pos, placement); |
| 631 SetSelectionModel(sel); |
| 632 } |
| 633 |
| 579 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { | 634 bool RenderText::IsPositionAtWordSelectionBoundary(size_t pos) { |
| 580 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || | 635 return pos == 0 || (u_isalnum(text()[pos - 1]) && !u_isalnum(text()[pos])) || |
| 581 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); | 636 (!u_isalnum(text()[pos - 1]) && u_isalnum(text()[pos])); |
| 582 } | 637 } |
| 583 | 638 |
| 584 void RenderText::UpdateCachedBoundsAndOffset() { | 639 void RenderText::UpdateCachedBoundsAndOffset() { |
| 585 if (cached_bounds_and_offset_valid_) | 640 if (cached_bounds_and_offset_valid_) |
| 586 return; | 641 return; |
| 587 // First, set the valid flag true to calculate the current cursor bounds using | 642 // First, set the valid flag true to calculate the current cursor bounds using |
| 588 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 643 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| 589 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 644 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
| 590 cached_bounds_and_offset_valid_ = true; | 645 cached_bounds_and_offset_valid_ = true; |
| 591 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); | 646 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_); |
| 592 cursor_bounds_.set_width(std::max(cursor_bounds_.width(), 1)); | |
| 593 // Update |display_offset_| to ensure the current cursor is visible. | 647 // Update |display_offset_| to ensure the current cursor is visible. |
| 594 int display_width = display_rect_.width(); | 648 int display_width = display_rect_.width(); |
| 595 int string_width = GetStringWidth(); | 649 int string_width = GetStringWidth(); |
| 596 int delta_offset = 0; | 650 int delta_offset = 0; |
| 597 if (string_width < display_width) { | 651 if (string_width < display_width) { |
| 598 // Show all text whenever the text fits to the size. | 652 // Show all text whenever the text fits to the size. |
| 599 delta_offset = -display_offset_.x(); | 653 delta_offset = -display_offset_.x(); |
| 600 } else if (cursor_bounds_.right() > display_rect_.right()) { | 654 } else if (cursor_bounds_.right() > display_rect_.right()) { |
| 601 // Pan to show the cursor when it overflows to the right, | 655 // Pan to show the cursor when it overflows to the right, |
| 602 delta_offset = display_rect_.right() - cursor_bounds_.right(); | 656 delta_offset = display_rect_.right() - cursor_bounds_.right(); |
| 603 } else if (cursor_bounds_.x() < display_rect_.x()) { | 657 } else if (cursor_bounds_.x() < display_rect_.x()) { |
| 604 // Pan to show the cursor when it overflows to the left. | 658 // Pan to show the cursor when it overflows to the left. |
| 605 delta_offset = display_rect_.x() - cursor_bounds_.x(); | 659 delta_offset = display_rect_.x() - cursor_bounds_.x(); |
| 606 } | 660 } |
| 607 display_offset_.Offset(delta_offset, 0); | 661 display_offset_.Offset(delta_offset, 0); |
| 608 cursor_bounds_.Offset(delta_offset, 0); | 662 cursor_bounds_.Offset(delta_offset, 0); |
| 609 } | 663 } |
| 610 | 664 |
| 611 } // namespace gfx | 665 } // namespace gfx |
| OLD | NEW |