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.
5 #include "chrome/browser/ui/views/link_disambiguation/link_disambiguation_popup.h"
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"
19 class LinkDisambiguationPopup::ZoomBubbleView
20 : public views::BubbleDelegateView
{
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
);
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
;
37 // WidgetObserver overrides
38 virtual void OnWidgetClosing(views::Widget
* widget
) override
;
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_
;
47 DISALLOW_COPY_AND_ASSIGN(ZoomBubbleView
);
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())),
61 gesture_cb_(gesture_cb
),
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
);
70 AddChildView(image_view
);
72 views::BubbleDelegateView::CreateBubble(this);
75 void LinkDisambiguationPopup::ZoomBubbleView::Close() {
80 gfx::Size
LinkDisambiguationPopup::ZoomBubbleView::GetPreferredSize() const {
81 return target_rect_
.size();
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
);
96 // If user completed a click we can close the window.
97 if (event
->type() == ui::EventType::ET_MOUSE_RELEASED
)
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()) {
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(),
128 gesture_cb_
.Run(&xform_event
);
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
)
137 void LinkDisambiguationPopup::ZoomBubbleView::OnWidgetClosing(
138 views::Widget
* widget
) {
139 popup_
->InvalidateBubbleView();
142 LinkDisambiguationPopup::LinkDisambiguationPopup()
147 LinkDisambiguationPopup::~LinkDisambiguationPopup() {
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
) {
159 view_
= new ZoomBubbleView(
161 gfx::Image::CreateFrom1xBitmap(zoomed_bitmap
).ToImageSkia(),
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();
189 void LinkDisambiguationPopup::Close() {
196 void LinkDisambiguationPopup::InvalidateBubbleView() {