Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / link_disambiguation / link_disambiguation_popup.cc
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 }