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 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 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. 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 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