• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PopupControllerCommon(const gfx::RectF & element_bounds,const gfx::NativeView container_view,content::WebContents * web_contents)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) {}
~PopupControllerCommon()27 PopupControllerCommon::~PopupControllerCommon() {}
28 
SetKeyPressCallback(content::RenderWidgetHost::KeyPressEventCallback callback)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 
RegisterKeyPressCallback()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 
RemoveKeyPressCallback()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 
GetDisplayNearestPoint(const gfx::Point & point) const52 gfx::Display PopupControllerCommon::GetDisplayNearestPoint(
53     const gfx::Point& point) const {
54   return gfx::Screen::GetScreenFor(container_view_)->GetDisplayNearestPoint(
55       point);
56 }
57 
RoundedElementBounds() const58 const gfx::Rect PopupControllerCommon::RoundedElementBounds() const {
59   return gfx::ToEnclosingRect(element_bounds_);
60 }
61 
CalculatePopupXAndWidth(const gfx::Display & left_display,const gfx::Display & right_display,int popup_required_width) const62 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 
CalculatePopupYAndHeight(const gfx::Display & top_display,const gfx::Display & bottom_display,int popup_required_height) const93 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 
GetPopupBounds(int popup_required_height,int popup_required_width) const125 gfx::Rect PopupControllerCommon::GetPopupBounds(
126     int popup_required_height,
127     int popup_required_width) const {
128   // This is the top left point of the popup if the popup is above the element
129   // and grows to the left (since that is the highest and furthest left the
130   // popup go could).
131   gfx::Point top_left_corner_of_popup = RoundedElementBounds().origin() +
132       gfx::Vector2d(RoundedElementBounds().width() - popup_required_width,
133                     -popup_required_height);
134 
135   // This is the bottom right point of the popup if the popup is below the
136   // element and grows to the right (since the is the lowest and furthest right
137   // the popup could go).
138   gfx::Point bottom_right_corner_of_popup = RoundedElementBounds().origin() +
139       gfx::Vector2d(popup_required_width,
140                     RoundedElementBounds().height() + popup_required_height);
141 
142   gfx::Display top_left_display = GetDisplayNearestPoint(
143       top_left_corner_of_popup);
144   gfx::Display bottom_right_display = GetDisplayNearestPoint(
145       bottom_right_corner_of_popup);
146 
147   std::pair<int, int> popup_x_and_width =
148       CalculatePopupXAndWidth(top_left_display,
149                               bottom_right_display,
150                               popup_required_width);
151   std::pair<int, int> popup_y_and_height =
152       CalculatePopupYAndHeight(top_left_display,
153                                bottom_right_display,
154                                popup_required_height);
155 
156   return gfx::Rect(popup_x_and_width.first,
157                    popup_y_and_height.first,
158                    popup_x_and_width.second,
159                    popup_y_and_height.second);
160 }
161 
162 }  // namespace autofill
163