Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: chrome/browser/ui/autofill/autofill_popup_controller_impl.cc

Issue 11817051: Elide text in the new Autofill UI (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" 5 #include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h"
6 6
7 #include <algorithm>
8 #include <utility>
9
7 #include "base/logging.h" 10 #include "base/logging.h"
8 #include "base/utf_string_conversions.h" 11 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/ui/autofill/autofill_popup_delegate.h" 12 #include "chrome/browser/ui/autofill/autofill_popup_delegate.h"
10 #include "chrome/browser/ui/autofill/autofill_popup_view.h" 13 #include "chrome/browser/ui/autofill/autofill_popup_view.h"
11 #include "content/public/browser/native_web_keyboard_event.h" 14 #include "content/public/browser/native_web_keyboard_event.h"
12 #include "grit/webkit_resources.h" 15 #include "grit/webkit_resources.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" 16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h"
14 #include "ui/base/events/event.h" 17 #include "ui/base/events/event.h"
18 #include "ui/base/text/text_elider.h"
19 #include "ui/gfx/display.h"
20 #include "ui/gfx/screen.h"
21 #include "ui/gfx/vector2d.h"
15 22
16 using WebKit::WebAutofillClient; 23 using WebKit::WebAutofillClient;
17 24
18 namespace { 25 namespace {
19 26
20 // Used to indicate that no line is currently selected by the user. 27 // Used to indicate that no line is currently selected by the user.
21 const int kNoSelection = -1; 28 const int kNoSelection = -1;
22 29
23 // Size difference between name and subtext in pixels. 30 // Size difference between name and subtext in pixels.
24 const int kLabelFontSizeDelta = -2; 31 const int kLabelFontSizeDelta = -2;
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 const std::vector<string16>& icons, 118 const std::vector<string16>& icons,
112 const std::vector<int>& identifiers) { 119 const std::vector<int>& identifiers) {
113 names_ = names; 120 names_ = names;
114 subtexts_ = subtexts; 121 subtexts_ = subtexts;
115 icons_ = icons; 122 icons_ = icons;
116 identifiers_ = identifiers; 123 identifiers_ = identifiers;
117 124
118 #if !defined(OS_ANDROID) 125 #if !defined(OS_ANDROID)
119 // Android displays the long text with ellipsis using the view attributes. 126 // Android displays the long text with ellipsis using the view attributes.
120 127
121 // TODO(csharp): Fix crbug.com/156163 and use better logic when clipping. 128 UpdatePopupBounds();
129 int popup_width = popup_bounds().width();
130
131 // Elide the name and subtext strings so that the popup fits in the available
132 // space.
122 for (size_t i = 0; i < names_.size(); ++i) { 133 for (size_t i = 0; i < names_.size(); ++i) {
123 if (names_[i].length() > 15) 134 int name_width = name_font().GetStringWidth(names_[i]);
124 names_[i].erase(15); 135 int subtext_width = subtext_font().GetStringWidth(subtexts_[i]);
125 if (subtexts[i].length() > 15) 136 int total_text_length = name_width + subtext_width;
126 subtexts_[i].erase(15); 137
138 // The line can have no strings if it represents a UI element, such as
139 // a separator line.
140 if (total_text_length == 0)
141 continue;
142
143 int available_width = popup_width - RowWidthWithoutText(i);
144
145 // Each field recieves space in proportion to its length.
146 int name_size = available_width * name_width / total_text_length;
147 names_[i] = ui::ElideText(names_[i],
148 name_font(),
149 name_size,
150 ui::ELIDE_AT_END);
151
152 int subtext_size = available_width * subtext_width / total_text_length;
153 subtexts_[i] = ui::ElideText(subtexts_[i],
154 subtext_font(),
155 subtext_size,
156 ui::ELIDE_AT_END);
127 } 157 }
128 #endif 158 #endif
129 159
130 if (!view_) { 160 if (!view_) {
131 view_ = AutofillPopupView::Create(this); 161 view_ = AutofillPopupView::Create(this);
132 ShowView(); 162 ShowView();
133 } else { 163 } else {
134 UpdateBoundsAndRedrawPopup(); 164 UpdateBoundsAndRedrawPopup();
135 } 165 }
136 } 166 }
137 167
138 void AutofillPopupControllerImpl::Hide() { 168 void AutofillPopupControllerImpl::Hide() {
139 inform_delegate_of_destruction_ = false; 169 inform_delegate_of_destruction_ = false;
140 HideInternal(); 170 HideInternal();
141 } 171 }
142 172
143 void AutofillPopupControllerImpl::ViewDestroyed() { 173 void AutofillPopupControllerImpl::ViewDestroyed() {
144 delete this; 174 delete this;
145 } 175 }
146 176
147 void AutofillPopupControllerImpl::UpdateBoundsAndRedrawPopup() { 177 void AutofillPopupControllerImpl::UpdateBoundsAndRedrawPopup() {
148 #if !defined(OS_ANDROID) 178 #if !defined(OS_ANDROID)
149 popup_bounds_.set_width(GetPopupRequiredWidth()); 179 popup_bounds_.set_width(GetDesiredPopupWidth());
150 popup_bounds_.set_height(GetPopupRequiredHeight()); 180 popup_bounds_.set_height(GetDesiredPopupHeight());
Ilya Sherman 2013/01/15 22:29:55 This seems wrong. Wouldn't it cause the popup to
csharp 2013/01/16 20:56:58 yup, fixed.
151 #endif 181 #endif
152 182
153 view_->UpdateBoundsAndRedrawPopup(); 183 view_->UpdateBoundsAndRedrawPopup();
154 } 184 }
155 185
156 void AutofillPopupControllerImpl::MouseHovered(int x, int y) { 186 void AutofillPopupControllerImpl::MouseHovered(int x, int y) {
157 SetSelectedLine(LineFromY(y)); 187 SetSelectedLine(LineFromY(y));
158 188
159 bool delete_icon_hovered = DeleteIconIsUnder(x, y); 189 bool delete_icon_hovered = DeleteIconIsUnder(x, y);
160 if (delete_icon_hovered != delete_icon_hovered_) { 190 if (delete_icon_hovered != delete_icon_hovered_) {
(...skipping 22 matching lines...) Expand all
183 int AutofillPopupControllerImpl::GetIconResourceID( 213 int AutofillPopupControllerImpl::GetIconResourceID(
184 const string16& resource_name) { 214 const string16& resource_name) {
185 for (size_t i = 0; i < arraysize(kDataResources); ++i) { 215 for (size_t i = 0; i < arraysize(kDataResources); ++i) {
186 if (resource_name == ASCIIToUTF16(kDataResources[i].name)) 216 if (resource_name == ASCIIToUTF16(kDataResources[i].name))
187 return kDataResources[i].id; 217 return kDataResources[i].id;
188 } 218 }
189 219
190 return -1; 220 return -1;
191 } 221 }
192 222
193 bool AutofillPopupControllerImpl::CanDelete(size_t index) { 223 bool AutofillPopupControllerImpl::CanDelete(size_t index) const {
194 // TODO(isherman): AddressBook suggestions on Mac should not be drawn as 224 // TODO(isherman): AddressBook suggestions on Mac should not be drawn as
195 // deleteable. 225 // deleteable.
196 int id = identifiers_[index]; 226 int id = identifiers_[index];
197 return id > 0 || 227 return id > 0 ||
198 id == WebAutofillClient::MenuItemIDAutocompleteEntry || 228 id == WebAutofillClient::MenuItemIDAutocompleteEntry ||
199 id == WebAutofillClient::MenuItemIDPasswordEntry; 229 id == WebAutofillClient::MenuItemIDPasswordEntry;
200 } 230 }
201 231
202 #if !defined(OS_ANDROID)
203 int AutofillPopupControllerImpl::GetPopupRequiredWidth() {
204 if (name_font_.platform_font() == NULL ||
205 subtext_font_.platform_font() == NULL) {
206 // We can't calculate the size of the popup if the fonts
207 // aren't present.
208 return 0;
209 }
210
211 int popup_width = element_bounds().width();
212 DCHECK_EQ(names().size(), subtexts().size());
213 for (size_t i = 0; i < names().size(); ++i) {
214 int row_size = kEndPadding +
215 name_font_.GetStringWidth(names()[i]) +
216 kNamePadding +
217 subtext_font_.GetStringWidth(subtexts()[i]);
218
219 // Add the Autofill icon size, if required.
220 if (!icons()[i].empty())
221 row_size += kAutofillIconWidth + kIconPadding;
222
223 // Add delete icon, if required.
224 if (CanDelete(i))
225 row_size += kDeleteIconWidth + kIconPadding;
226
227 // Add the padding at the end
228 row_size += kEndPadding;
229
230 popup_width = std::max(popup_width, row_size);
231 }
232
233 return popup_width;
234 }
235
236 int AutofillPopupControllerImpl::GetPopupRequiredHeight() {
237 int popup_height = 0;
238
239 for (size_t i = 0; i < identifiers().size(); ++i) {
240 popup_height += GetRowHeightFromId(identifiers()[i]);
241 }
242
243 return popup_height;
244 }
245 #endif // !defined(OS_ANDROID)
246
247 gfx::Rect AutofillPopupControllerImpl::GetRowBounds(size_t index) { 232 gfx::Rect AutofillPopupControllerImpl::GetRowBounds(size_t index) {
248 int top = 0; 233 int top = 0;
249 for (size_t i = 0; i < index; ++i) { 234 for (size_t i = 0; i < index; ++i) {
250 top += GetRowHeightFromId(identifiers()[i]); 235 top += GetRowHeightFromId(identifiers()[i]);
251 } 236 }
252 237
253 return gfx::Rect( 238 return gfx::Rect(
254 0, 239 0,
255 top, 240 top,
256 popup_bounds_.width(), 241 popup_bounds_.width(),
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 current_height += GetRowHeightFromId(identifiers()[i]); 434 current_height += GetRowHeightFromId(identifiers()[i]);
450 435
451 if (y <= current_height) 436 if (y <= current_height)
452 return i; 437 return i;
453 } 438 }
454 439
455 // The y value goes beyond the popup so stop the selection at the last line. 440 // The y value goes beyond the popup so stop the selection at the last line.
456 return identifiers().size() - 1; 441 return identifiers().size() - 1;
457 } 442 }
458 443
459 int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) { 444 int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const {
460 if (identifier == WebAutofillClient::MenuItemIDSeparator) 445 if (identifier == WebAutofillClient::MenuItemIDSeparator)
461 return kSeparatorHeight; 446 return kSeparatorHeight;
462 447
463 return kRowHeight; 448 return kRowHeight;
464 } 449 }
465 450
466 bool AutofillPopupControllerImpl::DeleteIconIsUnder(int x, int y) { 451 bool AutofillPopupControllerImpl::DeleteIconIsUnder(int x, int y) {
467 #if defined(OS_ANDROID) 452 #if defined(OS_ANDROID)
468 return false; 453 return false;
469 #else 454 #else
470 if (!CanDelete(selected_line())) 455 if (!CanDelete(selected_line()))
471 return false; 456 return false;
472 457
473 int row_start_y = 0; 458 int row_start_y = 0;
474 for (int i = 0; i < selected_line(); ++i) { 459 for (int i = 0; i < selected_line(); ++i) {
475 row_start_y += GetRowHeightFromId(identifiers()[i]); 460 row_start_y += GetRowHeightFromId(identifiers()[i]);
476 } 461 }
477 462
478 gfx::Rect delete_icon_bounds = gfx::Rect( 463 gfx::Rect delete_icon_bounds = gfx::Rect(
479 GetPopupRequiredWidth() - kDeleteIconWidth - kIconPadding, 464 popup_bounds().width() - kDeleteIconWidth - kIconPadding,
480 row_start_y + ((kRowHeight - kDeleteIconHeight) / 2), 465 row_start_y + ((kRowHeight - kDeleteIconHeight) / 2),
481 kDeleteIconWidth, 466 kDeleteIconWidth,
482 kDeleteIconHeight); 467 kDeleteIconHeight);
483 468
484 return delete_icon_bounds.Contains(x, y); 469 return delete_icon_bounds.Contains(x, y);
485 #endif 470 #endif
486 } 471 }
487 472
488 bool AutofillPopupControllerImpl::CanAccept(int id) { 473 bool AutofillPopupControllerImpl::CanAccept(int id) {
489 return id != WebAutofillClient::MenuItemIDSeparator && 474 return id != WebAutofillClient::MenuItemIDSeparator &&
490 id != WebAutofillClient::MenuItemIDWarningMessage; 475 id != WebAutofillClient::MenuItemIDWarningMessage;
491 } 476 }
492 477
493 bool AutofillPopupControllerImpl::HasSuggestions() { 478 bool AutofillPopupControllerImpl::HasSuggestions() {
494 return identifiers_.size() != 0 && 479 return identifiers_.size() != 0 &&
495 (identifiers_[0] > 0 || 480 (identifiers_[0] > 0 ||
496 identifiers_[0] == 481 identifiers_[0] ==
497 WebAutofillClient::MenuItemIDAutocompleteEntry || 482 WebAutofillClient::MenuItemIDAutocompleteEntry ||
498 identifiers_[0] == WebAutofillClient::MenuItemIDPasswordEntry || 483 identifiers_[0] == WebAutofillClient::MenuItemIDPasswordEntry ||
499 identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry); 484 identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry);
500 } 485 }
501 486
502 void AutofillPopupControllerImpl::ShowView() { 487 void AutofillPopupControllerImpl::ShowView() {
503 view_->Show(); 488 view_->Show();
504 } 489 }
505 490
506 void AutofillPopupControllerImpl::InvalidateRow(size_t row) { 491 void AutofillPopupControllerImpl::InvalidateRow(size_t row) {
507 view_->InvalidateRow(row); 492 view_->InvalidateRow(row);
508 } 493 }
494
495 #if !defined(OS_ANDROID)
496 int AutofillPopupControllerImpl::GetDesiredPopupWidth() const {
497 if (!name_font_.platform_font() || !subtext_font_.platform_font()) {
498 // We can't calculate the size of the popup if the fonts
499 // aren't present.
500 return 0;
501 }
502
503 int popup_width = element_bounds().width();
504 DCHECK_EQ(names().size(), subtexts().size());
505 for (size_t i = 0; i < names().size(); ++i) {
506 int row_size = name_font_.GetStringWidth(names()[i]) +
507 subtext_font_.GetStringWidth(subtexts()[i]) +
508 RowWidthWithoutText(i);
509
510 popup_width = std::max(popup_width, row_size);
511 }
512
513 return popup_width;
514 }
515
516 int AutofillPopupControllerImpl::GetDesiredPopupHeight() const {
517 int popup_height = 0;
518
519 for (size_t i = 0; i < identifiers().size(); ++i) {
520 popup_height += GetRowHeightFromId(identifiers()[i]);
521 }
522
523 return popup_height;
524 }
525
526 int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const {
527 int row_size = kEndPadding + kNamePadding;
528
529 // Add the Autofill icon size, if required.
530 if (!icons_[row].empty())
531 row_size += kAutofillIconWidth + kIconPadding;
532
533 // Add the delete icon size, if required.
534 if (CanDelete(row))
535 row_size += kDeleteIconWidth + kIconPadding;
536
537 // Add the padding at the end
538 row_size += kEndPadding;
539
540 return row_size;
541 }
542
543 void AutofillPopupControllerImpl::UpdatePopupBounds() {
544 int popup_required_width = GetDesiredPopupWidth();
545 int popup_height = GetDesiredPopupHeight();
546 // This is the top left point of the popup if the popup is above the element
547 // and grows to the left (since that is the highest and furthest left the
548 // popup go could).
549 gfx::Point top_left_corner_of_popup = element_bounds().origin() +
550 gfx::Vector2d(element_bounds().width() - popup_required_width,
551 -popup_height);
552
553 // This is the bottom right point of the popup if the popup is below the
554 // element and grows to the right (since the is the lowest and furthest right
555 // the popup could go).
556 gfx::Point bottom_right_corner_of_popup = element_bounds().origin() +
557 gfx::Vector2d(popup_required_width,
558 element_bounds().height() + popup_height);
559
560 gfx::Display top_left_display = GetDisplayNearestPoint(
561 top_left_corner_of_popup);
562 gfx::Display bottom_right_display = GetDisplayNearestPoint(
563 bottom_right_corner_of_popup);
564
565 std::pair<int, int> popup_x_and_width = CalculatePopupXAndWidth(
566 top_left_display, bottom_right_display, popup_required_width);
567 std::pair<int, int> popup_y_and_height = CalculatePopupYAndHeight(
568 top_left_display, bottom_right_display, popup_height);
569
570 popup_bounds_ = gfx::Rect(popup_x_and_width.first,
571 popup_y_and_height.first,
572 popup_x_and_width.second,
573 popup_y_and_height.second);
574 }
575 #endif // !defined(OS_ANDROID)
576
577 gfx::Display AutofillPopupControllerImpl::GetDisplayNearestPoint(
578 const gfx::Point& point) const {
579 return gfx::Screen::GetScreenFor(container_view())->GetDisplayNearestPoint(
580 point);
581 }
582
583 std::pair<int, int> AutofillPopupControllerImpl::CalculatePopupXAndWidth(
584 const gfx::Display& left_display,
585 const gfx::Display& right_display,
586 int popup_required_width) const {
587 int leftmost_display_x = left_display.bounds().x() *
588 left_display.device_scale_factor();
589 int rightmost_display_x = right_display.GetSizeInPixel().width() +
590 right_display.bounds().x() * right_display.device_scale_factor();
591
592 // Calculate the leftmost and rightmost coordinates the popup can have without
593 // going off the screen.
594 int left_cutoff = std::max(element_bounds().origin().x(), leftmost_display_x);
595 int right_cutoff = std::min(element_bounds().top_right().x(),
596 rightmost_display_x);
597
598 int right_available = rightmost_display_x - left_cutoff;
599 int left_available = right_cutoff - leftmost_display_x;
600
601 int popup_width = std::min(popup_required_width,
602 std::max(right_available, left_available));
603
604 // If there is enough space for the popup on the right, show it there,
605 // otherwise choose the larger size.
606 if (right_available >= popup_width || right_available >= left_available)
607 return std::make_pair(left_cutoff, popup_width);
608 else
609 return std::make_pair(right_cutoff - popup_width, popup_width);
610 }
611
612 std::pair<int,int> AutofillPopupControllerImpl::CalculatePopupYAndHeight(
613 const gfx::Display& top_display,
614 const gfx::Display& bottom_display,
615 int popup_required_height) const {
616 // Find the correct top position of the popup so that it doesn't go off
617 // the screen.
618 int bottom_display_y = bottom_display.GetSizeInPixel().height() +
619 (bottom_display.bounds().y() *
620 bottom_display.device_scale_factor());
621 int bottom_of_field = element_bounds().bottom();
622
623 if (bottom_display_y < bottom_of_field + popup_required_height) {
624 // The popup must appear above the field.
625 return std::make_pair(element_bounds().y() - popup_required_height,
626 popup_required_height);
627 } else {
628 // The popup can appear below the field.
629 return std::make_pair(bottom_of_field, popup_required_height);
630 }
631 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698