find_bar_host.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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. 4 5#include "chrome/browser/ui/views/find_bar_host.h" 6 7#include <algorithm> 8 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" 23 24using content::NativeWebKeyboardEvent; 25 26namespace chrome { 27 28// Declared in browser_dialogs.h so others don't have to depend on our header. 29FindBar* CreateFindBar(BrowserView* browser_view) { 30 return new FindBarHost(browser_view); 31} 32 33} // namespace chrome 34 35//////////////////////////////////////////////////////////////////////////////// 36// FindBarHost, public: 37 38FindBarHost::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); 43} 44 45FindBarHost::~FindBarHost() { 46} 47 48bool FindBarHost::MaybeForwardKeyEventToWebpage( 49 const ui::KeyEvent& key_event) { 50 if (!ShouldForwardKeyEventToWebpageNative(key_event)) { 51 // Native implementation says not to forward these events. 52 return false; 53 } 54 55 switch (key_event.key_code()) { 56 case ui::VKEY_DOWN: 57 case ui::VKEY_UP: 58 case ui::VKEY_PRIOR: 59 case ui::VKEY_NEXT: 60 break; 61 case ui::VKEY_HOME: 62 case ui::VKEY_END: 63 if (key_event.IsControlDown()) 64 break; 65 // Fall through. 66 default: 67 return false; 68 } 69 70 content::WebContents* contents = find_bar_controller_->web_contents(); 71 if (!contents) 72 return false; 73 74 content::RenderViewHost* render_view_host = contents->GetRenderViewHost(); 75 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); 81 return true; 82} 83 84FindBarController* FindBarHost::GetFindBarController() const { 85 return find_bar_controller_; 86} 87 88void FindBarHost::SetFindBarController(FindBarController* find_bar_controller) { 89 find_bar_controller_ = find_bar_controller; 90} 91 92void FindBarHost::Show(bool animate) { 93 DropdownBarHost::Show(animate); 94} 95 96void FindBarHost::Hide(bool animate) { 97 DropdownBarHost::Hide(animate); 98} 99 100void FindBarHost::SetFocusAndSelection() { 101 DropdownBarHost::SetFocusAndSelection(); 102} 103 104void FindBarHost::ClearResults(const FindNotificationDetails& results) { 105 find_bar_view()->UpdateForResult(results, string16()); 106} 107 108void FindBarHost::StopAnimation() { 109 DropdownBarHost::StopAnimation(); 110} 111 112void FindBarHost::MoveWindowIfNecessary(const gfx::Rect& selection_rect, 113 bool no_redraw) { 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 116 // Bar visible. 117 content::WebContents* web_contents = find_bar_controller_->web_contents(); 118 if (!web_contents) 119 return; 120 121 FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(web_contents); 122 if (!find_tab_helper || !find_tab_helper->find_ui_active()) 123 return; 124 125 gfx::Rect new_pos = GetDialogPosition(selection_rect); 126 SetDialogPosition(new_pos, no_redraw); 127 128 // May need to redraw our frame to accommodate bookmark bar styles. 129 view()->Layout(); // Bounds may have changed. 130 view()->SchedulePaint(); 131} 132 133void FindBarHost::SetFindTextAndSelectedRange( 134 const string16& find_text, 135 const gfx::Range& selected_range) { 136 find_bar_view()->SetFindTextAndSelectedRange(find_text, selected_range); 137} 138 139string16 FindBarHost::GetFindText() { 140 return find_bar_view()->GetFindText(); 141} 142 143gfx::Range FindBarHost::GetSelectedRange() { 144 return find_bar_view()->GetSelectedRange(); 145} 146 147void FindBarHost::UpdateUIForFindResult(const FindNotificationDetails& result, 148 const 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(); 152 153 if (!find_text.empty()) 154 find_bar_view()->UpdateForResult(result, find_text); 155 156 // We now need to check if the window is obscuring the search results. 157 MoveWindowIfNecessary(result.selection_rect(), false); 158 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) 162 ResetFocusTracker(); 163} 164 165bool FindBarHost::IsFindBarVisible() { 166 return DropdownBarHost::IsVisible(); 167} 168 169void FindBarHost::RestoreSavedFocus() { 170 if (focus_tracker() == NULL) { 171 // TODO(brettw): Focus() should be on WebContentsView. 172 find_bar_controller_->web_contents()->GetView()->Focus(); 173 } else { 174 focus_tracker()->FocusLastFocusedExternalView(); 175 } 176} 177 178bool FindBarHost::HasGlobalFindPasteboard() { 179 return false; 180} 181 182void FindBarHost::UpdateFindBarForChangedWebContents() { 183} 184 185FindBarTesting* FindBarHost::GetFindBarTesting() { 186 return this; 187} 188 189//////////////////////////////////////////////////////////////////////////////// 190// FindBarWin, ui::AcceleratorTarget implementation: 191 192bool 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); 199 return true; 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); 207 return true; 208 } else { 209 NOTREACHED() << "Unknown accelerator"; 210 } 211 212 return false; 213} 214 215bool FindBarHost::CanHandleAccelerators() const { 216 return true; 217} 218 219//////////////////////////////////////////////////////////////////////////////// 220// FindBarTesting implementation: 221 222bool 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())) { 227#else 228 false) { 229 // TODO(sky): figure out linux side. 230 // This is tricky due to asynchronous nature of x11. 231 // See bug http://crbug.com/28629. 232#endif 233 if (position) 234 *position = gfx::Point(); 235 if (fully_visible) 236 *fully_visible = false; 237 return false; 238 } 239 240 gfx::Rect window_rect = host()->GetWindowBoundsInScreen(); 241 if (position) 242 *position = window_rect.origin(); 243 if (fully_visible) 244 *fully_visible = IsVisible() && !IsAnimating(); 245 return true; 246} 247 248string16 FindBarHost::GetFindSelectedText() { 249 return find_bar_view()->GetFindSelectedText(); 250} 251 252string16 FindBarHost::GetMatchCountText() { 253 return find_bar_view()->GetMatchCountText(); 254} 255 256int FindBarHost::GetWidth() { 257 return view()->width(); 258} 259 260//////////////////////////////////////////////////////////////////////////////// 261// Overridden from DropdownBarHost: 262 263gfx::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()) 268 return gfx::Rect(); 269 270 // Ask the view how large an area it needs to draw on. 271 gfx::Size prefsize = view()->GetPreferredSize(); 272 273 // Limit width to the available area. 274 if (widget_bounds.width() < prefsize.width()) 275 prefsize.set_width(widget_bounds.width()); 276 277 // Don't show the find bar if |widget_bounds| is not tall enough. 278 if (widget_bounds.height() < prefsize.height()) 279 return gfx::Rect(); 280 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()); 289 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); 298 } 299 300 gfx::Rect new_pos = FindBarController::GetLocationForFindbarView( 301 view_location, widget_bounds, avoid_overlapping_rect); 302 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())); 307 308 return new_pos; 309} 310 311void FindBarHost::SetDialogPosition(const gfx::Rect& new_pos, bool no_redraw) { 312 if (new_pos.IsEmpty()) 313 return; 314 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); 319 320 SetWidgetPositionNative(new_pos, no_redraw); 321 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()); 327} 328 329void FindBarHost::GetWidgetBounds(gfx::Rect* bounds) { 330 DCHECK(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(); 334} 335 336void FindBarHost::RegisterAccelerators() { 337 DropdownBarHost::RegisterAccelerators(); 338 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); 343} 344 345void FindBarHost::UnregisterAccelerators() { 346 // Unregister Ctrl+Return. 347 ui::Accelerator escape(ui::VKEY_RETURN, ui::EF_CONTROL_DOWN); 348 focus_manager()->UnregisterAccelerator(escape, this); 349 350 DropdownBarHost::UnregisterAccelerators(); 351} 352 353void 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; 358 if (IsVisible()) 359 visible_bounds = host()->GetWindowBoundsInScreen(); 360 browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged( 361 visible_bounds); 362} 363 364//////////////////////////////////////////////////////////////////////////////// 365// private: 366 367void 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()); 373} 374