Home | History | Annotate | Download | only in views
      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/base/events/event.h"
     18 #include "ui/base/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 
     24 using content::NativeWebKeyboardEvent;
     25 
     26 namespace chrome {
     27 
     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);
     31 }
     32 
     33 }  // namespace chrome
     34 
     35 ////////////////////////////////////////////////////////////////////////////////
     36 // FindBarHost, public:
     37 
     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);
     43 }
     44 
     45 FindBarHost::~FindBarHost() {
     46 }
     47 
     48 bool 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 
     84 FindBarController* FindBarHost::GetFindBarController() const {
     85   return find_bar_controller_;
     86 }
     87 
     88 void FindBarHost::SetFindBarController(FindBarController* find_bar_controller) {
     89   find_bar_controller_ = find_bar_controller;
     90 }
     91 
     92 void FindBarHost::Show(bool animate) {
     93   DropdownBarHost::Show(animate);
     94 }
     95 
     96 void FindBarHost::Hide(bool animate) {
     97   DropdownBarHost::Hide(animate);
     98 }
     99 
    100 void FindBarHost::SetFocusAndSelection() {
    101   DropdownBarHost::SetFocusAndSelection();
    102 }
    103 
    104 void FindBarHost::ClearResults(const FindNotificationDetails& results) {
    105   find_bar_view()->UpdateForResult(results, string16());
    106 }
    107 
    108 void FindBarHost::StopAnimation() {
    109   DropdownBarHost::StopAnimation();
    110 }
    111 
    112 void 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 
    133 void FindBarHost::SetFindText(const string16& find_text) {
    134   find_bar_view()->SetFindText(find_text);
    135 }
    136 
    137 void FindBarHost::UpdateUIForFindResult(const FindNotificationDetails& result,
    138                                         const string16& find_text) {
    139   // Make sure match count is clear. It may get set again in UpdateForResult
    140   // if enough data is available.
    141   find_bar_view()->ClearMatchCount();
    142 
    143   if (!find_text.empty())
    144     find_bar_view()->UpdateForResult(result, find_text);
    145 
    146   // We now need to check if the window is obscuring the search results.
    147   MoveWindowIfNecessary(result.selection_rect(), false);
    148 
    149   // Once we find a match we no longer want to keep track of what had
    150   // focus. EndFindSession will then set the focus to the page content.
    151   if (result.number_of_matches() > 0)
    152     ResetFocusTracker();
    153 }
    154 
    155 bool FindBarHost::IsFindBarVisible() {
    156   return DropdownBarHost::IsVisible();
    157 }
    158 
    159 void FindBarHost::RestoreSavedFocus() {
    160   if (focus_tracker() == NULL) {
    161     // TODO(brettw): Focus() should be on WebContentsView.
    162     find_bar_controller_->web_contents()->GetView()->Focus();
    163   } else {
    164     focus_tracker()->FocusLastFocusedExternalView();
    165   }
    166 }
    167 
    168 bool FindBarHost::HasGlobalFindPasteboard() {
    169   return false;
    170 }
    171 
    172 void FindBarHost::UpdateFindBarForChangedWebContents() {
    173 }
    174 
    175 FindBarTesting* FindBarHost::GetFindBarTesting() {
    176   return this;
    177 }
    178 
    179 ////////////////////////////////////////////////////////////////////////////////
    180 // FindBarWin, ui::AcceleratorTarget implementation:
    181 
    182 bool FindBarHost::AcceleratorPressed(const ui::Accelerator& accelerator) {
    183   ui::KeyboardCode key = accelerator.key_code();
    184   if (key == ui::VKEY_RETURN && accelerator.IsCtrlDown()) {
    185     // Ctrl+Enter closes the Find session and navigates any link that is active.
    186     find_bar_controller_->EndFindSession(
    187         FindBarController::kActivateSelectionOnPage,
    188         FindBarController::kClearResultsInFindBox);
    189     return true;
    190   } else if (key == ui::VKEY_ESCAPE) {
    191     // This will end the Find session and hide the window, causing it to loose
    192     // focus and in the process unregister us as the handler for the Escape
    193     // accelerator through the OnWillChangeFocus event.
    194     find_bar_controller_->EndFindSession(
    195         FindBarController::kKeepSelectionOnPage,
    196         FindBarController::kKeepResultsInFindBox);
    197     return true;
    198   } else {
    199     NOTREACHED() << "Unknown accelerator";
    200   }
    201 
    202   return false;
    203 }
    204 
    205 bool FindBarHost::CanHandleAccelerators() const {
    206   return true;
    207 }
    208 
    209 ////////////////////////////////////////////////////////////////////////////////
    210 // FindBarTesting implementation:
    211 
    212 bool FindBarHost::GetFindBarWindowInfo(gfx::Point* position,
    213                                       bool* fully_visible) {
    214   if (!find_bar_controller_ ||
    215 #if defined(OS_WIN) && !defined(USE_AURA)
    216       !::IsWindow(host()->GetNativeView())) {
    217 #else
    218       false) {
    219       // TODO(sky): figure out linux side.
    220       // This is tricky due to asynchronous nature of x11.
    221       // See bug http://crbug.com/28629.
    222 #endif
    223     if (position)
    224       *position = gfx::Point();
    225     if (fully_visible)
    226       *fully_visible = false;
    227     return false;
    228   }
    229 
    230   gfx::Rect window_rect = host()->GetWindowBoundsInScreen();
    231   if (position)
    232     *position = window_rect.origin();
    233   if (fully_visible)
    234     *fully_visible = IsVisible() && !IsAnimating();
    235   return true;
    236 }
    237 
    238 string16 FindBarHost::GetFindText() {
    239   return find_bar_view()->GetFindText();
    240 }
    241 
    242 string16 FindBarHost::GetFindSelectedText() {
    243   return find_bar_view()->GetFindSelectedText();
    244 }
    245 
    246 string16 FindBarHost::GetMatchCountText() {
    247   return find_bar_view()->GetMatchCountText();
    248 }
    249 
    250 int FindBarHost::GetWidth() {
    251   return view()->width();
    252 }
    253 
    254 ////////////////////////////////////////////////////////////////////////////////
    255 // Overridden from DropdownBarHost:
    256 
    257 gfx::Rect FindBarHost::GetDialogPosition(gfx::Rect avoid_overlapping_rect) {
    258   // Find the area we have to work with (after accounting for scrollbars, etc).
    259   gfx::Rect widget_bounds;
    260   GetWidgetBounds(&widget_bounds);
    261   if (widget_bounds.IsEmpty())
    262     return gfx::Rect();
    263 
    264   // Ask the view how large an area it needs to draw on.
    265   gfx::Size prefsize = view()->GetPreferredSize();
    266 
    267   // Limit width to the available area.
    268   if (widget_bounds.width() < prefsize.width())
    269     prefsize.set_width(widget_bounds.width());
    270 
    271   // Don't show the find bar if |widget_bounds| is not tall enough.
    272   if (widget_bounds.height() < prefsize.height())
    273     return gfx::Rect();
    274 
    275   // Place the view in the top right corner of the widget boundaries (top left
    276   // for RTL languages).
    277   gfx::Rect view_location;
    278   int x = widget_bounds.x();
    279   if (!base::i18n::IsRTL())
    280     x += widget_bounds.width() - prefsize.width();
    281   int y = widget_bounds.y();
    282   view_location.SetRect(x, y, prefsize.width(), prefsize.height());
    283 
    284   // When we get Find results back, we specify a selection rect, which we
    285   // should strive to avoid overlapping. But first, we need to offset the
    286   // selection rect (if one was provided).
    287   if (!avoid_overlapping_rect.IsEmpty()) {
    288     // For comparison (with the Intersects function below) we need to account
    289     // for the fact that we draw the Find widget relative to the Chrome frame,
    290     // whereas the selection rect is relative to the page.
    291     GetWidgetPositionNative(&avoid_overlapping_rect);
    292   }
    293 
    294   gfx::Rect new_pos = FindBarController::GetLocationForFindbarView(
    295       view_location, widget_bounds, avoid_overlapping_rect);
    296 
    297   // While we are animating, the Find window will grow bottoms up so we need to
    298   // re-position the widget so that it appears to grow out of the toolbar.
    299   if (animation_offset() > 0)
    300     new_pos.Offset(0, std::min(0, -animation_offset()));
    301 
    302   return new_pos;
    303 }
    304 
    305 void FindBarHost::SetDialogPosition(const gfx::Rect& new_pos, bool no_redraw) {
    306   if (new_pos.IsEmpty())
    307     return;
    308 
    309   // Make sure the window edges are clipped to just the visible region. We need
    310   // to do this before changing position, so that when we animate the closure
    311   // of it it doesn't look like the window crumbles into the toolbar.
    312   UpdateWindowEdges(new_pos);
    313 
    314   SetWidgetPositionNative(new_pos, no_redraw);
    315 
    316   // Tell the immersive mode controller about the find bar's new bounds. The
    317   // immersive mode controller uses the bounds to keep the top-of-window views
    318   // revealed when the mouse is hovered over the find bar.
    319   browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
    320       host()->GetWindowBoundsInScreen());
    321 }
    322 
    323 void FindBarHost::GetWidgetBounds(gfx::Rect* bounds) {
    324   DCHECK(bounds);
    325   // The BrowserView does Layout for the components that we care about
    326   // positioning relative to, so we ask it to tell us where we should go.
    327   *bounds = browser_view()->GetFindBarBoundingBox();
    328 }
    329 
    330 void FindBarHost::RegisterAccelerators() {
    331   DropdownBarHost::RegisterAccelerators();
    332 
    333   // Register for Ctrl+Return.
    334   ui::Accelerator escape(ui::VKEY_RETURN, ui::EF_CONTROL_DOWN);
    335   focus_manager()->RegisterAccelerator(
    336       escape, ui::AcceleratorManager::kNormalPriority, this);
    337 }
    338 
    339 void FindBarHost::UnregisterAccelerators() {
    340   // Unregister Ctrl+Return.
    341   ui::Accelerator escape(ui::VKEY_RETURN, ui::EF_CONTROL_DOWN);
    342   focus_manager()->UnregisterAccelerator(escape, this);
    343 
    344   DropdownBarHost::UnregisterAccelerators();
    345 }
    346 
    347 void FindBarHost::OnVisibilityChanged() {
    348   // Tell the immersive mode controller about the find bar's bounds. The
    349   // immersive mode controller uses the bounds to keep the top-of-window views
    350   // revealed when the mouse is hovered over the find bar.
    351   gfx::Rect visible_bounds;
    352   if (IsVisible())
    353     visible_bounds = host()->GetWindowBoundsInScreen();
    354   browser_view()->immersive_mode_controller()->OnFindBarVisibleBoundsChanged(
    355       visible_bounds);
    356 }
    357 
    358 ////////////////////////////////////////////////////////////////////////////////
    359 // private:
    360 
    361 void FindBarHost::GetWidgetPositionNative(gfx::Rect* avoid_overlapping_rect) {
    362   gfx::Rect frame_rect = host()->GetTopLevelWidget()->GetWindowBoundsInScreen();
    363   content::WebContentsView* tab_view =
    364       find_bar_controller_->web_contents()->GetView();
    365   gfx::Rect webcontents_rect = tab_view->GetViewBounds();
    366   avoid_overlapping_rect->Offset(0, webcontents_rect.y() - frame_rect.y());
    367 }
    368 
    369 FindBarView* FindBarHost::find_bar_view() {
    370   return static_cast<FindBarView*>(view());
    371 }
    372