1 // Copyright (c) 2012 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.
5 #include "content/renderer/disambiguation_popup_helper.h"
7 #include "third_party/WebKit/public/platform/WebRect.h"
8 #include "ui/gfx/geometry/size_conversions.h"
11 using blink::WebVector
;
15 // The amount of padding to add to the disambiguation popup to show
16 // content around the possible elements, adding some context.
17 const int kDisambiguationPopupPadding
= 8;
19 // Constants used for fitting the disambiguation popup inside the bounds of
20 // the view. Note that there are mirror constants in PopupZoomer.java.
21 const int kDisambiguationPopupBoundsMargin
= 25;
23 // The smallest allowable touch target used for disambiguation popup.
24 // This value is used to determine the minimum amount we need to scale to
25 // make all targets touchable.
26 const int kDisambiguationPopupMinimumTouchSize
= 40;
27 const float kDisambiguationPopupMaxScale
= 5.0;
28 const float kDisambiguationPopupMinScale
= 2.0;
30 // Compute the scaling factor to ensure the smallest touch candidate reaches
31 // a certain clickable size after zooming
32 float FindOptimalScaleFactor(const WebVector
<WebRect
>& target_rects
,
36 if (!target_rects
.size()) // shall never reach
37 return kDisambiguationPopupMinScale
;
38 float smallest_target
= min(target_rects
[0].width
* total_scale
,
39 target_rects
[0].height
* total_scale
);
40 for (size_t i
= 1; i
< target_rects
.size(); i
++) {
41 smallest_target
= min(smallest_target
, target_rects
[i
].width
* total_scale
);
42 smallest_target
= min(smallest_target
,
43 target_rects
[i
].height
* total_scale
);
45 smallest_target
= max(smallest_target
, 1.0f
);
46 return min(kDisambiguationPopupMaxScale
, max(kDisambiguationPopupMinScale
,
47 kDisambiguationPopupMinimumTouchSize
/ smallest_target
)) * total_scale
;
50 void TrimEdges(int *e1
, int *e2
, int max_combined
) {
51 if (*e1
+ *e2
<= max_combined
)
54 if (std::min(*e1
, *e2
) * 2 >= max_combined
)
55 *e1
= *e2
= max_combined
/ 2;
57 *e1
= max_combined
- *e2
;
59 *e2
= max_combined
- *e1
;
62 // Ensure the disambiguation popup fits inside the screen,
63 // clip the edges farthest to the touch point if needed.
64 gfx::Rect
CropZoomArea(const gfx::Rect
& zoom_rect
,
65 const gfx::Size
& viewport_size
,
66 const gfx::Point
& touch_point
,
68 gfx::Size max_size
= viewport_size
;
69 max_size
.Enlarge(-2 * kDisambiguationPopupBoundsMargin
,
70 -2 * kDisambiguationPopupBoundsMargin
);
71 max_size
= ToCeiledSize(ScaleSize(max_size
, 1.0 / scale
));
73 int left
= touch_point
.x() - zoom_rect
.x();
74 int right
= zoom_rect
.right() - touch_point
.x();
75 int top
= touch_point
.y() - zoom_rect
.y();
76 int bottom
= zoom_rect
.bottom() - touch_point
.y();
77 TrimEdges(&left
, &right
, max_size
.width());
78 TrimEdges(&top
, &bottom
, max_size
.height());
80 return gfx::Rect(touch_point
.x() - left
,
81 touch_point
.y() - top
,
90 float DisambiguationPopupHelper::ComputeZoomAreaAndScaleFactor(
91 const gfx::Rect
& tap_rect
,
92 const WebVector
<WebRect
>& target_rects
,
93 const gfx::Size
& screen_size
,
94 const gfx::Size
& visible_content_size
,
96 gfx::Rect
* zoom_rect
) {
97 *zoom_rect
= tap_rect
;
98 for (size_t i
= 0; i
< target_rects
.size(); i
++)
99 zoom_rect
->Union(gfx::Rect(target_rects
[i
]));
100 zoom_rect
->Inset(-kDisambiguationPopupPadding
, -kDisambiguationPopupPadding
);
102 zoom_rect
->Intersect(gfx::Rect(visible_content_size
));
104 float new_total_scale
=
105 FindOptimalScaleFactor(target_rects
, total_scale
);
106 *zoom_rect
= CropZoomArea(
107 *zoom_rect
, screen_size
, tap_rect
.CenterPoint(), new_total_scale
);
109 return new_total_scale
;
112 } // namespace content