Index: chrome/browser/ui/autofill/autofill_popup_controller_impl.cc |
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc |
index 9c14c0b5111ecc32bb4dfa7248e7caa9a19759db..7e28e376c01d1f46981d08adedc6f8fedd9f4110 100644 |
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc |
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc |
@@ -4,6 +4,9 @@ |
#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" |
+#include <algorithm> |
+#include <utility> |
+ |
#include "base/logging.h" |
#include "base/utf_string_conversions.h" |
#include "chrome/browser/ui/autofill/autofill_popup_delegate.h" |
@@ -12,6 +15,10 @@ |
#include "grit/webkit_resources.h" |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" |
#include "ui/base/events/event.h" |
+#include "ui/base/text/text_elider.h" |
+#include "ui/gfx/display.h" |
+#include "ui/gfx/screen.h" |
+#include "ui/gfx/vector2d.h" |
using WebKit::WebAutofillClient; |
@@ -111,6 +118,7 @@ void AutofillPopupControllerImpl::Show( |
const std::vector<string16>& icons, |
const std::vector<int>& identifiers) { |
names_ = names; |
+ full_names_ = names; |
subtexts_ = subtexts; |
icons_ = icons; |
identifiers_ = identifiers; |
@@ -118,12 +126,35 @@ void AutofillPopupControllerImpl::Show( |
#if !defined(OS_ANDROID) |
// Android displays the long text with ellipsis using the view attributes. |
- // TODO(csharp): Fix crbug.com/156163 and use better logic when clipping. |
+ UpdatePopupBounds(); |
+ int popup_width = popup_bounds().width(); |
+ |
+ // Elide the name and subtext strings so that the popup fits in the available |
+ // space. |
for (size_t i = 0; i < names_.size(); ++i) { |
- if (names_[i].length() > 15) |
- names_[i].erase(15); |
- if (subtexts[i].length() > 15) |
- subtexts_[i].erase(15); |
+ int name_width = name_font().GetStringWidth(names_[i]); |
+ int subtext_width = subtext_font().GetStringWidth(subtexts_[i]); |
+ int total_text_length = name_width + subtext_width; |
+ |
+ // The line can have no strings if it represents a UI element, such as |
+ // a separator line. |
+ if (total_text_length == 0) |
+ continue; |
+ |
+ int available_width = popup_width - RowWidthWithoutText(i); |
+ |
+ // Each field recieves space in proportion to its length. |
+ int name_size = available_width * name_width / total_text_length; |
+ names_[i] = ui::ElideText(names_[i], |
+ name_font(), |
+ name_size, |
+ ui::ELIDE_AT_END); |
+ |
+ int subtext_size = available_width * subtext_width / total_text_length; |
+ subtexts_[i] = ui::ElideText(subtexts_[i], |
+ subtext_font(), |
+ subtext_size, |
+ ui::ELIDE_AT_END); |
} |
#endif |
@@ -174,8 +205,11 @@ void AutofillPopupControllerImpl::ViewDestroyed() { |
void AutofillPopupControllerImpl::UpdateBoundsAndRedrawPopup() { |
#if !defined(OS_ANDROID) |
- popup_bounds_.set_width(GetPopupRequiredWidth()); |
- popup_bounds_.set_height(GetPopupRequiredHeight()); |
+ // TODO(csharp): Since UpdatePopupBounds can change the position of the popup, |
+ // the popup could end up jumping from above the element to below it. |
+ // It is unclear if it is better to keep the popup where it was, or if it |
+ // should try and move to its desired position. |
+ UpdatePopupBounds(); |
#endif |
view_->UpdateBoundsAndRedrawPopup(); |
@@ -205,7 +239,7 @@ void AutofillPopupControllerImpl::MouseExitedPopup() { |
} |
void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) { |
- delegate_->DidAcceptSuggestion(names_[index], identifiers_[index]); |
+ delegate_->DidAcceptSuggestion(full_names_[index], identifiers_[index]); |
} |
int AutofillPopupControllerImpl::GetIconResourceID( |
@@ -218,7 +252,7 @@ int AutofillPopupControllerImpl::GetIconResourceID( |
return -1; |
} |
-bool AutofillPopupControllerImpl::CanDelete(size_t index) { |
+bool AutofillPopupControllerImpl::CanDelete(size_t index) const { |
// TODO(isherman): AddressBook suggestions on Mac should not be drawn as |
// deleteable. |
int id = identifiers_[index]; |
@@ -227,51 +261,6 @@ bool AutofillPopupControllerImpl::CanDelete(size_t index) { |
id == WebAutofillClient::MenuItemIDPasswordEntry; |
} |
-#if !defined(OS_ANDROID) |
-int AutofillPopupControllerImpl::GetPopupRequiredWidth() { |
- if (name_font_.platform_font() == NULL || |
- subtext_font_.platform_font() == NULL) { |
- // We can't calculate the size of the popup if the fonts |
- // aren't present. |
- return 0; |
- } |
- |
- int popup_width = element_bounds().width(); |
- DCHECK_EQ(names().size(), subtexts().size()); |
- for (size_t i = 0; i < names().size(); ++i) { |
- int row_size = kEndPadding + |
- name_font_.GetStringWidth(names()[i]) + |
- kNamePadding + |
- subtext_font_.GetStringWidth(subtexts()[i]); |
- |
- // Add the Autofill icon size, if required. |
- if (!icons()[i].empty()) |
- row_size += kAutofillIconWidth + kIconPadding; |
- |
- // Add delete icon, if required. |
- if (CanDelete(i)) |
- row_size += kDeleteIconWidth + kIconPadding; |
- |
- // Add the padding at the end |
- row_size += kEndPadding; |
- |
- popup_width = std::max(popup_width, row_size); |
- } |
- |
- return popup_width; |
-} |
- |
-int AutofillPopupControllerImpl::GetPopupRequiredHeight() { |
- int popup_height = 0; |
- |
- for (size_t i = 0; i < identifiers().size(); ++i) { |
- popup_height += GetRowHeightFromId(identifiers()[i]); |
- } |
- |
- return popup_height; |
-} |
-#endif // !defined(OS_ANDROID) |
- |
gfx::Rect AutofillPopupControllerImpl::GetRowBounds(size_t index) { |
int top = 0; |
for (size_t i = 0; i < index; ++i) { |
@@ -421,11 +410,12 @@ bool AutofillPopupControllerImpl::RemoveSelectedLine() { |
if (!CanDelete(selected_line_)) |
return false; |
- delegate_->RemoveSuggestion(names_[selected_line_], |
+ delegate_->RemoveSuggestion(full_names_[selected_line_], |
identifiers_[selected_line_]); |
// Remove the deleted element. |
names_.erase(names_.begin() + selected_line_); |
+ full_names_.erase(full_names_.begin() + selected_line_); |
subtexts_.erase(subtexts_.begin() + selected_line_); |
icons_.erase(icons_.begin() + selected_line_); |
identifiers_.erase(identifiers_.begin() + selected_line_); |
@@ -456,7 +446,7 @@ int AutofillPopupControllerImpl::LineFromY(int y) { |
return identifiers().size() - 1; |
} |
-int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) { |
+int AutofillPopupControllerImpl::GetRowHeightFromId(int identifier) const { |
if (identifier == WebAutofillClient::MenuItemIDSeparator) |
return kSeparatorHeight; |
@@ -476,7 +466,7 @@ bool AutofillPopupControllerImpl::DeleteIconIsUnder(int x, int y) { |
} |
gfx::Rect delete_icon_bounds = gfx::Rect( |
- GetPopupRequiredWidth() - kDeleteIconWidth - kIconPadding, |
+ popup_bounds().width() - kDeleteIconWidth - kIconPadding, |
row_start_y + ((kRowHeight - kDeleteIconHeight) / 2), |
kDeleteIconWidth, |
kDeleteIconHeight); |
@@ -506,3 +496,157 @@ void AutofillPopupControllerImpl::ShowView() { |
void AutofillPopupControllerImpl::InvalidateRow(size_t row) { |
view_->InvalidateRow(row); |
} |
+ |
+#if !defined(OS_ANDROID) |
+int AutofillPopupControllerImpl::GetDesiredPopupWidth() const { |
+ if (!name_font_.platform_font() || !subtext_font_.platform_font()) { |
+ // We can't calculate the size of the popup if the fonts |
+ // aren't present. |
+ return 0; |
+ } |
+ |
+ int popup_width = element_bounds().width(); |
+ DCHECK_EQ(names().size(), subtexts().size()); |
+ for (size_t i = 0; i < names().size(); ++i) { |
+ int row_size = name_font_.GetStringWidth(names()[i]) + |
+ subtext_font_.GetStringWidth(subtexts()[i]) + |
+ RowWidthWithoutText(i); |
+ |
+ popup_width = std::max(popup_width, row_size); |
+ } |
+ |
+ return popup_width; |
+} |
+ |
+int AutofillPopupControllerImpl::GetDesiredPopupHeight() const { |
+ int popup_height = 0; |
+ |
+ for (size_t i = 0; i < identifiers().size(); ++i) { |
+ popup_height += GetRowHeightFromId(identifiers()[i]); |
+ } |
+ |
+ return popup_height; |
+} |
+ |
+int AutofillPopupControllerImpl::RowWidthWithoutText(int row) const { |
+ int row_size = kEndPadding + kNamePadding; |
+ |
+ // Add the Autofill icon size, if required. |
+ if (!icons_[row].empty()) |
+ row_size += kAutofillIconWidth + kIconPadding; |
+ |
+ // Add the delete icon size, if required. |
+ if (CanDelete(row)) |
+ row_size += kDeleteIconWidth + kIconPadding; |
+ |
+ // Add the padding at the end |
+ row_size += kEndPadding; |
+ |
+ return row_size; |
+} |
+ |
+void AutofillPopupControllerImpl::UpdatePopupBounds() { |
+ int popup_required_width = GetDesiredPopupWidth(); |
+ int popup_height = GetDesiredPopupHeight(); |
+ // This is the top left point of the popup if the popup is above the element |
+ // and grows to the left (since that is the highest and furthest left the |
+ // popup go could). |
+ gfx::Point top_left_corner_of_popup = element_bounds().origin() + |
+ gfx::Vector2d(element_bounds().width() - popup_required_width, |
+ -popup_height); |
+ |
+ // This is the bottom right point of the popup if the popup is below the |
+ // element and grows to the right (since the is the lowest and furthest right |
+ // the popup could go). |
+ gfx::Point bottom_right_corner_of_popup = element_bounds().origin() + |
+ gfx::Vector2d(popup_required_width, |
+ element_bounds().height() + popup_height); |
+ |
+ gfx::Display top_left_display = GetDisplayNearestPoint( |
+ top_left_corner_of_popup); |
+ gfx::Display bottom_right_display = GetDisplayNearestPoint( |
+ bottom_right_corner_of_popup); |
+ |
+ std::pair<int, int> popup_x_and_width = CalculatePopupXAndWidth( |
+ top_left_display, bottom_right_display, popup_required_width); |
+ std::pair<int, int> popup_y_and_height = CalculatePopupYAndHeight( |
+ top_left_display, bottom_right_display, popup_height); |
+ |
+ popup_bounds_ = gfx::Rect(popup_x_and_width.first, |
+ popup_y_and_height.first, |
+ popup_x_and_width.second, |
+ popup_y_and_height.second); |
+} |
+#endif // !defined(OS_ANDROID) |
+ |
+gfx::Display AutofillPopupControllerImpl::GetDisplayNearestPoint( |
+ const gfx::Point& point) const { |
+ return gfx::Screen::GetScreenFor(container_view())->GetDisplayNearestPoint( |
+ point); |
+} |
+ |
+std::pair<int, int> AutofillPopupControllerImpl::CalculatePopupXAndWidth( |
+ const gfx::Display& left_display, |
+ const gfx::Display& right_display, |
+ int popup_required_width) const { |
+ int leftmost_display_x = left_display.bounds().x() * |
+ left_display.device_scale_factor(); |
+ int rightmost_display_x = right_display.GetSizeInPixel().width() + |
+ right_display.bounds().x() * right_display.device_scale_factor(); |
+ |
+ // Calculate the start coordinates for the popup if it is growing right or |
+ // the end position if it is growing to the left, capped to screen space. |
+ int right_growth_start = std::max(leftmost_display_x, |
+ std::min(rightmost_display_x, |
+ element_bounds().x())); |
+ int left_growth_end = std::max(leftmost_display_x, |
+ std::min(rightmost_display_x, |
+ element_bounds().right())); |
+ |
+ int right_available = rightmost_display_x - right_growth_start; |
+ int left_available = left_growth_end - leftmost_display_x; |
+ |
+ int popup_width = std::min(popup_required_width, |
+ std::max(right_available, left_available)); |
+ |
+ // If there is enough space for the popup on the right, show it there, |
+ // otherwise choose the larger size. |
+ if (right_available >= popup_width || right_available >= left_available) |
+ return std::make_pair(right_growth_start, popup_width); |
+ else |
+ return std::make_pair(left_growth_end - popup_width, popup_width); |
+} |
+ |
+std::pair<int,int> AutofillPopupControllerImpl::CalculatePopupYAndHeight( |
+ const gfx::Display& top_display, |
+ const gfx::Display& bottom_display, |
+ int popup_required_height) const { |
+ int topmost_display_y = top_display.bounds().y() * |
+ top_display.device_scale_factor(); |
+ int bottommost_display_y = bottom_display.GetSizeInPixel().height() + |
+ (bottom_display.bounds().y() * |
+ bottom_display.device_scale_factor()); |
+ |
+ // Calculate the start coordinates for the popup if it is growing down or |
+ // the end position if it is growing up, capped to screen space. |
+ int top_growth_end = std::max(topmost_display_y, |
+ std::min(bottommost_display_y, |
+ element_bounds().y())); |
+ int bottom_growth_start = std::max(topmost_display_y, |
+ std::min(bottommost_display_y, |
+ element_bounds().bottom())); |
+ |
+ int top_available = bottom_growth_start - topmost_display_y; |
+ int bottom_available = bottommost_display_y - top_growth_end; |
+ |
+ // TODO(csharp): Restrict the popup height to what is available. |
+ if (bottom_available >= popup_required_height || |
+ bottom_available >= top_available) { |
+ // The popup can appear below the field. |
+ return std::make_pair(bottom_growth_start, popup_required_height); |
+ } else { |
+ // The popup must appear above the field. |
+ return std::make_pair(top_growth_end - popup_required_height, |
+ popup_required_height); |
+ } |
+} |