Home | History | Annotate | Download | only in renderer
      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