summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/autofill/popup_controller_common.cc
blob: c686cb932201dbf768667437705ac530bf767216 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 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 <algorithm>
#include <utility>

#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<int, int> 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<int,int> 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<int, int> popup_x_and_width =
      CalculatePopupXAndWidth(top_left_display,
                              bottom_right_display,
                              desired_width);
  std::pair<int, int> 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