// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/autofill/popup_controller_common.h" #include #include #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "ui/gfx/display.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/screen.h" #include "ui/gfx/vector2d.h" namespace autofill { PopupControllerCommon::PopupControllerCommon( const gfx::RectF& element_bounds, const gfx::NativeView container_view, content::WebContents* web_contents) : element_bounds_(element_bounds), container_view_(container_view), web_contents_(web_contents), key_press_event_target_(NULL) {} PopupControllerCommon::~PopupControllerCommon() {} void PopupControllerCommon::SetKeyPressCallback( content::RenderWidgetHost::KeyPressEventCallback callback) { DCHECK(key_press_event_callback_.is_null()); key_press_event_callback_ = callback; } void PopupControllerCommon::RegisterKeyPressCallback() { if (web_contents_ && !key_press_event_target_) { key_press_event_target_ = web_contents_->GetRenderViewHost(); key_press_event_target_->AddKeyPressEventCallback( key_press_event_callback_); } } void PopupControllerCommon::RemoveKeyPressCallback() { if (web_contents_ && (!web_contents_->IsBeingDestroyed()) && key_press_event_target_ == web_contents_->GetRenderViewHost()) { web_contents_->GetRenderViewHost()->RemoveKeyPressEventCallback( key_press_event_callback_); } key_press_event_target_ = NULL; } gfx::Display PopupControllerCommon::GetDisplayNearestPoint( const gfx::Point& point) const { return gfx::Screen::GetScreenFor(container_view_)->GetDisplayNearestPoint( point); } const gfx::Rect PopupControllerCommon::RoundedElementBounds() const { return gfx::ToEnclosingRect(element_bounds_); } std::pair PopupControllerCommon::CalculatePopupXAndWidth( const gfx::Display& left_display, const gfx::Display& right_display, int popup_required_width) const { int leftmost_display_x = left_display.bounds().x(); int rightmost_display_x = right_display.GetSizeInPixel().width() + right_display.bounds().x(); // 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, RoundedElementBounds().x())); int left_growth_end = std::max(leftmost_display_x, std::min(rightmost_display_x, RoundedElementBounds().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 PopupControllerCommon::CalculatePopupYAndHeight( const gfx::Display& top_display, const gfx::Display& bottom_display, int popup_required_height) const { int topmost_display_y = top_display.bounds().y(); int bottommost_display_y = bottom_display.GetSizeInPixel().height() + bottom_display.bounds().y(); // 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, RoundedElementBounds().y())); int bottom_growth_start = std::max(topmost_display_y, std::min(bottommost_display_y, RoundedElementBounds().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); } } gfx::Rect PopupControllerCommon::GetPopupBounds(int desired_width, int desired_height) const { // 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 = RoundedElementBounds().origin() + gfx::Vector2d(RoundedElementBounds().width() - desired_width, -desired_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 = RoundedElementBounds().origin() + gfx::Vector2d(desired_width, RoundedElementBounds().height() + desired_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 popup_x_and_width = CalculatePopupXAndWidth(top_left_display, bottom_right_display, desired_width); std::pair popup_y_and_height = CalculatePopupYAndHeight(top_left_display, bottom_right_display, desired_height); return gfx::Rect(popup_x_and_width.first, popup_y_and_height.first, popup_x_and_width.second, popup_y_and_height.second); } } // namespace autofill