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.
5 #include "chrome/browser/ui/views/find_bar_host.h"
9 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
10 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
11 #include "chrome/browser/ui/view_ids.h"
12 #include "chrome/browser/ui/views/find_bar_view.h"
13 #include "chrome/browser/ui/views/frame/browser_view.h"
14 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/browser/web_contents_view.h"
17 #include "ui/events/event.h"
18 #include "ui/events/keycodes/keyboard_codes.h"
19 #include "ui/views/focus/external_focus_tracker.h"
20 #include "ui/views/focus/view_storage.h"
21 #include "ui/views/widget/root_view.h"
22 #include "ui/views/widget/widget.h"
24 using content::NativeWebKeyboardEvent
;
28 // Declared in browser_dialogs.h so others don't have to depend on our header.
29 FindBar
* CreateFindBar(BrowserView
* browser_view
) {
30 return new FindBarHost(browser_view
);
35 ////////////////////////////////////////////////////////////////////////////////
36 // FindBarHost, public:
38 FindBarHost::FindBarHost(BrowserView
* browser_view
)
39 : DropdownBarHost(browser_view
),
40 find_bar_controller_(NULL
) {
41 FindBarView
* find_bar_view
= new FindBarView(this);
42 Init(browser_view
->find_bar_host_view(), find_bar_view
, find_bar_view
);
45 FindBarHost::~FindBarHost() {
48 bool FindBarHost::MaybeForwardKeyEventToWebpage(
49 const ui::KeyEvent
& key_event
) {
50 if (!ShouldForwardKeyEventToWebpageNative(key_event
)) {
51 // Native implementation says not to forward these events.
55 switch (key_event
.key_code()) {
63 if (key_event
.IsControlDown())
70 content::WebContents
* contents
= find_bar_controller_
->web_contents();
74 content::RenderViewHost
* render_view_host
= contents
->GetRenderViewHost();
76 // Make sure we don't have a text field element interfering with keyboard
77 // input. Otherwise Up and Down arrow key strokes get eaten. "Nom Nom Nom".
78 render_view_host
->ClearFocusedNode();
79 NativeWebKeyboardEvent event
= GetKeyboardEvent(contents
, key_event
);
80 render_view_host
->ForwardKeyboardEvent(event
);
84 FindBarController
* FindBarHost::GetFindBarController() const {
85 return find_bar_controller_
;
88 void FindBarHost::SetFindBarController(FindBarController
* find_bar_controller
) {
89 find_bar_controller_
= find_bar_controller
;
92 void FindBarHost::Show(bool animate
) {
93 DropdownBarHost::Show(animate
);
96 void FindBarHost::Hide(bool animate
) {
97 DropdownBarHost::Hide(animate
);
100 void FindBarHost::SetFocusAndSelection() {
101 DropdownBarHost::SetFocusAndSelection();
104 void FindBarHost::ClearResults(const FindNotificationDetails
& results
) {
105 find_bar_view()->UpdateForResult(results
, base::string16());
108 void FindBarHost::StopAnimation() {
109 DropdownBarHost::StopAnimation();
112 void FindBarHost::MoveWindowIfNecessary(const gfx::Rect
& selection_rect
,
114 // We only move the window if one is active for the current WebContents. If we
115 // don't check this, then SetWidgetPosition below will end up making the Find
117 content::WebContents
* web_contents
= find_bar_controller_
->web_contents();
121 FindTabHelper
* find_tab_helper
= FindTabHelper::FromWebContents(web_contents
);
122 if (!find_tab_helper
|| !find_tab_helper
->find_ui_active())
125 gfx::Rect new_pos
= GetDialogPosition(selection_rect
);
126 SetDialogPosition(new_pos
, no_redraw
);
128 // May need to redraw our frame to accommodate bookmark bar styles.
129 view()->Layout(); // Bounds may have changed.
130 view()->SchedulePaint();
133 void FindBarHost::SetFindTextAndSelectedRange(
134 const base::string16
& find_text
,
135 const gfx::Range
& selected_range
) {
136 find_bar_view()->SetFindTextAndSelectedRange(find_text
, selected_range
);
139 base::string16
FindBarHost::GetFindText() {
140 return find_bar_view()->GetFindText();
143 gfx::Range
FindBarHost::GetSelectedRange() {
144 return find_bar_view()->GetSelectedRange();
147 void FindBarHost::UpdateUIForFindResult(const FindNotificationDetails
& result
,
148 const base::string16
& find_text
) {
149 // Make sure match count is clear. It may get set again in UpdateForResult
150 // if enough data is available.
151 find_bar_view()->ClearMatchCount();
153 if (!find_text
.empty())
154 find_bar_view()->UpdateForResult(result
, find_text
);
156 // We now need to check if the window is obscuring the search results.
157 MoveWindowIfNecessary(result
.selection_rect(), false);
159 // Once we find a match we no longer want to keep track of what had
160 // focus. EndFindSession will then set the focus to the page content.
161 if (result
.number_of_matches() > 0)
165 bool FindBarHost::IsFindBarVisible() {
166 return DropdownBarHost::IsVisible();
169 void FindBarHost::RestoreSavedFocus() {
170 if (focus_tracker() == NULL
) {
171 // TODO(brettw): Focus() should be on WebContentsView.
172 find_bar_controller_
->web_contents()->GetView()->Focus();
174 focus_tracker()->FocusLastFocusedExternalView();
178 bool FindBarHost::HasGlobalFindPasteboard() {
182 void FindBarHost::UpdateFindBarForChangedWebContents() {
185 FindBarTesting
* FindBarHost::GetFindBarTesting() {
189 ////////////////////////////////////////////////////////////////////////////////
190 // FindBarWin, ui::AcceleratorTarget implementation:
192 bool FindBarHost::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
193 ui::KeyboardCode key
= accelerator
.key_code();
194 if (key
== ui::VKEY_RETURN
&& accelerator
.IsCtrlDown()) {
195 // Ctrl+Enter closes the Find session and navigates any link that is active.
196 find_bar_controller_
->EndFindSession(
197 FindBarController::kActivateSelectionOnPage
,
198 FindBarController::kClearResultsInFindBox
);
200 } else if (key
== ui::VKEY_ESCAPE
) {
201 // This will end the Find session and hide the window, causing it to loose
202 // focus and in the process unregister us as the handler for the Escape
203 // accelerator through the OnWillChangeFocus event.
204 find_bar_controller_
->EndFindSession(
205 FindBarController::kKeepSelectionOnPage
,
206 FindBarController::kKeepResultsInFindBox
);
209 NOTREACHED() << "Unknown accelerator";
215 bool FindBarHost::CanHandleAccelerators() const {
219 ////////////////////////////////////////////////////////////////////////////////
220 // FindBarTesting implementation:
222 bool FindBarHost::GetFindBarWindowInfo(gfx::Point
* position
,
223 bool* fully_visible
) {
224 if (!find_bar_controller_
||
225 #if defined(OS_WIN) && !defined(USE_AURA)
226 !::IsWindow(host()->GetNativeView())) {
229 // TODO(sky): figure out linux side.
230 // This is tricky due to asynchronous nature of x11.
231 // See bug http://crbug.com/28629.
234 *position
= gfx::Point();
236 *fully_visible
= false;
240 gfx::Rect window_rect
= host()->GetWindowBoundsInScreen();
242 *position
= window_rect
.origin();
244 *fully_visible
= IsVisible() && !IsAnimating();
248 base::string16
FindBarHost::GetFindSelectedText() {
249 return find_bar_view()->GetFindSelectedText();
252 base::string16
FindBarHost::GetMatchCountText() {
253 return find_bar_view()->GetMatchCountText();
256 int FindBarHost::GetWidth() {
257 return view()->width();
260 ////////////////////////////////////////////////////////////////////////////////
261 // Overridden from DropdownBarHost:
263 gfx::Rect
FindBarHost::GetDialogPosition(gfx::Rect avoid_overlapping_rect
) {
264 // Find the area we have to work with (after accounting for scrollbars, etc).
265 gfx::Rect widget_bounds
;
266 GetWidgetBounds(&widget_bounds
);
267 if (widget_bounds
.IsEmpty())
270 // Ask the view how large an area it needs to draw on.
271 gfx::Size prefsize
= view()->GetPreferredSize();
273 // Limit width to the available area.
274 if (widget_bounds
.width() < prefsize
.width())
275 prefsize
.set_width(widget_bounds
.width());
277 // Don't show the find bar if |widget_bounds| is not tall enough.
278 if (widget_bounds
.height() < prefsize
.height())
281 // Place the view in the top right corner of the widget boundaries (top left
282 // for RTL languages).
283 gfx::Rect view_location
;
284 int x
= widget_bounds
.x();
285 if (!base::i18n::IsRTL())
286 x
+= widget_bounds
.width() - prefsize
.width();
287 int y
= widget_bounds
.y();
288 view_location
.SetRect(x
, y
, prefsize
.width(), prefsize
.height());
290 // When we get Find results back, we specify a selection rect, which we
291 // should strive to avoid overlapping. But first, we need to offset the
292 // selection rect (if one was provided).
293 if (!avoid_overlapping_rect
.IsEmpty()) {
294 // For comparison (with the Intersects function below) we need to account
295 // for the fact that we draw the Find widget relative to the Chrome frame,
296 // whereas the selection rect is relative to the page.
297 GetWidgetPositionNative(&avoid_overlapping_rect
);
300 gfx::Rect new_pos
= FindBarController::GetLocationForFindbarView(
301 view_location
, widget_bounds
, avoid_overlapping_rect
);
303 // While we are animating, the Find window will grow bottoms up so we need to
304 // re-position the widget so that it appears to grow out of the toolbar.
305 if (animation_offset() > 0)
306 new_pos
.Offset(0, std::min(0, -animation_offset()));
311 void FindBarHost::SetDialogPosition(const gfx::Rect
& new_pos
, bool no_redraw
) {
312 if (new_pos
.IsEmpty())
315 // Make sure the window edges are clipped to just the visible region. We need
316 // to do this before changing position, so that when we animate the closure
317 // of it it doesn't look like the window crumbles into the toolbar.
318 UpdateWindowEdges(new_pos
);
320 SetWidgetPositionNative(new_pos
, no_redraw
);
322 // Tell the immersive mode controller about the find bar's new bounds. The
323 // immersive mode controller uses the bounds to keep the top-of-window views
324 // revealed when the mouse is hovered over the find bar.
325 browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
326 host()->GetWindowBoundsInScreen());
329 void FindBarHost::GetWidgetBounds(gfx::Rect
* bounds
) {
331 // The BrowserView does Layout for the components that we care about
332 // positioning relative to, so we ask it to tell us where we should go.
333 *bounds
= browser_view()->GetFindBarBoundingBox();
336 void FindBarHost::RegisterAccelerators() {
337 DropdownBarHost::RegisterAccelerators();
339 // Register for Ctrl+Return.
340 ui::Accelerator
escape(ui::VKEY_RETURN
, ui::EF_CONTROL_DOWN
);
341 focus_manager()->RegisterAccelerator(
342 escape
, ui::AcceleratorManager::kNormalPriority
, this);
345 void FindBarHost::UnregisterAccelerators() {
346 // Unregister Ctrl+Return.
347 ui::Accelerator
escape(ui::VKEY_RETURN
, ui::EF_CONTROL_DOWN
);
348 focus_manager()->UnregisterAccelerator(escape
, this);
350 DropdownBarHost::UnregisterAccelerators();
353 void FindBarHost::OnVisibilityChanged() {
354 // Tell the immersive mode controller about the find bar's bounds. The
355 // immersive mode controller uses the bounds to keep the top-of-window views
356 // revealed when the mouse is hovered over the find bar.
357 gfx::Rect visible_bounds
;
359 visible_bounds
= host()->GetWindowBoundsInScreen();
360 browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
364 ////////////////////////////////////////////////////////////////////////////////
367 void FindBarHost::GetWidgetPositionNative(gfx::Rect
* avoid_overlapping_rect
) {
368 gfx::Rect frame_rect
= host()->GetTopLevelWidget()->GetWindowBoundsInScreen();
369 content::WebContentsView
* tab_view
=
370 find_bar_controller_
->web_contents()->GetView();
371 gfx::Rect webcontents_rect
= tab_view
->GetViewBounds();
372 avoid_overlapping_rect
->Offset(0, webcontents_rect
.y() - frame_rect
.y());