Home | History | Annotate | Download | only in autofill
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/autofill/popup_controller_common.h"
      6 
      7 #include <algorithm>
      8 #include <utility>
      9 
     10 #include "content/public/browser/render_view_host.h"
     11 #include "content/public/browser/web_contents.h"
     12 #include "ui/gfx/display.h"
     13 #include "ui/gfx/rect_conversions.h"
     14 #include "ui/gfx/screen.h"
     15 #include "ui/gfx/vector2d.h"
     16 
     17 namespace autofill {
     18 
     19 PopupControllerCommon::PopupControllerCommon(
     20     const gfx::RectF& element_bounds,
     21     const gfx::NativeView container_view,
     22     content::WebContents* web_contents)
     23     : element_bounds_(element_bounds),
     24       container_view_(container_view),
     25       web_contents_(web_contents),
     26       key_press_event_target_(NULL) {}
     27 PopupControllerCommon::~PopupControllerCommon() {}
     28 
     29 void PopupControllerCommon::SetKeyPressCallback(
     30     content::RenderWidgetHost::KeyPressEventCallback callback) {
     31   DCHECK(key_press_event_callback_.is_null());
     32   key_press_event_callback_ = callback;
     33 }
     34 
     35 void PopupControllerCommon::RegisterKeyPressCallback() {
     36   if (web_contents_ && !key_press_event_target_) {
     37     key_press_event_target_ = web_contents_->GetRenderViewHost();
     38     key_press_event_target_->AddKeyPressEventCallback(
     39         key_press_event_callback_);
     40   }
     41 }
     42 
     43 void PopupControllerCommon::RemoveKeyPressCallback() {
     44   if (web_contents_ && (!web_contents_->IsBeingDestroyed()) &&
     45       key_press_event_target_ == web_contents_->GetRenderViewHost()) {
     46     web_contents_->GetRenderViewHost()->RemoveKeyPressEventCallback(
     47         key_press_event_callback_);
     48   }
     49   key_press_event_target_ = NULL;
     50 }
     51 
     52 gfx::Display PopupControllerCommon::GetDisplayNearestPoint(
     53     const gfx::Point& point) const {
     54   return gfx::Screen::GetScreenFor(container_view_)->GetDisplayNearestPoint(
     55       point);
     56 }
     57 
     58 const gfx::Rect PopupControllerCommon::RoundedElementBounds() const {
     59   return gfx::ToEnclosingRect(element_bounds_);
     60 }
     61 
     62 std::pair<int, int> PopupControllerCommon::CalculatePopupXAndWidth(
     63     const gfx::Display& left_display,
     64     const gfx::Display& right_display,
     65     int popup_required_width) const {
     66   int leftmost_display_x = left_display.bounds().x();
     67   int rightmost_display_x =
     68       right_display.GetSizeInPixel().width() + right_display.bounds().x();
     69 
     70   // Calculate the start coordinates for the popup if it is growing right or
     71   // the end position if it is growing to the left, capped to screen space.
     72   int right_growth_start = std::max(leftmost_display_x,
     73                                     std::min(rightmost_display_x,
     74                                              RoundedElementBounds().x()));
     75   int left_growth_end = std::max(leftmost_display_x,
     76                                  std::min(rightmost_display_x,
     77                                           RoundedElementBounds().right()));
     78 
     79   int right_available = rightmost_display_x - right_growth_start;
     80   int left_available = left_growth_end - leftmost_display_x;
     81 
     82   int popup_width = std::min(popup_required_width,
     83                              std::max(right_available, left_available));
     84 
     85   // If there is enough space for the popup on the right, show it there,
     86   // otherwise choose the larger size.
     87   if (right_available >= popup_width || right_available >= left_available)
     88     return std::make_pair(right_growth_start, popup_width);
     89   else
     90     return std::make_pair(left_growth_end - popup_width, popup_width);
     91 }
     92 
     93 std::pair<int,int> PopupControllerCommon::CalculatePopupYAndHeight(
     94     const gfx::Display& top_display,
     95     const gfx::Display& bottom_display,
     96     int popup_required_height) const {
     97   int topmost_display_y = top_display.bounds().y();
     98   int bottommost_display_y =
     99       bottom_display.GetSizeInPixel().height() + bottom_display.bounds().y();
    100 
    101   // Calculate the start coordinates for the popup if it is growing down or
    102   // the end position if it is growing up, capped to screen space.
    103   int top_growth_end = std::max(topmost_display_y,
    104                                 std::min(bottommost_display_y,
    105                                          RoundedElementBounds().y()));
    106   int bottom_growth_start = std::max(topmost_display_y,
    107                                      std::min(bottommost_display_y,
    108                                               RoundedElementBounds().bottom()));
    109 
    110   int top_available = bottom_growth_start - topmost_display_y;
    111   int bottom_available = bottommost_display_y - top_growth_end;
    112 
    113   // TODO(csharp): Restrict the popup height to what is available.
    114   if (bottom_available >= popup_required_height ||
    115       bottom_available >= top_available) {
    116     // The popup can appear below the field.
    117     return std::make_pair(bottom_growth_start, popup_required_height);
    118   } else {
    119     // The popup must appear above the field.
    120     return std::make_pair(top_growth_end - popup_required_height,
    121                           popup_required_height);
    122   }
    123 }
    124 
    125 gfx::Rect PopupControllerCommon::GetPopupBounds(int desired_width,
    126                                                 int desired_height) const {
    127   // This is the top left point of the popup if the popup is above the element
    128   // and grows to the left (since that is the highest and furthest left the
    129   // popup go could).
    130   gfx::Point top_left_corner_of_popup = RoundedElementBounds().origin() +
    131       gfx::Vector2d(RoundedElementBounds().width() - desired_width,
    132                     -desired_height);
    133 
    134   // This is the bottom right point of the popup if the popup is below the
    135   // element and grows to the right (since the is the lowest and furthest right
    136   // the popup could go).
    137   gfx::Point bottom_right_corner_of_popup = RoundedElementBounds().origin() +
    138       gfx::Vector2d(desired_width,
    139                     RoundedElementBounds().height() + desired_height);
    140 
    141   gfx::Display top_left_display = GetDisplayNearestPoint(
    142       top_left_corner_of_popup);
    143   gfx::Display bottom_right_display = GetDisplayNearestPoint(
    144       bottom_right_corner_of_popup);
    145 
    146   std::pair<int, int> popup_x_and_width =
    147       CalculatePopupXAndWidth(top_left_display,
    148                               bottom_right_display,
    149                               desired_width);
    150   std::pair<int, int> popup_y_and_height =
    151       CalculatePopupYAndHeight(top_left_display,
    152                                bottom_right_display,
    153                                desired_height);
    154 
    155   return gfx::Rect(popup_x_and_width.first,
    156                    popup_y_and_height.first,
    157                    popup_x_and_width.second,
    158                    popup_y_and_height.second);
    159 }
    160 
    161 }  // namespace autofill
    162