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

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 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 if (inform_delegate_of_destruction_) 111 if (inform_delegate_of_destruction_)
105 delegate_->ControllerDestroyed(); 112 delegate_->ControllerDestroyed();
106 } 113 }
107 114
108 void AutofillPopupControllerImpl::Show( 115 void AutofillPopupControllerImpl::Show(
109 const std::vector<string16>& names, 116 const std::vector<string16>& names,
110 const std::vector<string16>& subtexts, 117 const std::vector<string16>& subtexts,
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;
121 full_names_ = names;
114 subtexts_ = subtexts; 122 subtexts_ = subtexts;
115 icons_ = icons; 123 icons_ = icons;
116 identifiers_ = identifiers; 124 identifiers_ = identifiers;
117 125
118 #if !defined(OS_ANDROID) 126 #if !defined(OS_ANDROID)
119 // Android displays the long text with ellipsis using the view attributes. 127 // Android displays the long text with ellipsis using the view attributes.
120 128
121 // TODO(csharp): Fix crbug.com/156163 and use better logic when clipping. 129 UpdatePopupBounds();
130 int popup_width = popup_bounds().width();
131
132 // Elide the name and subtext strings so that the popup fits in the available
133 // space.
122 for (size_t i = 0; i < names_.size(); ++i) { 134 for (size_t i = 0; i < names_.size(); ++i) {
123 if (names_[i].length() > 15) 135 int name_width = name_font().GetStringWidth(names_[i]);
124 names_[i].erase(15); 136 int subtext_width = subtext_font().GetStringWidth(subtexts_[i]);
125 if (subtexts[i].length() > 15) 137 int total_text_length = name_width + subtext_width;
126 subtexts_[i].erase(15); 138
139 // The line can have no strings if it represents a UI element, such as
140 // a separator line.
141 if (total_text_length == 0)
142 continue;
143
144 int available_width = popup_width - RowWidthWithoutText(i);
145
146 // Each field recieves space in proportion to its length.
147 int name_size = available_width * name_width / total_text_length;
148 names_[i] = ui::ElideText(names_[i],
149 name_font(),
150 name_size,
151 ui::ELIDE_AT_END);
152
153 int subtext_size = available_width * subtext_width / total_text_length;
154 subtexts_[i] = ui::ElideText(subtexts_[i],
155 subtext_font(),
156 subtext_size,
157 ui::ELIDE_AT_END);
127 } 158 }
128 #endif 159 #endif
129 160
130 if (!view_) { 161 if (!view_) {
131 view_ = AutofillPopupView::Create(this); 162 view_ = AutofillPopupView::Create(this);
132 ShowView(); 163 ShowView();
133 } else { 164 } else {
134 UpdateBoundsAndRedrawPopup(); 165 UpdateBoundsAndRedrawPopup();
135 } 166 }
136 } 167 }
(...skipping 30 matching lines...) Expand all
167 return false; 198 return false;
168 } 199 }
169 } 200 }
170 201
171 void AutofillPopupControllerImpl::ViewDestroyed() { 202 void AutofillPopupControllerImpl::ViewDestroyed() {
172 delete this; 203 delete this;
173 } 204 }
174 205
175 void AutofillPopupControllerImpl::UpdateBoundsAndRedrawPopup() { 206 void AutofillPopupControllerImpl::UpdateBoundsAndRedrawPopup() {
176 #if !defined(OS_ANDROID) 207 #if !defined(OS_ANDROID)
177 popup_bounds_.set_width(GetPopupRequiredWidth()); 208 // TODO(csharp): Since UpdatePopupBounds can the position of the popup might
Ilya Sherman 2013/01/18 01:52:48 nit: "can the position" -> rephrase
csharp 2013/01/21 15:48:23 Done.
178 popup_bounds_.set_height(GetPopupRequiredHeight()); 209 // move as the values change (e.g. if the popup initially appeared above the
210 // element, if could end up moving to below the element).
211 UpdatePopupBounds();
179 #endif 212 #endif
180 213
181 view_->UpdateBoundsAndRedrawPopup(); 214 view_->UpdateBoundsAndRedrawPopup();
182 } 215 }
183 216
184 void AutofillPopupControllerImpl::MouseHovered(int x, int y) { 217 void AutofillPopupControllerImpl::MouseHovered(int x, int y) {
185 SetSelectedLine(LineFromY(y)); 218 SetSelectedLine(LineFromY(y));
186 219
187 bool delete_icon_hovered = DeleteIconIsUnder(x, y); 220 bool delete_icon_hovered = DeleteIconIsUnder(x, y);
188 if (delete_icon_hovered != delete_icon_hovered_) { 221 if (delete_icon_hovered != delete_icon_hovered_) {
189 delete_icon_hovered_ = delete_icon_hovered; 222 delete_icon_hovered_ = delete_icon_hovered;
190 InvalidateRow(selected_line()); 223 InvalidateRow(selected_line());
191 } 224 }
192 } 225 }
193 226
194 void AutofillPopupControllerImpl::MouseClicked(int x, int y) { 227 void AutofillPopupControllerImpl::MouseClicked(int x, int y) {
195 MouseHovered(x, y); 228 MouseHovered(x, y);
196 229
197 if (delete_icon_hovered_) 230 if (delete_icon_hovered_)
198 RemoveSelectedLine(); 231 RemoveSelectedLine();
199 else 232 else
200 AcceptSelectedLine(); 233 AcceptSelectedLine();
201 } 234 }
202 235
203 void AutofillPopupControllerImpl::MouseExitedPopup() { 236 void AutofillPopupControllerImpl::MouseExitedPopup() {
204 SetSelectedLine(kNoSelection); 237 SetSelectedLine(kNoSelection);
205 } 238 }
206 239
207 void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) { 240 void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) {
208 delegate_->DidAcceptSuggestion(names_[index], identifiers_[index]); 241 delegate_->DidAcceptSuggestion(full_names_[index], identifiers_[index]);
209 } 242 }
210 243
211 int AutofillPopupControllerImpl::GetIconResourceID( 244 int AutofillPopupControllerImpl::GetIconResourceID(
212 const string16& resource_name) { 245 const string16& resource_name) {
213 for (size_t i = 0; i < arraysize(kDataResources); ++i) { 246 for (size_t i = 0; i < arraysize(kDataResources); ++i) {
214 if (resource_name == ASCIIToUTF16(kDataResources[i].name)) 247 if (resource_name == ASCIIToUTF16(kDataResources[i].name))
215 return kDataResources[i].id; 248 return kDataResources[i].id;
216 } 249 }
217 250
218 return -1; 251 return -1;
219 } 252 }
220 253
221 bool AutofillPopupControllerImpl::CanDelete(size_t index) { 254 bool AutofillPopupControllerImpl::CanDelete(size_t index) const {
222 // TODO(isherman): AddressBook suggestions on Mac should not be drawn as 255 // TODO(isherman): AddressBook suggestions on Mac should not be drawn as
223 // deleteable. 256 // deleteable.
224 int id = identifiers_[index]; 257 int id = identifiers_[index];
225 return id > 0 || 258 return id > 0 ||
226 id == WebAutofillClient::MenuItemIDAutocompleteEntry || 259 id == WebAutofillClient::MenuItemIDAutocompleteEntry ||
227 id == WebAutofillClient::MenuItemIDPasswordEntry; 260 id == WebAutofillClient::MenuItemIDPasswordEntry;
228 } 261 }
229 262
230 #if !defined(OS_ANDROID)
231 int AutofillPopupControllerImpl::GetPopupRequiredWidth() {
232 if (name_font_.platform_font() == NULL ||
233 subtext_font_.platform_font() == NULL) {
234 // We can't calculate the size of the popup if the fonts
235 // aren't present.
236 return 0;
237 }
238
239 int popup_width = element_bounds().width();
240 DCHECK_EQ(names().size(), subtexts().size());
241 for (size_t i = 0; i < names().size(); ++i) {
242 int row_size = kEndPadding +
243 name_font_.GetStringWidth(names()[i]) +
244 kNamePadding +
245 subtext_font_.GetStringWidth(subtexts()[i]);
246
247 // Add the Autofill icon size, if required.
248 if (!icons()[i].empty())
249 row_size += kAutofillIconWidth + kIconPadding;
250
251 // Add delete icon, if required.
252 if (CanDelete(i))
253 row_size += kDeleteIconWidth + kIconPadding;
254
255 // Add the padding at the end
256 row_size += kEndPadding;
257
258 popup_width = std::max(popup_width, row_size);
259 }
260
261 return popup_width;
262 }
263
264 int AutofillPopupControllerImpl::GetPopupRequiredHeight() {
265 int popup_height = 0;
266
267 for (size_t i = 0; i < identifiers().size(); ++i) {
268 popup_height += GetRowHeightFromId(identifiers()[i]);
269 }
270
271 return popup_height;
272 }
273 #endif // !defined(OS_ANDROID)
274
275 gfx::Rect AutofillPopupControllerImpl::GetRowBounds(size_t index) { 263 gfx::Rect AutofillPopupControllerImpl::GetRowBounds(size_t index) {
276 int top = 0; 264 int top = 0;
277 for (size_t i = 0; i < index; ++i) { 265 for (size_t i = 0; i < index; ++i) {
278 top += GetRowHeightFromId(identifiers()[i]); 266 top += GetRowHeightFromId(identifiers()[i]);
279 } 267 }
280 268
281 return gfx::Rect( 269 return gfx::Rect(
282 0, 270 0,
283 top, 271 top,
284 popup_bounds_.width(), 272 popup_bounds_.width(),
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 bool AutofillPopupControllerImpl::RemoveSelectedLine() { 402 bool AutofillPopupControllerImpl::RemoveSelectedLine() {
415 if (selected_line_ == kNoSelection) 403 if (selected_line_ == kNoSelection)
416 return false; 404 return false;
417 405
418 DCHECK_GE(selected_line_, 0); 406 DCHECK_GE(selected_line_, 0);
419 DCHECK_LT(selected_line_, static_cast<int>(names_.size())); 407 DCHECK_LT(selected_line_, static_cast<int>(names_.size()));
420 408
421 if (!CanDelete(selected_line_)) 409 if (!CanDelete(selected_line_))
422 return false; 410 return false;
423 411
424 delegate_->RemoveSuggestion(names_[selected_line_], 412 delegate_->RemoveSuggestion(full_names_[selected_line_],
425 identifiers_[selected_line_]); 413 identifiers_[selected_line_]);
426 414
427 // Remove the deleted element. 415 // Remove the deleted element.
428 names_.erase(names_.begin() + selected_line_); 416 names_.erase(names_.begin() + selected_line_);
417 full_names_.erase(full_names_.begin() + selected_line_);
429 subtexts_.erase(subtexts_.begin() + selected_line_); 418 subtexts_.erase(subtexts_.begin() + selected_line_);
430 icons_.erase(icons_.begin() + selected_line_); 419 icons_.erase(icons_.begin() + selected_line_);
431 identifiers_.erase(identifiers_.begin() + selected_line_); 420 identifiers_.erase(identifiers_.begin() + selected_line_);
432 421
433 SetSelectedLine(kNoSelection); 422 SetSelectedLine(kNoSelection);
434 423
435 if (HasSuggestions()) { 424 if (HasSuggestions()) {
436 delegate_->ClearPreviewedForm(); 425 delegate_->ClearPreviewedForm();
437 UpdateBoundsAndRedrawPopup(); 426 UpdateBoundsAndRedrawPopup();
438 } else { 427 } else {
(...skipping 10 matching lines...) Expand all
449 current_height += GetRowHeightFromId(identifiers()[i]); 438 current_height += GetRowHeightFromId(identifiers()[i]);
450 439
451 if (y <= current_height) 440 if (y <= current_height)
452 return i; 441 return i;
453 } 442 }
454 443
455 // The y value goes beyond the popup so stop the selection at the last line. 444 // The y value goes beyond the popup so stop the selection at the last line.
456 return identifiers().size() - 1; 445 return identifiers().size() - 1;
457 } 446 }
458 447
459 int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) { 448 int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const {
460 if (identifier == WebAutofillClient::MenuItemIDSeparator) 449 if (identifier == WebAutofillClient::MenuItemIDSeparator)
461 return kSeparatorHeight; 450 return kSeparatorHeight;
462 451
463 return kRowHeight; 452 return kRowHeight;
464 } 453 }
465 454
466 bool AutofillPopupControllerImpl::DeleteIconIsUnder(int x, int y) { 455 bool AutofillPopupControllerImpl::DeleteIconIsUnder(int x, int y) {
467 #if defined(OS_ANDROID) 456 #if defined(OS_ANDROID)
468 return false; 457 return false;
469 #else 458 #else
470 if (!CanDelete(selected_line())) 459 if (!CanDelete(selected_line()))
471 return false; 460 return false;
472 461
473 int row_start_y = 0; 462 int row_start_y = 0;
474 for (int i = 0; i < selected_line(); ++i) { 463 for (int i = 0; i < selected_line(); ++i) {
475 row_start_y += GetRowHeightFromId(identifiers()[i]); 464 row_start_y += GetRowHeightFromId(identifiers()[i]);
476 } 465 }
477 466
478 gfx::Rect delete_icon_bounds = gfx::Rect( 467 gfx::Rect delete_icon_bounds = gfx::Rect(
479 GetPopupRequiredWidth() - kDeleteIconWidth - kIconPadding, 468 popup_bounds().width() - kDeleteIconWidth - kIconPadding,
480 row_start_y + ((kRowHeight - kDeleteIconHeight) / 2), 469 row_start_y + ((kRowHeight - kDeleteIconHeight) / 2),
481 kDeleteIconWidth, 470 kDeleteIconWidth,
482 kDeleteIconHeight); 471 kDeleteIconHeight);
483 472
484 return delete_icon_bounds.Contains(x, y); 473 return delete_icon_bounds.Contains(x, y);
485 #endif 474 #endif
486 } 475 }
487 476
488 bool AutofillPopupControllerImpl::CanAccept(int id) { 477 bool AutofillPopupControllerImpl::CanAccept(int id) {
489 return id != WebAutofillClient::MenuItemIDSeparator && 478 return id != WebAutofillClient::MenuItemIDSeparator &&
490 id != WebAutofillClient::MenuItemIDWarningMessage; 479 id != WebAutofillClient::MenuItemIDWarningMessage;
491 } 480 }
492 481
493 bool AutofillPopupControllerImpl::HasSuggestions() { 482 bool AutofillPopupControllerImpl::HasSuggestions() {
494 return identifiers_.size() != 0 && 483 return identifiers_.size() != 0 &&
495 (identifiers_[0] > 0 || 484 (identifiers_[0] > 0 ||
496 identifiers_[0] == 485 identifiers_[0] ==
497 WebAutofillClient::MenuItemIDAutocompleteEntry || 486 WebAutofillClient::MenuItemIDAutocompleteEntry ||
498 identifiers_[0] == WebAutofillClient::MenuItemIDPasswordEntry || 487 identifiers_[0] == WebAutofillClient::MenuItemIDPasswordEntry ||
499 identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry); 488 identifiers_[0] == WebAutofillClient::MenuItemIDDataListEntry);
500 } 489 }
501 490
502 void AutofillPopupControllerImpl::ShowView() { 491 void AutofillPopupControllerImpl::ShowView() {
503 view_->Show(); 492 view_->Show();
504 } 493 }
505 494
506 void AutofillPopupControllerImpl::InvalidateRow(size_t row) { 495 void AutofillPopupControllerImpl::InvalidateRow(size_t row) {
507 view_->InvalidateRow(row); 496 view_->InvalidateRow(row);
508 } 497 }
498
499 #if !defined(OS_ANDROID)
500 int AutofillPopupControllerImpl::GetDesiredPopupWidth() const {
501 if (!name_font_.platform_font() || !subtext_font_.platform_font()) {
502 // We can't calculate the size of the popup if the fonts
503 // aren't present.
504 return 0;
505 }
506
507 int popup_width = element_bounds().width();
508 DCHECK_EQ(names().size(), subtexts().size());
509 for (size_t i = 0; i < names().size(); ++i) {
510 int row_size = name_font_.GetStringWidth(names()[i]) +
511 subtext_font_.GetStringWidth(subtexts()[i]) +
512 RowWidthWithoutText(i);
513
514 popup_width = std::max(popup_width, row_size);
515 }
516
517 return popup_width;
518 }
519
520 int AutofillPopupControllerImpl::GetDesiredPopupHeight() const {
521 int popup_height = 0;
522
523 for (size_t i = 0; i < identifiers().size(); ++i) {
524 popup_height += GetRowHeightFromId(identifiers()[i]);
525 }
526
527 return popup_height;
528 }
529
530 int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const {
531 int row_size = kEndPadding + kNamePadding;
532
533 // Add the Autofill icon size, if required.
534 if (!icons_[row].empty())
535 row_size += kAutofillIconWidth + kIconPadding;
536
537 // Add the delete icon size, if required.
538 if (CanDelete(row))
539 row_size += kDeleteIconWidth + kIconPadding;
540
541 // Add the padding at the end
542 row_size += kEndPadding;
543
544 return row_size;
545 }
546
547 void AutofillPopupControllerImpl::UpdatePopupBounds() {
548 int popup_required_width = GetDesiredPopupWidth();
549 int popup_height = GetDesiredPopupHeight();
550 // This is the top left point of the popup if the popup is above the element
551 // and grows to the left (since that is the highest and furthest left the
552 // popup go could).
553 gfx::Point top_left_corner_of_popup = element_bounds().origin() +
554 gfx::Vector2d(element_bounds().width() - popup_required_width,
555 -popup_height);
556
557 // This is the bottom right point of the popup if the popup is below the
558 // element and grows to the right (since the is the lowest and furthest right
559 // the popup could go).
560 gfx::Point bottom_right_corner_of_popup = element_bounds().origin() +
561 gfx::Vector2d(popup_required_width,
562 element_bounds().height() + popup_height);
563
564 gfx::Display top_left_display = GetDisplayNearestPoint(
565 top_left_corner_of_popup);
566 gfx::Display bottom_right_display = GetDisplayNearestPoint(
567 bottom_right_corner_of_popup);
568
569 std::pair<int, int> popup_x_and_width = CalculatePopupXAndWidth(
570 top_left_display, bottom_right_display, popup_required_width);
571 std::pair<int, int> popup_y_and_height = CalculatePopupYAndHeight(
572 top_left_display, bottom_right_display, popup_height);
573
574 popup_bounds_ = gfx::Rect(popup_x_and_width.first,
575 popup_y_and_height.first,
576 popup_x_and_width.second,
577 popup_y_and_height.second);
578 }
579 #endif // !defined(OS_ANDROID)
580
581 gfx::Display AutofillPopupControllerImpl::GetDisplayNearestPoint(
582 const gfx::Point& point) const {
583 return gfx::Screen::GetScreenFor(container_view())->GetDisplayNearestPoint(
584 point);
585 }
586
587 std::pair<int, int> AutofillPopupControllerImpl::CalculatePopupXAndWidth(
588 const gfx::Display& left_display,
589 const gfx::Display& right_display,
590 int popup_required_width) const {
591 int leftmost_display_x = left_display.bounds().x() *
592 left_display.device_scale_factor();
593 int rightmost_display_x = right_display.GetSizeInPixel().width() +
594 right_display.bounds().x() * right_display.device_scale_factor();
595
596 // Calculate the start coordinates for the popup if it is growing right or
597 // the end position if it is growing to the left, capped to screen space.
598 int right_growth_start = std::max(leftmost_display_x,
599 std::min(rightmost_display_x,
600 element_bounds().x()));
601 int left_growth_end = std::max(leftmost_display_x,
602 std::min(rightmost_display_x,
603 element_bounds().right()));
604
605 int right_available = rightmost_display_x - right_growth_start;
606 int left_available = left_growth_end - leftmost_display_x;
607
608 int popup_width = std::min(popup_required_width,
609 std::max(right_available, left_available));
610
611 // If there is enough space for the popup on the right, show it there,
612 // otherwise choose the larger size.
613 if (right_available >= popup_width || right_available >= left_available)
614 return std::make_pair(right_growth_start, popup_width);
615 else
616 return std::make_pair(left_growth_end - popup_width, popup_width);
617 }
618
619 std::pair<int,int> AutofillPopupControllerImpl::CalculatePopupYAndHeight(
620 const gfx::Display& top_display,
621 const gfx::Display& bottom_display,
622 int popup_required_height) const {
623 int topmost_display_y = top_display.bounds().y() *
624 top_display.device_scale_factor();
625 int bottommost_display_y = bottom_display.GetSizeInPixel().height() +
626 (bottom_display.bounds().y() *
627 bottom_display.device_scale_factor());
628
629 // Calculate the start coordinates for the popup if it is growing down or
630 // the end position if it is growing up, capped to screen space.
631 int top_growth_end = std::max(topmost_display_y,
632 std::min(bottommost_display_y,
633 element_bounds().y()));
634 int bottom_growth_start = std::max(topmost_display_y,
635 std::min(bottommost_display_y,
636 element_bounds().bottom()));
637
638 int top_available = bottom_growth_start - topmost_display_y;
639 int bottom_available = bottommost_display_y - top_growth_end;
640
641 // TODO(csharp): Restrict the popup height to what is available.
642 if (bottom_available >= popup_required_height ||
643 bottom_available >= top_available) {
644 // The popup can appear below the field.
645 return std::make_pair(bottom_growth_start, popup_required_height);
646 } else {
647 // The popup must appear above the field.
648 return std::make_pair(top_growth_end - popup_required_height,
649 popup_required_height);
650 }
651 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698