• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/touch/frame/touch_browser_frame_view.h"
6 
7 #include "chrome/browser/profiles/profile.h"
8 #include "chrome/browser/renderer_host/render_widget_host_view_views.h"
9 #include "chrome/browser/tabs/tab_strip_model.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
12 #include "chrome/browser/ui/touch/frame/keyboard_container_view.h"
13 #include "chrome/browser/ui/views/frame/browser_view.h"
14 #include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
15 #include "content/browser/renderer_host/render_view_host.h"
16 #include "content/browser/tab_contents/navigation_controller.h"
17 #include "content/browser/tab_contents/tab_contents.h"
18 #include "content/browser/tab_contents/tab_contents_view.h"
19 #include "content/common/notification_service.h"
20 #include "content/common/notification_type.h"
21 #include "ui/base/animation/slide_animation.h"
22 #include "ui/gfx/rect.h"
23 #include "views/controls/button/image_button.h"
24 #include "views/controls/textfield/textfield.h"
25 #include "views/focus/focus_manager.h"
26 
27 namespace {
28 
29 const int kKeyboardHeight = 300;
30 const int kKeyboardSlideDuration = 500;  // In milliseconds
31 
GetFocusedStateAccessor()32 PropertyAccessor<bool>* GetFocusedStateAccessor() {
33   static PropertyAccessor<bool> state;
34   return &state;
35 }
36 
TabContentsHasFocus(const TabContents * contents)37 bool TabContentsHasFocus(const TabContents* contents) {
38   views::View* view = static_cast<TabContentsViewTouch*>(contents->view());
39   return view->Contains(view->GetFocusManager()->GetFocusedView());
40 }
41 
42 }  // namespace
43 
44 ///////////////////////////////////////////////////////////////////////////////
45 // TouchBrowserFrameView, public:
46 
TouchBrowserFrameView(BrowserFrame * frame,BrowserView * browser_view)47 TouchBrowserFrameView::TouchBrowserFrameView(BrowserFrame* frame,
48                                              BrowserView* browser_view)
49     : OpaqueBrowserFrameView(frame, browser_view),
50       keyboard_showing_(false),
51       focus_listener_added_(false),
52       keyboard_(NULL) {
53   registrar_.Add(this,
54                  NotificationType::NAV_ENTRY_COMMITTED,
55                  NotificationService::AllSources());
56   registrar_.Add(this,
57                  NotificationType::FOCUS_CHANGED_IN_PAGE,
58                  NotificationService::AllSources());
59   registrar_.Add(this,
60                  NotificationType::TAB_CONTENTS_DESTROYED,
61                  NotificationService::AllSources());
62 
63   browser_view->browser()->tabstrip_model()->AddObserver(this);
64 
65   animation_.reset(new ui::SlideAnimation(this));
66   animation_->SetTweenType(ui::Tween::LINEAR);
67   animation_->SetSlideDuration(kKeyboardSlideDuration);
68 }
69 
~TouchBrowserFrameView()70 TouchBrowserFrameView::~TouchBrowserFrameView() {
71   browser_view()->browser()->tabstrip_model()->RemoveObserver(this);
72 }
73 
Layout()74 void TouchBrowserFrameView::Layout() {
75   OpaqueBrowserFrameView::Layout();
76 
77   if (!keyboard_)
78     return;
79 
80   keyboard_->SetVisible(keyboard_showing_ || animation_->is_animating());
81   gfx::Rect bounds = GetBoundsForReservedArea();
82   if (animation_->is_animating() && !keyboard_showing_) {
83     // The keyboard is in the process of hiding. So pretend it still has the
84     // same bounds as when the keyboard is visible. But
85     // |GetBoundsForReservedArea| should not take this into account so that the
86     // render view gets the entire area to relayout itself.
87     bounds.set_y(bounds.y() - kKeyboardHeight);
88     bounds.set_height(kKeyboardHeight);
89   }
90   keyboard_->SetBoundsRect(bounds);
91 }
92 
FocusWillChange(views::View * focused_before,views::View * focused_now)93 void TouchBrowserFrameView::FocusWillChange(views::View* focused_before,
94                                             views::View* focused_now) {
95   VirtualKeyboardType before = DecideKeyboardStateForView(focused_before);
96   VirtualKeyboardType now = DecideKeyboardStateForView(focused_now);
97   if (before != now) {
98     // TODO(varunjain): support other types of keyboard.
99     UpdateKeyboardAndLayout(now == GENERIC);
100   }
101 }
102 
103 ///////////////////////////////////////////////////////////////////////////////
104 // TouchBrowserFrameView, protected:
GetReservedHeight() const105 int TouchBrowserFrameView::GetReservedHeight() const {
106   return keyboard_showing_ ? kKeyboardHeight : 0;
107 }
108 
ViewHierarchyChanged(bool is_add,View * parent,View * child)109 void TouchBrowserFrameView::ViewHierarchyChanged(bool is_add,
110                                                  View* parent,
111                                                  View* child) {
112   OpaqueBrowserFrameView::ViewHierarchyChanged(is_add, parent, child);
113   if (!GetFocusManager())
114     return;
115 
116   if (is_add && !focus_listener_added_) {
117     // Add focus listener when this view is added to the hierarchy.
118     GetFocusManager()->AddFocusChangeListener(this);
119     focus_listener_added_ = true;
120   } else if (!is_add && focus_listener_added_) {
121     // Remove focus listener when this view is removed from the hierarchy.
122     GetFocusManager()->RemoveFocusChangeListener(this);
123     focus_listener_added_ = false;
124   }
125 }
126 
127 ///////////////////////////////////////////////////////////////////////////////
128 // TouchBrowserFrameView, private:
129 
InitVirtualKeyboard()130 void TouchBrowserFrameView::InitVirtualKeyboard() {
131   if (keyboard_)
132     return;
133 
134   Profile* keyboard_profile = browser_view()->browser()->profile();
135   DCHECK(keyboard_profile) << "Profile required for virtual keyboard.";
136 
137   keyboard_ = new KeyboardContainerView(keyboard_profile);
138   keyboard_->SetVisible(false);
139   AddChildView(keyboard_);
140 }
141 
UpdateKeyboardAndLayout(bool should_show_keyboard)142 void TouchBrowserFrameView::UpdateKeyboardAndLayout(bool should_show_keyboard) {
143   if (should_show_keyboard)
144     InitVirtualKeyboard();
145 
146   if (should_show_keyboard == keyboard_showing_)
147     return;
148 
149   DCHECK(keyboard_);
150 
151   keyboard_showing_ = should_show_keyboard;
152   if (keyboard_showing_) {
153     animation_->Show();
154 
155     // We don't re-layout the client view until the animation ends (see
156     // AnimationEnded below) because we want the client view to occupy the
157     // entire height during the animation.
158     Layout();
159   } else {
160     animation_->Hide();
161 
162     browser_view()->set_clip_y(ui::Tween::ValueBetween(
163           animation_->GetCurrentValue(), 0, kKeyboardHeight));
164     parent()->Layout();
165   }
166 }
167 
168 TouchBrowserFrameView::VirtualKeyboardType
DecideKeyboardStateForView(views::View * view)169     TouchBrowserFrameView::DecideKeyboardStateForView(views::View* view) {
170   if (!view)
171     return NONE;
172 
173   std::string cname = view->GetClassName();
174   if (cname == views::Textfield::kViewClassName) {
175     return GENERIC;
176   } else if (cname == RenderWidgetHostViewViews::kViewClassName) {
177     TabContents* contents = browser_view()->browser()->GetSelectedTabContents();
178     bool* editable = contents ? GetFocusedStateAccessor()->GetProperty(
179         contents->property_bag()) : NULL;
180     if (editable && *editable)
181       return GENERIC;
182   }
183   return NONE;
184 }
185 
HitTest(const gfx::Point & point) const186 bool TouchBrowserFrameView::HitTest(const gfx::Point& point) const {
187   if (OpaqueBrowserFrameView::HitTest(point))
188     return true;
189 
190   if (close_button()->IsVisible() &&
191       close_button()->GetMirroredBounds().Contains(point))
192     return true;
193   if (restore_button()->IsVisible() &&
194       restore_button()->GetMirroredBounds().Contains(point))
195     return true;
196   if (maximize_button()->IsVisible() &&
197       maximize_button()->GetMirroredBounds().Contains(point))
198     return true;
199   if (minimize_button()->IsVisible() &&
200       minimize_button()->GetMirroredBounds().Contains(point))
201     return true;
202 
203   return false;
204 }
205 
TabSelectedAt(TabContentsWrapper * old_contents,TabContentsWrapper * new_contents,int index,bool user_gesture)206 void TouchBrowserFrameView::TabSelectedAt(TabContentsWrapper* old_contents,
207                                           TabContentsWrapper* new_contents,
208                                           int index,
209                                           bool user_gesture) {
210   if (new_contents == old_contents)
211     return;
212 
213   TabContents* contents = new_contents->tab_contents();
214   if (!TabContentsHasFocus(contents))
215     return;
216 
217   bool* editable = GetFocusedStateAccessor()->GetProperty(
218       contents->property_bag());
219   UpdateKeyboardAndLayout(editable ? *editable : false);
220 }
221 
222 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)223 void TouchBrowserFrameView::Observe(NotificationType type,
224                                     const NotificationSource& source,
225                                     const NotificationDetails& details) {
226   Browser* browser = browser_view()->browser();
227   if (type == NotificationType::FOCUS_CHANGED_IN_PAGE) {
228     // Only modify the keyboard state if the currently active tab sent the
229     // notification.
230     const TabContents* current_tab = browser->GetSelectedTabContents();
231     TabContents* source_tab = Source<TabContents>(source).ptr();
232     const bool editable = *Details<const bool>(details).ptr();
233 
234     if (current_tab == source_tab && TabContentsHasFocus(source_tab))
235       UpdateKeyboardAndLayout(editable);
236 
237     // Save the state of the focused field so that the keyboard visibility
238     // can be determined after tab switching.
239     GetFocusedStateAccessor()->SetProperty(
240         source_tab->property_bag(), editable);
241   } else if (type == NotificationType::NAV_ENTRY_COMMITTED) {
242     Browser* source_browser = Browser::GetBrowserForController(
243         Source<NavigationController>(source).ptr(), NULL);
244     // If the Browser for the keyboard has navigated, re-evaluate the visibility
245     // of the keyboard.
246     if (source_browser == browser)
247       UpdateKeyboardAndLayout(DecideKeyboardStateForView(
248           GetFocusManager()->GetFocusedView()) == GENERIC);
249   } else if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
250     GetFocusedStateAccessor()->DeleteProperty(
251         Source<TabContents>(source).ptr()->property_bag());
252   }
253 }
254 
255 ///////////////////////////////////////////////////////////////////////////////
256 // ui::AnimationDelegate implementation
AnimationProgressed(const ui::Animation * anim)257 void TouchBrowserFrameView::AnimationProgressed(const ui::Animation* anim) {
258   keyboard_->SetTranslateY(
259       ui::Tween::ValueBetween(anim->GetCurrentValue(), kKeyboardHeight, 0));
260   browser_view()->set_clip_y(
261       ui::Tween::ValueBetween(anim->GetCurrentValue(), 0, kKeyboardHeight));
262   SchedulePaint();
263 }
264 
AnimationEnded(const ui::Animation * animation)265 void TouchBrowserFrameView::AnimationEnded(const ui::Animation* animation) {
266   browser_view()->set_clip_y(0);
267   if (keyboard_showing_) {
268     // Because the NonClientFrameView is a sibling of the ClientView, we rely on
269     // the parent to resize the ClientView instead of resizing it directly.
270     parent()->Layout();
271 
272     // The keyboard that pops up may end up hiding the text entry. So make sure
273     // the renderer scrolls when necessary to keep the textfield visible.
274     RenderViewHost* host =
275         browser_view()->browser()->GetSelectedTabContents()->render_view_host();
276     host->ScrollFocusedEditableNodeIntoView();
277   }
278   SchedulePaint();
279 }
280