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(views::Widget
* top_level_widget
,
23 const gfx::Rect
& target_rect
,
24 const gfx::ImageSkia
* zoomed_skia_image
,
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 gfx::Size
GetPreferredSize() const override
;
34 void OnGestureEvent(ui::GestureEvent
* event
) override
;
35 void OnMouseEvent(ui::MouseEvent
* event
) override
;
37 // WidgetObserver overrides
38 void OnWidgetClosing(views::Widget
* widget
) override
;
41 const base::Callback
<void(ui::GestureEvent
*)> gesture_cb_
;
42 const base::Callback
<void(ui::MouseEvent
*)> mouse_cb_
;
43 LinkDisambiguationPopup
* popup_
;
44 const gfx::Rect target_rect_
;
46 DISALLOW_COPY_AND_ASSIGN(ZoomBubbleView
);
49 LinkDisambiguationPopup::ZoomBubbleView::ZoomBubbleView(
50 views::Widget
* top_level_widget
,
51 const gfx::Rect
& target_rect
,
52 const gfx::ImageSkia
* zoomed_skia_image
,
53 LinkDisambiguationPopup
* popup
,
54 const base::Callback
<void(ui::GestureEvent
*)>& gesture_cb
,
55 const base::Callback
<void(ui::MouseEvent
*)>& mouse_cb
)
57 top_level_widget
? top_level_widget
->GetContentsView() : nullptr,
58 views::BubbleBorder::FLOAT
),
59 scale_(static_cast<float>(zoomed_skia_image
->width()) /
60 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 ui::EventTimeForNow(), event
->flags(),
93 event
->changed_button_flags());
94 mouse_cb_
.Run(&xform_event
);
97 // If user completed a click we can close the window.
98 if (event
->type() == ui::EventType::ET_MOUSE_RELEASED
)
102 void LinkDisambiguationPopup::ZoomBubbleView::OnGestureEvent(
103 ui::GestureEvent
* event
) {
104 // If we receive gesture events that are outside of our bounds we close
105 // ourselves, as perhaps the user has decided on a different part of the page.
106 if (event
->location().x() > bounds().width() ||
107 event
->location().y() > bounds().height()) {
112 // Scale the gesture event back to the size of the original |target_rect_|,
113 // and then offset it to be relative to that |target_rect_| before sending
114 // it back to the callback.
115 gfx::PointF
xform_location(
116 (event
->location().x() / scale_
) + target_rect_
.x(),
117 (event
->location().y() / scale_
) + target_rect_
.y());
118 ui::GestureEventDetails
xform_details(event
->details());
119 xform_details
.set_bounding_box(gfx::RectF(
120 (event
->details().bounding_box().x() / scale_
) + target_rect_
.x(),
121 (event
->details().bounding_box().y() / scale_
) + target_rect_
.y(),
122 event
->details().bounding_box().width() / scale_
,
123 event
->details().bounding_box().height() / scale_
));
124 ui::GestureEvent
xform_event(xform_location
.x(),
129 gesture_cb_
.Run(&xform_event
);
132 // If we completed a tap we close ourselves, as the web content will navigate
133 // if the user hit a link.
134 if (event
->type() == ui::EventType::ET_GESTURE_TAP
)
138 void LinkDisambiguationPopup::ZoomBubbleView::OnWidgetClosing(
139 views::Widget
* widget
) {
140 popup_
->InvalidateBubbleView();
143 LinkDisambiguationPopup::LinkDisambiguationPopup()
148 LinkDisambiguationPopup::~LinkDisambiguationPopup() {
152 void LinkDisambiguationPopup::Show(
153 views::Widget
* top_level_widget
,
154 const SkBitmap
& zoomed_bitmap
,
155 const gfx::Rect
& target_rect
,
156 const gfx::NativeView content
,
157 const base::Callback
<void(ui::GestureEvent
*)>& gesture_cb
,
158 const base::Callback
<void(ui::MouseEvent
*)>& mouse_cb
) {
161 view_
= new ZoomBubbleView(
164 gfx::Image::CreateFrom1xBitmap(zoomed_bitmap
).ToImageSkia(),
169 // Center the zoomed bubble over the target rectangle, constrained to the
170 // work area in the current display. Since |target_rect| is provided in
171 // |content_| coordinate system, we must convert it into Screen coordinates
172 // for correct window positioning.
173 aura::client::ScreenPositionClient
* screen_position_client
=
174 aura::client::GetScreenPositionClient(content_
->GetRootWindow());
175 gfx::Point
target_screen(target_rect
.x() + (target_rect
.width() / 2),
176 target_rect
.y() + (target_rect
.height() / 2));
177 if (screen_position_client
)
178 screen_position_client
->ConvertPointToScreen(content_
, &target_screen
);
179 gfx::Rect
window_bounds(
180 target_screen
.x() - (zoomed_bitmap
.width() / 2),
181 target_screen
.y() - (zoomed_bitmap
.height() / 2),
182 zoomed_bitmap
.width(),
183 zoomed_bitmap
.height());
184 const gfx::Display display
=
185 gfx::Screen::GetScreenFor(content
)->GetDisplayNearestWindow(content
);
186 window_bounds
.AdjustToFit(display
.work_area());
187 view_
->GetWidget()->SetBounds(window_bounds
);
188 view_
->GetWidget()->Show();
191 void LinkDisambiguationPopup::Close() {
198 void LinkDisambiguationPopup::InvalidateBubbleView() {