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.
4
5 #include "content/renderer/disambiguation_popup_helper.h"
6
7 #include "third_party/WebKit/public/platform/WebRect.h"
8 #include "ui/gfx/size_conversions.h"
9
10 using blink::WebRect;
11 using blink::WebVector;
12
13 namespace {
14
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;
18
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;
22
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;
29
30 // Compute the scaling factor to ensure the smallest touch candidate reaches
31 // a certain clickable size after zooming
FindOptimalScaleFactor(const WebVector<WebRect> & target_rects,float total_scale)32 float FindOptimalScaleFactor(const WebVector<WebRect>& target_rects,
33 float total_scale) {
34 using std::min;
35 using std::max;
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);
44 }
45 smallest_target = max(smallest_target, 1.0f);
46 return min(kDisambiguationPopupMaxScale, max(kDisambiguationPopupMinScale,
47 kDisambiguationPopupMinimumTouchSize / smallest_target)) * total_scale;
48 }
49
TrimEdges(int * e1,int * e2,int max_combined)50 void TrimEdges(int *e1, int *e2, int max_combined) {
51 if (*e1 + *e2 <= max_combined)
52 return;
53
54 if (std::min(*e1, *e2) * 2 >= max_combined)
55 *e1 = *e2 = max_combined / 2;
56 else if (*e1 > *e2)
57 *e1 = max_combined - *e2;
58 else
59 *e2 = max_combined - *e1;
60 }
61
62 // Ensure the disambiguation popup fits inside the screen,
63 // clip the edges farthest to the touch point if needed.
CropZoomArea(const gfx::Rect & zoom_rect,const gfx::Size & viewport_size,const gfx::Point & touch_point,float scale)64 gfx::Rect CropZoomArea(const gfx::Rect& zoom_rect,
65 const gfx::Size& viewport_size,
66 const gfx::Point& touch_point,
67 float scale) {
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));
72
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());
79
80 return gfx::Rect(touch_point.x() - left,
81 touch_point.y() - top,
82 left + right,
83 top + bottom);
84 }
85
86 } // namespace
87
88 namespace content {
89
ComputeZoomAreaAndScaleFactor(const gfx::Rect & tap_rect,const WebVector<WebRect> & target_rects,const gfx::Size & screen_size,const gfx::Size & visible_content_size,float total_scale,gfx::Rect * zoom_rect)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,
95 float total_scale,
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);
101
102 zoom_rect->Intersect(gfx::Rect(visible_content_size));
103
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);
108
109 return new_total_scale;
110 }
111
112 } // namespace content
113