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/Source/WebKit/chromium/public/platform/WebRect.h"
8 #include "ui/gfx/size_conversions.h"
10 using WebKit::WebRect
;
11 using WebKit::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
) {
35 if (!target_rects
.size()) // shall never reach
36 return kDisambiguationPopupMinScale
;
37 int smallest_target
= min(target_rects
[0].width
, target_rects
[0].height
);
38 for (size_t i
= 1; i
< target_rects
.size(); i
++) {
39 smallest_target
= min(smallest_target
, target_rects
[i
].width
);
40 smallest_target
= min(smallest_target
, target_rects
[i
].height
);
42 smallest_target
= max(smallest_target
, 1);
43 return min(kDisambiguationPopupMaxScale
, max(kDisambiguationPopupMinScale
,
44 static_cast<float>(kDisambiguationPopupMinimumTouchSize
)
48 void TrimEdges(int *e1
, int *e2
, int max_combined
) {
49 if (*e1
+ *e2
<= max_combined
)
52 if (std::min(*e1
, *e2
) * 2 >= max_combined
)
53 *e1
= *e2
= max_combined
/ 2;
55 *e1
= max_combined
- *e2
;
57 *e2
= max_combined
- *e1
;
60 // Ensure the disambiguation popup fits inside the screen,
61 // clip the edges farthest to the touch point if needed.
62 gfx::Rect
CropZoomArea(const gfx::Rect
& zoom_rect
,
63 const gfx::Size
& viewport_size
,
64 const gfx::Point
& touch_point
,
66 gfx::Size max_size
= viewport_size
;
67 max_size
.Enlarge(-2 * kDisambiguationPopupBoundsMargin
,
68 -2 * kDisambiguationPopupBoundsMargin
);
69 max_size
= ToCeiledSize(ScaleSize(max_size
, 1.0 / scale
));
71 int left
= touch_point
.x() - zoom_rect
.x();
72 int right
= zoom_rect
.right() - touch_point
.x();
73 int top
= touch_point
.y() - zoom_rect
.y();
74 int bottom
= zoom_rect
.bottom() - touch_point
.y();
75 TrimEdges(&left
, &right
, max_size
.width());
76 TrimEdges(&top
, &bottom
, max_size
.height());
78 return gfx::Rect(touch_point
.x() - left
,
79 touch_point
.y() - top
,
84 } // unnamed namespace
88 float DisambiguationPopupHelper::ComputeZoomAreaAndScaleFactor(
89 const gfx::Rect
& tap_rect
,
90 const WebVector
<WebRect
>& target_rects
,
91 const gfx::Size
& viewport_size
,
92 gfx::Rect
* zoom_rect
) {
93 *zoom_rect
= tap_rect
;
94 for (size_t i
= 0; i
< target_rects
.size(); i
++)
95 zoom_rect
->Union(gfx::Rect(target_rects
[i
]));
96 zoom_rect
->Inset(-kDisambiguationPopupPadding
, -kDisambiguationPopupPadding
);
97 zoom_rect
->Intersect(gfx::Rect(viewport_size
));
99 float scale
= FindOptimalScaleFactor(target_rects
);
100 *zoom_rect
= CropZoomArea(
101 *zoom_rect
, viewport_size
, tap_rect
.CenterPoint(), scale
);
106 } // namespace content