Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 "chrome/browser/chromeos/input_method/candidate_window.h" | 5 #include "chrome/browser/chromeos/input_method/candidate_window.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 229 shortcut_label->SetText( | 229 shortcut_label->SetText( |
| 230 CreateShortcutText(i, lookup_table.orientation)); | 230 CreateShortcutText(i, lookup_table.orientation)); |
| 231 shortcut_column_width = | 231 shortcut_column_width = |
| 232 std::max(shortcut_column_width, | 232 std::max(shortcut_column_width, |
| 233 wrapped_shortcut_label->GetPreferredSize().width()); | 233 wrapped_shortcut_label->GetPreferredSize().width()); |
| 234 } | 234 } |
| 235 | 235 |
| 236 return shortcut_column_width; | 236 return shortcut_column_width; |
| 237 } | 237 } |
| 238 | 238 |
| 239 // Computes the page index. For instance, if the page size is 9, and the | |
| 240 // cursor is pointing to 13th candidate, the page index will be 1 (2nd | |
| 241 // page, as the index is zero-origin). Returns -1 on error. | |
| 242 int ComputePageIndex(const chromeos::InputMethodLookupTable& lookup_table) { | |
| 243 if (lookup_table.page_size > 0) | |
| 244 return lookup_table.cursor_absolute_index / lookup_table.page_size; | |
| 245 return -1; | |
| 246 } | |
| 247 | |
| 239 // Computes candidate column width. | 248 // Computes candidate column width. |
| 240 int ComputeCandidateColumnWidth( | 249 int ComputeCandidateColumnWidth( |
| 241 const chromeos::InputMethodLookupTable& lookup_table) { | 250 const chromeos::InputMethodLookupTable& lookup_table) { |
| 242 int candidate_column_width = 0; | 251 int candidate_column_width = 0; |
| 243 scoped_ptr<views::Label> candidate_label( | 252 scoped_ptr<views::Label> candidate_label( |
| 244 CreateCandidateLabel(lookup_table.orientation)); | 253 CreateCandidateLabel(lookup_table.orientation)); |
| 245 | 254 |
| 246 // Compute the start index of |lookup_table_|. | 255 // Compute the start index of |lookup_table_|. |
| 247 const int current_page_index = | 256 const int current_page_index = ComputePageIndex(lookup_table); |
| 248 lookup_table.cursor_absolute_index / lookup_table.page_size; | 257 if (current_page_index < 0) |
| 258 return 0; | |
| 249 const size_t start_from = current_page_index * lookup_table.page_size; | 259 const size_t start_from = current_page_index * lookup_table.page_size; |
| 250 | 260 |
| 251 // Compute the max width in candidate labels. | 261 // Compute the max width in candidate labels. |
| 252 // We'll create temporary candidate labels, and choose the largest width. | 262 // We'll create temporary candidate labels, and choose the largest width. |
| 253 for (size_t i = 0; i < lookup_table.candidates.size(); ++i) { | 263 for (size_t i = 0; i < lookup_table.candidates.size(); ++i) { |
| 254 const size_t index = start_from + i; | 264 const size_t index = start_from + i; |
| 255 | 265 |
| 256 candidate_label->SetText( | 266 candidate_label->SetText( |
| 257 UTF8ToWide(lookup_table.candidates[index])); | 267 UTF8ToWide(lookup_table.candidates[index])); |
| 258 candidate_column_width = | 268 candidate_column_width = |
| 259 std::max(candidate_column_width, | 269 std::max(candidate_column_width, |
| 260 candidate_label->GetPreferredSize().width()); | 270 candidate_label->GetPreferredSize().width()); |
| 261 } | 271 } |
| 262 | 272 |
| 263 return candidate_column_width; | 273 return candidate_column_width; |
| 264 } | 274 } |
| 265 | 275 |
| 266 // Computes annotation column width. | 276 // Computes annotation column width. |
| 267 int ComputeAnnotationColumnWidth( | 277 int ComputeAnnotationColumnWidth( |
| 268 const chromeos::InputMethodLookupTable& lookup_table) { | 278 const chromeos::InputMethodLookupTable& lookup_table) { |
| 269 int annotation_column_width = 0; | 279 int annotation_column_width = 0; |
| 270 scoped_ptr<views::Label> annotation_label( | 280 scoped_ptr<views::Label> annotation_label( |
| 271 CreateAnnotationLabel(lookup_table.orientation)); | 281 CreateAnnotationLabel(lookup_table.orientation)); |
| 272 | 282 |
| 273 // Compute the start index of |lookup_table_|. | 283 // Compute the start index of |lookup_table_|. |
| 274 const int current_page_index = | 284 const int current_page_index = ComputePageIndex(lookup_table); |
| 275 lookup_table.cursor_absolute_index / lookup_table.page_size; | 285 if (current_page_index < 0) |
| 286 return 0; | |
| 276 const size_t start_from = current_page_index * lookup_table.page_size; | 287 const size_t start_from = current_page_index * lookup_table.page_size; |
| 277 | 288 |
| 278 // Compute max width in annotation labels. | 289 // Compute max width in annotation labels. |
| 279 // We'll create temporary annotation labels, and choose the largest width. | 290 // We'll create temporary annotation labels, and choose the largest width. |
| 280 for (size_t i = 0; i < lookup_table.annotations.size(); ++i) { | 291 for (size_t i = 0; i < lookup_table.annotations.size(); ++i) { |
| 281 const size_t index = start_from + i; | 292 const size_t index = start_from + i; |
| 282 | 293 |
| 283 annotation_label->SetText( | 294 annotation_label->SetText( |
| 284 UTF8ToWide(lookup_table.annotations[index])); | 295 UTF8ToWide(lookup_table.annotations[index])); |
| 285 annotation_column_width = | 296 annotation_column_width = |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 338 | 349 |
| 339 // Hides the auxiliary text. | 350 // Hides the auxiliary text. |
| 340 void HideAuxiliaryText(); | 351 void HideAuxiliaryText(); |
| 341 | 352 |
| 342 // Shows the auxiliary text. | 353 // Shows the auxiliary text. |
| 343 void ShowAuxiliaryText(); | 354 void ShowAuxiliaryText(); |
| 344 | 355 |
| 345 // Updates the auxiliary text. | 356 // Updates the auxiliary text. |
| 346 void UpdateAuxiliaryText(const std::string& utf8_text); | 357 void UpdateAuxiliaryText(const std::string& utf8_text); |
| 347 | 358 |
| 359 // Returns true if we should update candidate views in the window. For | |
| 360 // instance, if we are going to show the same candidates as before, we | |
| 361 // don't have to update candidate views. This happens when the user just | |
| 362 // moves the cursor in the same page in the candidate window. | |
| 363 bool ShouldUpdateCandidateViews( | |
| 364 const InputMethodLookupTable& previous_table, | |
| 365 const InputMethodLookupTable& current_table); | |
| 366 | |
| 348 // Updates candidates of the candidate window from |lookup_table|. | 367 // Updates candidates of the candidate window from |lookup_table|. |
| 349 // Candidates are arranged per |orientation|. | 368 // Candidates are arranged per |orientation|. |
| 350 void UpdateCandidates(const InputMethodLookupTable& lookup_table); | 369 void UpdateCandidates(const InputMethodLookupTable& lookup_table); |
| 351 | 370 |
| 352 // Resizes the parent frame and schedules painting. This needs to be | 371 // Resizes the parent frame and schedules painting. This needs to be |
| 353 // called when the visible contents of the candidate window are | 372 // called when the visible contents of the candidate window are |
| 354 // modified. | 373 // modified. |
| 355 void ResizeAndSchedulePaint(); | 374 void ResizeAndSchedulePaint(); |
| 356 | 375 |
| 357 // Returns the horizontal offset used for placing the vertical candidate | 376 // Returns the horizontal offset used for placing the vertical candidate |
| (...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 770 ResizeAndSchedulePaint(); | 789 ResizeAndSchedulePaint(); |
| 771 } | 790 } |
| 772 | 791 |
| 773 void CandidateWindowView::UpdateAuxiliaryText(const std::string& utf8_text) { | 792 void CandidateWindowView::UpdateAuxiliaryText(const std::string& utf8_text) { |
| 774 views::Label* target_label = ( | 793 views::Label* target_label = ( |
| 775 lookup_table_.orientation == InputMethodLookupTable::kHorizontal ? | 794 lookup_table_.orientation == InputMethodLookupTable::kHorizontal ? |
| 776 header_label_ : footer_label_); | 795 header_label_ : footer_label_); |
| 777 target_label->SetText(UTF8ToWide(utf8_text)); | 796 target_label->SetText(UTF8ToWide(utf8_text)); |
| 778 } | 797 } |
| 779 | 798 |
| 799 bool CandidateWindowView::ShouldUpdateCandidateViews( | |
| 800 const InputMethodLookupTable& previous_table, | |
| 801 const InputMethodLookupTable& current_table) { | |
| 802 // Check if most table contents are identical. | |
| 803 if (previous_table.page_size == current_table.page_size && | |
| 804 previous_table.orientation == current_table.orientation && | |
| 805 previous_table.candidates == current_table.candidates && | |
| 806 previous_table.labels == current_table.labels && | |
| 807 previous_table.annotations == current_table.annotations && | |
| 808 // Check if the page indexes are identical. | |
| 809 ComputePageIndex(previous_table) == ComputePageIndex(current_table)) { | |
| 810 // If all of the conditions are met, we don't have to update candidate | |
| 811 // views. | |
| 812 return false; | |
| 813 } | |
| 814 return true; | |
| 815 } | |
| 816 | |
| 780 void CandidateWindowView::UpdateCandidates( | 817 void CandidateWindowView::UpdateCandidates( |
| 781 const InputMethodLookupTable& lookup_table) { | 818 const InputMethodLookupTable& lookup_table) { |
| 782 // Initialize candidate views if necessary. | 819 const bool should_update = ShouldUpdateCandidateViews(lookup_table_, |
| 783 MaybeInitializeCandidateViews(lookup_table); | 820 lookup_table); |
| 821 // Updating the candidate views is expensive. We'll skip this if possible. | |
| 822 if (should_update) { | |
| 823 // Initialize candidate views if necessary. | |
| 824 MaybeInitializeCandidateViews(lookup_table); | |
| 784 | 825 |
| 785 // In MaybeInitializeCandidateViews(), | 826 // Compute the index of the current page. |
| 786 // |lookup_table| values and |lookup_table_| values are compared, | 827 if (lookup_table.page_size <= 0) { |
| 787 // so this substitution is needed after the function. | 828 LOG(ERROR) << "Invalid page size: " << lookup_table.page_size; |
| 829 return; | |
| 830 } | |
| 831 current_page_index_ = | |
| 832 lookup_table.cursor_absolute_index / lookup_table.page_size; | |
| 833 | |
| 834 // Update the candidates in the current page. | |
| 835 const size_t start_from = current_page_index_ * lookup_table.page_size; | |
| 836 | |
| 837 // In some cases, engines send empty shortcut labels. For instance, | |
| 838 // ibus-mozc sends empty labels when they show suggestions. In this | |
| 839 // case, we should not show shortcut labels. | |
| 840 const bool no_shortcut_mode = (start_from < lookup_table.labels.size() && | |
| 841 lookup_table.labels[start_from] == ""); | |
| 842 for (size_t i = 0; i < candidate_views_.size(); ++i) { | |
| 843 const size_t index_in_page = i; | |
| 844 const size_t candidate_index = start_from + index_in_page; | |
| 845 CandidateView* candidate_view = candidate_views_[index_in_page]; | |
| 846 // Set the shortcut text. | |
| 847 if (no_shortcut_mode) { | |
| 848 candidate_view->SetShortcutText(L""); | |
| 849 } else { | |
| 850 // At this moment, we don't use labels sent from engines for UX | |
| 851 // reasons. First, we want to show shortcut labels in empty rows | |
| 852 // (ex. show 6, 7, 8, ... in empty rows when the number of | |
| 853 // candidates is 5). Second, we want to add a period after each | |
| 854 // shortcut label when the candidate window is horizontal. | |
| 855 candidate_view->SetShortcutText( | |
| 856 CreateShortcutText(i, lookup_table.orientation)); | |
| 857 } | |
| 858 // Set the candidate text. | |
| 859 if (candidate_index < lookup_table.candidates.size() && | |
| 860 candidate_index < lookup_table.annotations.size()) { | |
| 861 candidate_view->SetCandidateText( | |
| 862 UTF8ToWide(lookup_table.candidates[candidate_index])); | |
| 863 candidate_view->SetAnnotationText( | |
| 864 UTF8ToWide(lookup_table.annotations[candidate_index])); | |
| 865 candidate_view->SetRowEnabled(true); | |
| 866 } else { | |
| 867 // Disable the empty row. | |
| 868 candidate_view->SetCandidateText(L""); | |
| 869 candidate_view->SetAnnotationText(L""); | |
| 870 candidate_view->SetRowEnabled(false); | |
| 871 } | |
| 872 } | |
| 873 } | |
|
satorux1
2010/12/01 09:25:32
Hmm, rietveld didn't do a good job for showing dif
| |
| 788 lookup_table_ = lookup_table; | 874 lookup_table_ = lookup_table; |
| 789 | 875 |
| 790 // Compute the index of the current page. | 876 // Select the current candidate in the page. |
| 791 current_page_index_ = | 877 const int current_candidate_in_page = |
| 792 lookup_table.cursor_absolute_index / lookup_table.page_size; | |
| 793 | |
| 794 // Update the candidates in the current page. | |
| 795 const size_t start_from = current_page_index_ * lookup_table.page_size; | |
| 796 | |
| 797 // In some cases, engines send empty shortcut labels. For instance, | |
| 798 // ibus-mozc sends empty labels when they show suggestions. In this | |
| 799 // case, we should not show shortcut labels. | |
| 800 const bool no_shortcut_mode = (start_from < lookup_table_.labels.size() && | |
| 801 lookup_table_.labels[start_from] == ""); | |
| 802 for (size_t i = 0; i < candidate_views_.size(); ++i) { | |
| 803 const size_t index_in_page = i; | |
| 804 const size_t candidate_index = start_from + index_in_page; | |
| 805 CandidateView* candidate_view = candidate_views_[index_in_page]; | |
| 806 // Set the shortcut text. | |
| 807 if (no_shortcut_mode) { | |
| 808 candidate_view->SetShortcutText(L""); | |
| 809 } else { | |
| 810 // At this moment, we don't use labels sent from engines for UX | |
| 811 // reasons. First, we want to show shortcut labels in empty rows | |
| 812 // (ex. show 6, 7, 8, ... in empty rows when the number of | |
| 813 // candidates is 5). Second, we want to add a period after each | |
| 814 // shortcut label when the candidate window is horizontal. | |
| 815 candidate_view->SetShortcutText( | |
| 816 CreateShortcutText(i, lookup_table_.orientation)); | |
| 817 } | |
| 818 // Set the candidate text. | |
| 819 if (candidate_index < lookup_table_.candidates.size() && | |
| 820 candidate_index < lookup_table_.annotations.size()) { | |
| 821 candidate_view->SetCandidateText( | |
| 822 UTF8ToWide(lookup_table_.candidates[candidate_index])); | |
| 823 candidate_view->SetAnnotationText( | |
| 824 UTF8ToWide(lookup_table_.annotations[candidate_index])); | |
| 825 candidate_view->SetRowEnabled(true); | |
| 826 } else { | |
| 827 // Disable the empty row. | |
| 828 candidate_view->SetCandidateText(L""); | |
| 829 candidate_view->SetAnnotationText(L""); | |
| 830 candidate_view->SetRowEnabled(false); | |
| 831 } | |
| 832 } | |
| 833 | |
| 834 // Select the first candidate candidate in the page. | |
| 835 const int first_candidate_in_page = | |
| 836 lookup_table.cursor_absolute_index % lookup_table.page_size; | 878 lookup_table.cursor_absolute_index % lookup_table.page_size; |
| 837 SelectCandidateAt(first_candidate_in_page); | 879 SelectCandidateAt(current_candidate_in_page); |
| 838 } | 880 } |
| 839 | 881 |
| 840 void CandidateWindowView::MaybeInitializeCandidateViews( | 882 void CandidateWindowView::MaybeInitializeCandidateViews( |
| 841 const InputMethodLookupTable& lookup_table) { | 883 const InputMethodLookupTable& lookup_table) { |
| 842 const InputMethodLookupTable::Orientation orientation = | 884 const InputMethodLookupTable::Orientation orientation = |
| 843 lookup_table.orientation; | 885 lookup_table.orientation; |
| 844 const int page_size = lookup_table.page_size; | 886 const int page_size = lookup_table.page_size; |
| 845 | 887 |
| 846 // Current column width. | 888 // Current column width. |
| 847 int shortcut_column_width = 0; | 889 int shortcut_column_width = 0; |
| 848 int candidate_column_width = 0; | 890 int candidate_column_width = 0; |
| 849 int annotation_column_width = 0; | 891 int annotation_column_width = 0; |
| 850 | 892 |
| 851 // If orientation is horizontal, don't need to compute width, | 893 // If orientation is horizontal, don't need to compute width, |
| 852 // because each label is left aligned. | 894 // because each label is left aligned. |
| 853 if (orientation == InputMethodLookupTable::kVertical) { | 895 if (orientation == InputMethodLookupTable::kVertical) { |
| 854 shortcut_column_width = ComputeShortcutColumnWidth(lookup_table); | 896 shortcut_column_width = ComputeShortcutColumnWidth(lookup_table); |
| 855 candidate_column_width = ComputeCandidateColumnWidth(lookup_table); | 897 candidate_column_width = ComputeCandidateColumnWidth(lookup_table); |
| 856 annotation_column_width = ComputeAnnotationColumnWidth(lookup_table); | 898 annotation_column_width = ComputeAnnotationColumnWidth(lookup_table); |
| 857 } | 899 } |
| 858 | 900 |
| 859 // If the requested number of views matches the number of current views, and | 901 // If the requested number of views matches the number of current views, and |
| 860 // previous and current column width are same, just reuse these. | 902 // previous and current column width are same, just reuse these. |
| 903 // | |
| 904 // Note that the early exit logic is not only useful for improving | |
| 905 // performance, but also necessary for the horizontal candidate window | |
| 906 // to be redrawn properly. If we get rid of the logic, the horizontal | |
| 907 // candidate window won't get redrawn properly for some reason when | |
| 908 // there is no size change. You can test this by removing "return" here | |
| 909 // and type "ni" with Pinyin input method. | |
| 861 if (static_cast<int>(candidate_views_.size()) == page_size && | 910 if (static_cast<int>(candidate_views_.size()) == page_size && |
| 862 lookup_table_.orientation == orientation && | 911 lookup_table_.orientation == orientation && |
| 863 previous_shortcut_column_width_ == shortcut_column_width && | 912 previous_shortcut_column_width_ == shortcut_column_width && |
| 864 previous_candidate_column_width_ == candidate_column_width && | 913 previous_candidate_column_width_ == candidate_column_width && |
| 865 previous_annotation_column_width_ == annotation_column_width) { | 914 previous_annotation_column_width_ == annotation_column_width) { |
| 866 return; | 915 return; |
| 867 } | 916 } |
| 868 | 917 |
| 869 // Update the previous column widths. | 918 // Update the previous column widths. |
| 870 previous_shortcut_column_width_ = shortcut_column_width; | 919 previous_shortcut_column_width_ = shortcut_column_width; |
| (...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1204 DLOG(INFO) << "Ignored set_cursor_location signal to prevent window shake"; | 1253 DLOG(INFO) << "Ignored set_cursor_location signal to prevent window shake"; |
| 1205 return; | 1254 return; |
| 1206 } | 1255 } |
| 1207 | 1256 |
| 1208 // Remember the cursor location. | 1257 // Remember the cursor location. |
| 1209 controller->set_cursor_location(gfx::Rect(x, y, width, height)); | 1258 controller->set_cursor_location(gfx::Rect(x, y, width, height)); |
| 1210 // Move the window per the cursor location. | 1259 // Move the window per the cursor location. |
| 1211 controller->MoveCandidateWindow( | 1260 controller->MoveCandidateWindow( |
| 1212 controller->cursor_location(), | 1261 controller->cursor_location(), |
| 1213 controller->candidate_window_->GetHorizontalOffset()); | 1262 controller->candidate_window_->GetHorizontalOffset()); |
| 1214 // The call is needed to ensure that the candidate window is redrawed | 1263 // The call is needed to ensure that the candidate window is redrawn |
| 1215 // properly after the cursor location is changed. | 1264 // properly after the cursor location is changed. |
| 1216 controller->candidate_window_->ResizeAndSchedulePaint(); | 1265 controller->candidate_window_->ResizeAndSchedulePaint(); |
| 1217 } | 1266 } |
| 1218 | 1267 |
| 1219 void CandidateWindowController::Impl::OnUpdateAuxiliaryText( | 1268 void CandidateWindowController::Impl::OnUpdateAuxiliaryText( |
| 1220 void* input_method_library, | 1269 void* input_method_library, |
| 1221 const std::string& utf8_text, | 1270 const std::string& utf8_text, |
| 1222 bool visible) { | 1271 bool visible) { |
| 1223 CandidateWindowController::Impl* controller = | 1272 CandidateWindowController::Impl* controller = |
| 1224 static_cast<CandidateWindowController::Impl*>(input_method_library); | 1273 static_cast<CandidateWindowController::Impl*>(input_method_library); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1281 | 1330 |
| 1282 CandidateWindowController::~CandidateWindowController() { | 1331 CandidateWindowController::~CandidateWindowController() { |
| 1283 delete impl_; | 1332 delete impl_; |
| 1284 } | 1333 } |
| 1285 | 1334 |
| 1286 bool CandidateWindowController::Init() { | 1335 bool CandidateWindowController::Init() { |
| 1287 return impl_->Init(); | 1336 return impl_->Init(); |
| 1288 } | 1337 } |
| 1289 | 1338 |
| 1290 } // namespace chromeos | 1339 } // namespace chromeos |
| OLD | NEW |