| Index: chrome/browser/chromeos/input_method/candidate_window.cc
|
| diff --git a/chrome/browser/chromeos/input_method/candidate_window.cc b/chrome/browser/chromeos/input_method/candidate_window.cc
|
| index 68f9c63b21336bde77f3bfcd561ef298f120a257..5e566e593e05aa55ddc6c856c0e1acf9988c7ec9 100644
|
| --- a/chrome/browser/chromeos/input_method/candidate_window.cc
|
| +++ b/chrome/browser/chromeos/input_method/candidate_window.cc
|
| @@ -236,6 +236,15 @@ int ComputeShortcutColumnWidth(
|
| return shortcut_column_width;
|
| }
|
|
|
| +// Computes the page index. For instance, if the page size is 9, and the
|
| +// cursor is pointing to 13th candidate, the page index will be 1 (2nd
|
| +// page, as the index is zero-origin). Returns -1 on error.
|
| +int ComputePageIndex(const chromeos::InputMethodLookupTable& lookup_table) {
|
| + if (lookup_table.page_size > 0)
|
| + return lookup_table.cursor_absolute_index / lookup_table.page_size;
|
| + return -1;
|
| +}
|
| +
|
| // Computes candidate column width.
|
| int ComputeCandidateColumnWidth(
|
| const chromeos::InputMethodLookupTable& lookup_table) {
|
| @@ -244,8 +253,9 @@ int ComputeCandidateColumnWidth(
|
| CreateCandidateLabel(lookup_table.orientation));
|
|
|
| // Compute the start index of |lookup_table_|.
|
| - const int current_page_index =
|
| - lookup_table.cursor_absolute_index / lookup_table.page_size;
|
| + const int current_page_index = ComputePageIndex(lookup_table);
|
| + if (current_page_index < 0)
|
| + return 0;
|
| const size_t start_from = current_page_index * lookup_table.page_size;
|
|
|
| // Compute the max width in candidate labels.
|
| @@ -271,8 +281,9 @@ int ComputeAnnotationColumnWidth(
|
| CreateAnnotationLabel(lookup_table.orientation));
|
|
|
| // Compute the start index of |lookup_table_|.
|
| - const int current_page_index =
|
| - lookup_table.cursor_absolute_index / lookup_table.page_size;
|
| + const int current_page_index = ComputePageIndex(lookup_table);
|
| + if (current_page_index < 0)
|
| + return 0;
|
| const size_t start_from = current_page_index * lookup_table.page_size;
|
|
|
| // Compute max width in annotation labels.
|
| @@ -345,6 +356,14 @@ class CandidateWindowView : public views::View {
|
| // Updates the auxiliary text.
|
| void UpdateAuxiliaryText(const std::string& utf8_text);
|
|
|
| + // Returns true if we should update candidate views in the window. For
|
| + // instance, if we are going to show the same candidates as before, we
|
| + // don't have to update candidate views. This happens when the user just
|
| + // moves the cursor in the same page in the candidate window.
|
| + bool ShouldUpdateCandidateViews(
|
| + const InputMethodLookupTable& old_table,
|
| + const InputMethodLookupTable& new_table);
|
| +
|
| // Updates candidates of the candidate window from |lookup_table|.
|
| // Candidates are arranged per |orientation|.
|
| void UpdateCandidates(const InputMethodLookupTable& lookup_table);
|
| @@ -382,10 +401,6 @@ class CandidateWindowView : public views::View {
|
| // The lookup table (candidates).
|
| InputMethodLookupTable lookup_table_;
|
|
|
| - // Zero-origin index of the current page. If the cursor is on the first
|
| - // page, the value will be 0.
|
| - int current_page_index_;
|
| -
|
| // The index in the current page of the candidate currently being selected.
|
| int selected_candidate_index_in_page_;
|
|
|
| @@ -696,8 +711,7 @@ void CandidateView::OnMouseReleased(const views::MouseEvent& event,
|
|
|
| CandidateWindowView::CandidateWindowView(
|
| views::Widget* parent_frame)
|
| - : current_page_index_(0),
|
| - selected_candidate_index_in_page_(0),
|
| + : selected_candidate_index_in_page_(0),
|
| parent_frame_(parent_frame),
|
| candidate_area_(NULL),
|
| footer_area_(NULL),
|
| @@ -777,64 +791,89 @@ void CandidateWindowView::UpdateAuxiliaryText(const std::string& utf8_text) {
|
| target_label->SetText(UTF8ToWide(utf8_text));
|
| }
|
|
|
| +bool CandidateWindowView::ShouldUpdateCandidateViews(
|
| + const InputMethodLookupTable& old_table,
|
| + const InputMethodLookupTable& new_table) {
|
| + // Check if most table contents are identical.
|
| + if (old_table.page_size == new_table.page_size &&
|
| + old_table.orientation == new_table.orientation &&
|
| + old_table.candidates == new_table.candidates &&
|
| + old_table.labels == new_table.labels &&
|
| + old_table.annotations == new_table.annotations &&
|
| + // Check if the page indexes are identical.
|
| + ComputePageIndex(old_table) == ComputePageIndex(new_table)) {
|
| + // If all of the conditions are met, we don't have to update candidate
|
| + // views.
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| void CandidateWindowView::UpdateCandidates(
|
| - const InputMethodLookupTable& lookup_table) {
|
| - // Initialize candidate views if necessary.
|
| - MaybeInitializeCandidateViews(lookup_table);
|
| -
|
| - // In MaybeInitializeCandidateViews(),
|
| - // |lookup_table| values and |lookup_table_| values are compared,
|
| - // so this substitution is needed after the function.
|
| - lookup_table_ = lookup_table;
|
| -
|
| - // Compute the index of the current page.
|
| - current_page_index_ =
|
| - lookup_table.cursor_absolute_index / lookup_table.page_size;
|
| -
|
| - // Update the candidates in the current page.
|
| - const size_t start_from = current_page_index_ * lookup_table.page_size;
|
| -
|
| - // In some cases, engines send empty shortcut labels. For instance,
|
| - // ibus-mozc sends empty labels when they show suggestions. In this
|
| - // case, we should not show shortcut labels.
|
| - const bool no_shortcut_mode = (start_from < lookup_table_.labels.size() &&
|
| - lookup_table_.labels[start_from] == "");
|
| - for (size_t i = 0; i < candidate_views_.size(); ++i) {
|
| - const size_t index_in_page = i;
|
| - const size_t candidate_index = start_from + index_in_page;
|
| - CandidateView* candidate_view = candidate_views_[index_in_page];
|
| - // Set the shortcut text.
|
| - if (no_shortcut_mode) {
|
| - candidate_view->SetShortcutText(L"");
|
| - } else {
|
| - // At this moment, we don't use labels sent from engines for UX
|
| - // reasons. First, we want to show shortcut labels in empty rows
|
| - // (ex. show 6, 7, 8, ... in empty rows when the number of
|
| - // candidates is 5). Second, we want to add a period after each
|
| - // shortcut label when the candidate window is horizontal.
|
| - candidate_view->SetShortcutText(
|
| - CreateShortcutText(i, lookup_table_.orientation));
|
| + const InputMethodLookupTable& new_lookup_table) {
|
| + const bool should_update = ShouldUpdateCandidateViews(lookup_table_,
|
| + new_lookup_table);
|
| + // Updating the candidate views is expensive. We'll skip this if possible.
|
| + if (should_update) {
|
| + // Initialize candidate views if necessary.
|
| + MaybeInitializeCandidateViews(new_lookup_table);
|
| +
|
| + // Compute the index of the current page.
|
| + const int current_page_index = ComputePageIndex(new_lookup_table);
|
| + if (current_page_index < 0) {
|
| + LOG(ERROR) << "Invalid lookup_table: " << new_lookup_table.ToString();
|
| + return;
|
| }
|
| - // Set the candidate text.
|
| - if (candidate_index < lookup_table_.candidates.size() &&
|
| - candidate_index < lookup_table_.annotations.size()) {
|
| - candidate_view->SetCandidateText(
|
| - UTF8ToWide(lookup_table_.candidates[candidate_index]));
|
| - candidate_view->SetAnnotationText(
|
| - UTF8ToWide(lookup_table_.annotations[candidate_index]));
|
| - candidate_view->SetRowEnabled(true);
|
| - } else {
|
| - // Disable the empty row.
|
| - candidate_view->SetCandidateText(L"");
|
| - candidate_view->SetAnnotationText(L"");
|
| - candidate_view->SetRowEnabled(false);
|
| +
|
| + // Update the candidates in the current page.
|
| + const size_t start_from = current_page_index * new_lookup_table.page_size;
|
| +
|
| + // In some cases, engines send empty shortcut labels. For instance,
|
| + // ibus-mozc sends empty labels when they show suggestions. In this
|
| + // case, we should not show shortcut labels.
|
| + const bool no_shortcut_mode =
|
| + (start_from < new_lookup_table.labels.size() &&
|
| + new_lookup_table.labels[start_from] == "");
|
| + for (size_t i = 0; i < candidate_views_.size(); ++i) {
|
| + const size_t index_in_page = i;
|
| + const size_t candidate_index = start_from + index_in_page;
|
| + CandidateView* candidate_view = candidate_views_[index_in_page];
|
| + // Set the shortcut text.
|
| + if (no_shortcut_mode) {
|
| + candidate_view->SetShortcutText(L"");
|
| + } else {
|
| + // At this moment, we don't use labels sent from engines for UX
|
| + // reasons. First, we want to show shortcut labels in empty rows
|
| + // (ex. show 6, 7, 8, ... in empty rows when the number of
|
| + // candidates is 5). Second, we want to add a period after each
|
| + // shortcut label when the candidate window is horizontal.
|
| + candidate_view->SetShortcutText(
|
| + CreateShortcutText(i, new_lookup_table.orientation));
|
| + }
|
| + // Set the candidate text.
|
| + if (candidate_index < new_lookup_table.candidates.size() &&
|
| + candidate_index < new_lookup_table.annotations.size()) {
|
| + candidate_view->SetCandidateText(
|
| + UTF8ToWide(new_lookup_table.candidates[candidate_index]));
|
| + candidate_view->SetAnnotationText(
|
| + UTF8ToWide(new_lookup_table.annotations[candidate_index]));
|
| + candidate_view->SetRowEnabled(true);
|
| + } else {
|
| + // Disable the empty row.
|
| + candidate_view->SetCandidateText(L"");
|
| + candidate_view->SetAnnotationText(L"");
|
| + candidate_view->SetRowEnabled(false);
|
| + }
|
| }
|
| }
|
| -
|
| - // Select the first candidate candidate in the page.
|
| - const int first_candidate_in_page =
|
| - lookup_table.cursor_absolute_index % lookup_table.page_size;
|
| - SelectCandidateAt(first_candidate_in_page);
|
| + // Update the current lookup table. We'll use lookup_table_ from here.
|
| + // Note that SelectCandidateAt() uses lookup_table_.
|
| + lookup_table_ = new_lookup_table;
|
| +
|
| + // Select the current candidate in the page.
|
| + const int current_candidate_in_page =
|
| + lookup_table_.cursor_absolute_index % lookup_table_.page_size;
|
| + SelectCandidateAt(current_candidate_in_page);
|
| }
|
|
|
| void CandidateWindowView::MaybeInitializeCandidateViews(
|
| @@ -858,6 +897,13 @@ void CandidateWindowView::MaybeInitializeCandidateViews(
|
|
|
| // If the requested number of views matches the number of current views, and
|
| // previous and current column width are same, just reuse these.
|
| + //
|
| + // Note that the early exit logic is not only useful for improving
|
| + // performance, but also necessary for the horizontal candidate window
|
| + // to be redrawn properly. If we get rid of the logic, the horizontal
|
| + // candidate window won't get redrawn properly for some reason when
|
| + // there is no size change. You can test this by removing "return" here
|
| + // and type "ni" with Pinyin input method.
|
| if (static_cast<int>(candidate_views_.size()) == page_size &&
|
| lookup_table_.orientation == orientation &&
|
| previous_shortcut_column_width_ == shortcut_column_width &&
|
| @@ -996,8 +1042,14 @@ views::View* CandidateWindowView::CreateFooterArea() {
|
| }
|
|
|
| void CandidateWindowView::SelectCandidateAt(int index_in_page) {
|
| - int cursor_absolute_index =
|
| - lookup_table_.page_size * current_page_index_ + index_in_page;
|
| + const int current_page_index = ComputePageIndex(lookup_table_);
|
| + if (current_page_index < 0) {
|
| + LOG(ERROR) << "Invalid lookup_table: " << lookup_table_.ToString();
|
| + return;
|
| + }
|
| +
|
| + const int cursor_absolute_index =
|
| + lookup_table_.page_size * current_page_index + index_in_page;
|
| // Ignore click on out of range views.
|
| if (cursor_absolute_index < 0 ||
|
| cursor_absolute_index >=
|
| @@ -1211,7 +1263,7 @@ void CandidateWindowController::Impl::OnSetCursorLocation(
|
| controller->MoveCandidateWindow(
|
| controller->cursor_location(),
|
| controller->candidate_window_->GetHorizontalOffset());
|
| - // The call is needed to ensure that the candidate window is redrawed
|
| + // The call is needed to ensure that the candidate window is redrawn
|
| // properly after the cursor location is changed.
|
| controller->candidate_window_->ResizeAndSchedulePaint();
|
| }
|
|
|