Home | History | Annotate | Download | only in link_disambiguation
      1 // Copyright (c) 2014 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 "chrome/browser/ui/views/link_disambiguation/link_disambiguation_popup.h"
      6 
      7 #include "ui/aura/client/screen_position_client.h"
      8 #include "ui/events/event.h"
      9 #include "ui/events/event_processor.h"
     10 #include "ui/events/event_utils.h"
     11 #include "ui/events/gesture_event_details.h"
     12 #include "ui/gfx/display.h"
     13 #include "ui/gfx/image/image.h"
     14 #include "ui/gfx/image/image_skia.h"
     15 #include "ui/gfx/screen.h"
     16 #include "ui/views/bubble/bubble_delegate.h"
     17 #include "ui/views/controls/image_view.h"
     18 
     19 class LinkDisambiguationPopup::ZoomBubbleView
     20     : public views::BubbleDelegateView {
     21  public:
     22   ZoomBubbleView(const gfx::Rect& target_rect,
     23                  const gfx::ImageSkia* zoomed_skia_image,
     24                  const aura::Window* content,
     25                  LinkDisambiguationPopup* popup,
     26                  const base::Callback<void(ui::GestureEvent*)>& gesture_cb,
     27                  const base::Callback<void(ui::MouseEvent*)>& mouse_cb);
     28 
     29   void Close();
     30 
     31  private:
     32   // views::View overrides
     33   virtual gfx::Size GetPreferredSize() const OVERRIDE;
     34   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
     35   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
     36 
     37   // WidgetObserver overrides
     38   virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE;
     39 
     40   const float scale_;
     41   const aura::Window* content_;
     42   const base::Callback<void(ui::GestureEvent*)> gesture_cb_;
     43   const base::Callback<void(ui::MouseEvent*)> mouse_cb_;
     44   LinkDisambiguationPopup* popup_;
     45   const gfx::Rect target_rect_;
     46 
     47   DISALLOW_COPY_AND_ASSIGN(ZoomBubbleView);
     48 };
     49 
     50 LinkDisambiguationPopup::ZoomBubbleView::ZoomBubbleView(
     51     const gfx::Rect& target_rect,
     52     const gfx::ImageSkia* zoomed_skia_image,
     53     const aura::Window* content,
     54     LinkDisambiguationPopup* popup,
     55     const base::Callback<void(ui::GestureEvent*)>& gesture_cb,
     56     const base::Callback<void(ui::MouseEvent*)>& mouse_cb)
     57     : BubbleDelegateView(NULL, views::BubbleBorder::FLOAT),
     58       scale_(static_cast<float>(zoomed_skia_image->width()) /
     59           static_cast<float>(target_rect.width())),
     60       content_(content),
     61       gesture_cb_(gesture_cb),
     62       mouse_cb_(mouse_cb),
     63       popup_(popup),
     64       target_rect_(target_rect) {
     65   views::ImageView* image_view = new views::ImageView();
     66   image_view->SetBounds(
     67       0, 0, zoomed_skia_image->width(), zoomed_skia_image->height());
     68   image_view->SetImage(zoomed_skia_image);
     69 
     70   AddChildView(image_view);
     71 
     72   views::BubbleDelegateView::CreateBubble(this);
     73 }
     74 
     75 void LinkDisambiguationPopup::ZoomBubbleView::Close() {
     76   if (GetWidget())
     77     GetWidget()->Close();
     78 }
     79 
     80 gfx::Size LinkDisambiguationPopup::ZoomBubbleView::GetPreferredSize() const {
     81   return target_rect_.size();
     82 }
     83 
     84 void LinkDisambiguationPopup::ZoomBubbleView::OnMouseEvent(
     85     ui::MouseEvent* event) {
     86   // Transform mouse event back to coordinate system of the web content window
     87   // before providing to the callback.
     88   gfx::PointF xform_location(
     89       (event->location().x() / scale_) + target_rect_.x(),
     90       (event->location().y() / scale_) + target_rect_.y());
     91   ui::MouseEvent xform_event(event->type(), xform_location, xform_location,
     92       event->flags(), event->changed_button_flags());
     93   mouse_cb_.Run(&xform_event);
     94   event->SetHandled();
     95 
     96   // If user completed a click we can close the window.
     97   if (event->type() == ui::EventType::ET_MOUSE_RELEASED)
     98     Close();
     99 }
    100 
    101 void LinkDisambiguationPopup::ZoomBubbleView::OnGestureEvent(
    102     ui::GestureEvent* event) {
    103   // If we receive gesture events that are outside of our bounds we close
    104   // ourselves, as perhaps the user has decided on a different part of the page.
    105   if (event->location().x() > bounds().width() ||
    106       event->location().y() > bounds().height()) {
    107     Close();
    108     return;
    109   }
    110 
    111   // Scale the gesture event back to the size of the original |target_rect_|,
    112   // and then offset it to be relative to that |target_rect_| before sending
    113   // it back to the callback.
    114   gfx::PointF xform_location(
    115       (event->location().x() / scale_) + target_rect_.x(),
    116       (event->location().y() / scale_) + target_rect_.y());
    117   ui::GestureEventDetails xform_details(event->details());
    118   xform_details.set_bounding_box(gfx::RectF(
    119       (event->details().bounding_box().x() / scale_) + target_rect_.x(),
    120       (event->details().bounding_box().y() / scale_) + target_rect_.y(),
    121       event->details().bounding_box().width() / scale_,
    122       event->details().bounding_box().height() / scale_));
    123   ui::GestureEvent xform_event(xform_location.x(),
    124                                xform_location.y(),
    125                                event->flags(),
    126                                event->time_stamp(),
    127                                xform_details);
    128   gesture_cb_.Run(&xform_event);
    129   event->SetHandled();
    130 
    131   // If we completed a tap we close ourselves, as the web content will navigate
    132   // if the user hit a link.
    133   if (event->type() == ui::EventType::ET_GESTURE_TAP)
    134     Close();
    135 }
    136 
    137 void LinkDisambiguationPopup::ZoomBubbleView::OnWidgetClosing(
    138     views::Widget* widget) {
    139   popup_->InvalidateBubbleView();
    140 }
    141 
    142 LinkDisambiguationPopup::LinkDisambiguationPopup()
    143     : content_(NULL),
    144       view_(NULL) {
    145 }
    146 
    147 LinkDisambiguationPopup::~LinkDisambiguationPopup() {
    148   Close();
    149 }
    150 
    151 void LinkDisambiguationPopup::Show(
    152     const SkBitmap& zoomed_bitmap,
    153     const gfx::Rect& target_rect,
    154     const gfx::NativeView content,
    155     const base::Callback<void(ui::GestureEvent*)>& gesture_cb,
    156     const base::Callback<void(ui::MouseEvent*)>& mouse_cb) {
    157   content_ = content;
    158 
    159   view_ = new ZoomBubbleView(
    160       target_rect,
    161       gfx::Image::CreateFrom1xBitmap(zoomed_bitmap).ToImageSkia(),
    162       content_,
    163       this,
    164       gesture_cb,
    165       mouse_cb);
    166 
    167   // Center the zoomed bubble over the target rectangle, constrained to the
    168   // work area in the current display. Since |target_rect| is provided in
    169   // |content_| coordinate system, we must convert it into Screen coordinates
    170   // for correct window positioning.
    171   aura::client::ScreenPositionClient* screen_position_client =
    172       aura::client::GetScreenPositionClient(content_->GetRootWindow());
    173   gfx::Point target_screen(target_rect.x() + (target_rect.width() / 2),
    174       target_rect.y() + (target_rect.height() / 2));
    175   if (screen_position_client)
    176     screen_position_client->ConvertPointToScreen(content_, &target_screen);
    177   gfx::Rect window_bounds(
    178       target_screen.x() - (zoomed_bitmap.width() / 2),
    179       target_screen.y() - (zoomed_bitmap.height() / 2),
    180       zoomed_bitmap.width(),
    181       zoomed_bitmap.height());
    182   const gfx::Display display =
    183       gfx::Screen::GetScreenFor(content)->GetDisplayNearestWindow(content);
    184   window_bounds.AdjustToFit(display.work_area());
    185   view_->GetWidget()->SetBounds(window_bounds);
    186   view_->GetWidget()->Show();
    187 }
    188 
    189 void LinkDisambiguationPopup::Close() {
    190   if (view_) {
    191     view_->Close();
    192     view_ = NULL;
    193   }
    194 }
    195 
    196 void LinkDisambiguationPopup::InvalidateBubbleView() {
    197   view_ = NULL;
    198 }
    199