OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/touch/frame/touch_browser_frame_view.h" | 5 #include "chrome/browser/ui/touch/frame/touch_browser_frame_view.h" |
6 | 6 |
7 #include "chrome/browser/profiles/profile.h" | 7 #include "chrome/browser/ui/touch/keyboard/keyboard_manager.h" |
8 #include "chrome/browser/renderer_host/render_widget_host_view_views.h" | 8 #include "ui/base/ime/text_input_type.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 "chrome/common/chrome_notification_types.h" | |
16 #include "content/browser/renderer_host/render_view_host.h" | |
17 #include "content/browser/tab_contents/navigation_controller.h" | |
18 #include "content/browser/tab_contents/tab_contents.h" | |
19 #include "content/browser/tab_contents/tab_contents_view.h" | |
20 #include "content/common/content_notification_types.h" | |
21 #include "content/common/notification_service.h" | |
22 #include "content/common/view_messages.h" | |
23 #include "ui/base/animation/slide_animation.h" | |
24 #include "ui/gfx/rect.h" | |
25 #include "ui/gfx/transform.h" | |
26 #include "views/controls/button/image_button.h" | 9 #include "views/controls/button/image_button.h" |
27 #include "views/controls/textfield/textfield.h" | 10 #include "views/controls/textfield/textfield.h" |
28 #include "views/focus/focus_manager.h" | 11 #include "views/focus/focus_manager.h" |
| 12 #include "views/ime/text_input_client.h" |
29 #include "views/window/hit_test.h" | 13 #include "views/window/hit_test.h" |
30 | 14 |
31 #if defined(OS_CHROMEOS) | |
32 #include "chrome/browser/chromeos/input_method/virtual_keyboard_selector.h" | |
33 #endif | |
34 | |
35 namespace { | |
36 | |
37 const int kDefaultKeyboardHeight = 300; | |
38 const int kKeyboardSlideDuration = 300; // In milliseconds | |
39 | |
40 PropertyAccessor<bool>* GetFocusedStateAccessor() { | |
41 static PropertyAccessor<bool> state; | |
42 return &state; | |
43 } | |
44 | |
45 bool TabContentsHasFocus(const TabContents* contents) { | |
46 views::View* view = static_cast<TabContentsViewTouch*>(contents->view()); | |
47 return view->Contains(view->GetFocusManager()->GetFocusedView()); | |
48 } | |
49 | |
50 } // namespace | |
51 | |
52 // static | 15 // static |
53 const char TouchBrowserFrameView::kViewClassName[] = | 16 const char TouchBrowserFrameView::kViewClassName[] = |
54 "browser/ui/touch/frame/TouchBrowserFrameView"; | 17 "browser/ui/touch/frame/TouchBrowserFrameView"; |
55 | 18 |
56 /////////////////////////////////////////////////////////////////////////////// | 19 /////////////////////////////////////////////////////////////////////////////// |
57 // TouchBrowserFrameView, public: | 20 // TouchBrowserFrameView, public: |
58 | 21 |
59 TouchBrowserFrameView::TouchBrowserFrameView(BrowserFrame* frame, | 22 TouchBrowserFrameView::TouchBrowserFrameView(BrowserFrame* frame, |
60 BrowserView* browser_view) | 23 BrowserView* browser_view) |
61 : OpaqueBrowserFrameView(frame, browser_view), | 24 : OpaqueBrowserFrameView(frame, browser_view), |
62 keyboard_showing_(false), | 25 focus_listener_added_(false) { |
63 keyboard_height_(kDefaultKeyboardHeight), | 26 // Make sure the singleton KeyboardManager object is initialized. |
64 focus_listener_added_(false), | 27 KeyboardManager::GetInstance(); |
65 keyboard_(NULL) { | |
66 registrar_.Add(this, | |
67 content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
68 NotificationService::AllSources()); | |
69 registrar_.Add(this, | |
70 content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE, | |
71 NotificationService::AllSources()); | |
72 registrar_.Add(this, | |
73 content::NOTIFICATION_TAB_CONTENTS_DESTROYED, | |
74 NotificationService::AllSources()); | |
75 registrar_.Add(this, | |
76 chrome::NOTIFICATION_HIDE_KEYBOARD_INVOKED, | |
77 NotificationService::AllSources()); | |
78 registrar_.Add(this, | |
79 chrome::NOTIFICATION_SET_KEYBOARD_HEIGHT_INVOKED, | |
80 NotificationService::AllSources()); | |
81 registrar_.Add(this, | |
82 chrome::NOTIFICATION_EDITABLE_ELEMENT_TOUCHED, | |
83 NotificationService::AllSources()); | |
84 | |
85 browser_view->browser()->tabstrip_model()->AddObserver(this); | |
86 | |
87 animation_.reset(new ui::SlideAnimation(this)); | |
88 animation_->SetTweenType(ui::Tween::LINEAR); | |
89 animation_->SetSlideDuration(kKeyboardSlideDuration); | |
90 | |
91 #if defined(OS_CHROMEOS) | |
92 chromeos::input_method::InputMethodManager* manager = | |
93 chromeos::input_method::InputMethodManager::GetInstance(); | |
94 manager->AddVirtualKeyboardObserver(this); | |
95 #endif | |
96 } | 28 } |
97 | 29 |
98 TouchBrowserFrameView::~TouchBrowserFrameView() { | 30 TouchBrowserFrameView::~TouchBrowserFrameView() { |
99 browser_view()->browser()->tabstrip_model()->RemoveObserver(this); | |
100 } | 31 } |
101 | 32 |
102 int TouchBrowserFrameView::NonClientHitTest(const gfx::Point& point) { | 33 /////////////////////////////////////////////////////////////////////////////// |
103 if (keyboard_ && keyboard_->GetMirroredBounds().Contains(point)) | 34 // TouchBrowserFrameView, private: |
104 return HTCLIENT; | 35 |
105 return OpaqueBrowserFrameView::NonClientHitTest(point); | 36 void TouchBrowserFrameView::FocusWillChange(views::View* focused_before, |
| 37 views::View* focused_now) { |
| 38 views::Widget* widget = focused_now ? focused_now->GetWidget() : NULL; |
| 39 if (!widget || !widget->IsActive()) |
| 40 return; |
| 41 |
| 42 views::TextInputClient* input = |
| 43 focused_now ? focused_now->GetTextInputClient() : NULL; |
| 44 // Show the keyboard if the focused view supports text-input. |
| 45 KeyboardManager* keyboard = KeyboardManager::GetInstance(); |
| 46 if (input && input->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) |
| 47 keyboard->ShowKeyboardForWidget(focused_now->GetWidget()); |
| 48 else |
| 49 keyboard->Hide(); |
106 } | 50 } |
107 | 51 |
108 std::string TouchBrowserFrameView::GetClassName() const { | 52 std::string TouchBrowserFrameView::GetClassName() const { |
109 return kViewClassName; | 53 return kViewClassName; |
110 } | 54 } |
111 | 55 |
112 void TouchBrowserFrameView::Layout() { | |
113 OpaqueBrowserFrameView::Layout(); | |
114 | |
115 if (!keyboard_) | |
116 return; | |
117 | |
118 keyboard_->SetVisible(keyboard_showing_ || animation_->is_animating()); | |
119 gfx::Rect bounds = GetBoundsForReservedArea(); | |
120 if (animation_->is_animating() && !keyboard_showing_) { | |
121 // The keyboard is in the process of hiding. So pretend it still has the | |
122 // same bounds as when the keyboard is visible. But | |
123 // |GetBoundsForReservedArea| should not take this into account so that the | |
124 // render view gets the entire area to relayout itself. | |
125 bounds.set_y(bounds.y() - keyboard_height_); | |
126 bounds.set_height(keyboard_height_); | |
127 } | |
128 keyboard_->SetBoundsRect(bounds); | |
129 } | |
130 | |
131 void TouchBrowserFrameView::FocusWillChange(views::View* focused_before, | |
132 views::View* focused_now) { | |
133 VirtualKeyboardType before = DecideKeyboardStateForView(focused_before); | |
134 VirtualKeyboardType now = DecideKeyboardStateForView(focused_now); | |
135 if (before != now) { | |
136 // TODO(varunjain): support other types of keyboard. | |
137 UpdateKeyboardAndLayout(now == GENERIC); | |
138 } | |
139 } | |
140 | |
141 /////////////////////////////////////////////////////////////////////////////// | |
142 // TouchBrowserFrameView, protected: | |
143 | |
144 int TouchBrowserFrameView::GetReservedHeight() const { | |
145 return keyboard_showing_ ? keyboard_height_ : 0; | |
146 } | |
147 | |
148 void TouchBrowserFrameView::ViewHierarchyChanged(bool is_add, | 56 void TouchBrowserFrameView::ViewHierarchyChanged(bool is_add, |
149 View* parent, | 57 View* parent, |
150 View* child) { | 58 View* child) { |
151 OpaqueBrowserFrameView::ViewHierarchyChanged(is_add, parent, child); | 59 OpaqueBrowserFrameView::ViewHierarchyChanged(is_add, parent, child); |
152 if (!GetFocusManager()) | 60 if (!GetFocusManager()) |
153 return; | 61 return; |
154 | 62 |
155 if (is_add && !focus_listener_added_) { | 63 if (is_add && !focus_listener_added_) { |
156 // Add focus listener when this view is added to the hierarchy. | 64 // Add focus listener when this view is added to the hierarchy. |
157 GetFocusManager()->AddFocusChangeListener(this); | 65 GetFocusManager()->AddFocusChangeListener(this); |
158 focus_listener_added_ = true; | 66 focus_listener_added_ = true; |
159 } else if (!is_add && focus_listener_added_) { | 67 } else if (!is_add && focus_listener_added_) { |
160 // Remove focus listener when this view is removed from the hierarchy. | 68 // Remove focus listener when this view is removed from the hierarchy. |
161 GetFocusManager()->RemoveFocusChangeListener(this); | 69 GetFocusManager()->RemoveFocusChangeListener(this); |
162 focus_listener_added_ = false; | 70 focus_listener_added_ = false; |
163 } | 71 } |
164 } | 72 } |
165 | 73 |
166 /////////////////////////////////////////////////////////////////////////////// | |
167 // TouchBrowserFrameView, private: | |
168 | |
169 void TouchBrowserFrameView::InitVirtualKeyboard() { | |
170 if (keyboard_) | |
171 return; | |
172 | |
173 Profile* keyboard_profile = browser_view()->browser()->profile(); | |
174 DCHECK(keyboard_profile) << "Profile required for virtual keyboard."; | |
175 | |
176 keyboard_ = new KeyboardContainerView(keyboard_profile, | |
177 browser_view()->browser(), | |
178 url_); | |
179 keyboard_->SetVisible(false); | |
180 AddChildView(keyboard_); | |
181 } | |
182 | |
183 void TouchBrowserFrameView::UpdateKeyboardAndLayout(bool should_show_keyboard) { | |
184 if (should_show_keyboard) | |
185 InitVirtualKeyboard(); | |
186 | |
187 if (should_show_keyboard == keyboard_showing_) | |
188 return; | |
189 | |
190 DCHECK(keyboard_); | |
191 | |
192 keyboard_showing_ = should_show_keyboard; | |
193 if (keyboard_showing_) { | |
194 // We don't re-layout the client view until the animation ends (see | |
195 // AnimationEnded below) because we want the client view to occupy the | |
196 // entire height during the animation. It is necessary to reset the | |
197 // transform for the keyboard first so that the contents are sized properly | |
198 // when layout, and then start the animation. | |
199 ui::Transform reset; | |
200 keyboard_->SetTransform(reset); | |
201 Layout(); | |
202 | |
203 animation_->Show(); | |
204 } else { | |
205 animation_->Hide(); | |
206 | |
207 browser_view()->set_clip_y(ui::Tween::ValueBetween( | |
208 animation_->GetCurrentValue(), 0, keyboard_height_)); | |
209 parent()->Layout(); | |
210 } | |
211 } | |
212 | |
213 TouchBrowserFrameView::VirtualKeyboardType | |
214 TouchBrowserFrameView::DecideKeyboardStateForView(views::View* view) { | |
215 if (!view) | |
216 return NONE; | |
217 | |
218 std::string cname = view->GetClassName(); | |
219 if (cname == views::Textfield::kViewClassName) { | |
220 return GENERIC; | |
221 } else if (cname == RenderWidgetHostViewViews::kViewClassName) { | |
222 TabContents* contents = browser_view()->browser()->GetSelectedTabContents(); | |
223 bool* editable = contents ? GetFocusedStateAccessor()->GetProperty( | |
224 contents->property_bag()) : NULL; | |
225 if (editable && *editable) | |
226 return GENERIC; | |
227 } | |
228 return NONE; | |
229 } | |
230 | |
231 bool TouchBrowserFrameView::HitTest(const gfx::Point& point) const { | 74 bool TouchBrowserFrameView::HitTest(const gfx::Point& point) const { |
232 if (OpaqueBrowserFrameView::HitTest(point)) | 75 if (OpaqueBrowserFrameView::HitTest(point)) |
233 return true; | 76 return true; |
234 | 77 |
235 if (close_button()->IsVisible() && | 78 if (close_button()->IsVisible() && |
236 close_button()->GetMirroredBounds().Contains(point)) | 79 close_button()->GetMirroredBounds().Contains(point)) |
237 return true; | 80 return true; |
238 if (restore_button()->IsVisible() && | 81 if (restore_button()->IsVisible() && |
239 restore_button()->GetMirroredBounds().Contains(point)) | 82 restore_button()->GetMirroredBounds().Contains(point)) |
240 return true; | 83 return true; |
241 if (maximize_button()->IsVisible() && | 84 if (maximize_button()->IsVisible() && |
242 maximize_button()->GetMirroredBounds().Contains(point)) | 85 maximize_button()->GetMirroredBounds().Contains(point)) |
243 return true; | 86 return true; |
244 if (minimize_button()->IsVisible() && | 87 if (minimize_button()->IsVisible() && |
245 minimize_button()->GetMirroredBounds().Contains(point)) | 88 minimize_button()->GetMirroredBounds().Contains(point)) |
246 return true; | 89 return true; |
247 | 90 |
248 return false; | 91 return false; |
249 } | 92 } |
250 | |
251 void TouchBrowserFrameView::ActiveTabChanged(TabContentsWrapper* old_contents, | |
252 TabContentsWrapper* new_contents, | |
253 int index, | |
254 bool user_gesture) { | |
255 TabContents* contents = new_contents->tab_contents(); | |
256 if (!TabContentsHasFocus(contents)) | |
257 return; | |
258 | |
259 bool* editable = GetFocusedStateAccessor()->GetProperty( | |
260 contents->property_bag()); | |
261 UpdateKeyboardAndLayout(editable ? *editable : false); | |
262 } | |
263 | |
264 void TouchBrowserFrameView::TabStripEmpty() { | |
265 if (animation_->is_animating()) { | |
266 // Reset the delegate so the AnimationEnded callback doesn't trigger. | |
267 animation_->set_delegate(NULL); | |
268 animation_->Stop(); | |
269 } | |
270 } | |
271 | |
272 void TouchBrowserFrameView::Observe(int type, | |
273 const NotificationSource& source, | |
274 const NotificationDetails& details) { | |
275 Browser* browser = browser_view()->browser(); | |
276 if (type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE) { | |
277 // Only modify the keyboard state if the currently active tab sent the | |
278 // notification. | |
279 const TabContents* current_tab = browser->GetSelectedTabContents(); | |
280 TabContents* source_tab = Source<TabContents>(source).ptr(); | |
281 const bool editable = *Details<const bool>(details).ptr(); | |
282 | |
283 if (current_tab == source_tab && TabContentsHasFocus(source_tab)) | |
284 UpdateKeyboardAndLayout(editable); | |
285 | |
286 // Save the state of the focused field so that the keyboard visibility | |
287 // can be determined after tab switching. | |
288 GetFocusedStateAccessor()->SetProperty( | |
289 source_tab->property_bag(), editable); | |
290 } else if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) { | |
291 NavigationController* controller = | |
292 Source<NavigationController>(source).ptr(); | |
293 Browser* source_browser = Browser::GetBrowserForController( | |
294 controller, NULL); | |
295 | |
296 // If the Browser for the keyboard has navigated, re-evaluate the visibility | |
297 // of the keyboard. | |
298 TouchBrowserFrameView::VirtualKeyboardType keyboard_type = NONE; | |
299 views::View* view = GetFocusManager()->GetFocusedView(); | |
300 if (view) { | |
301 if (view->GetClassName() == views::Textfield::kViewClassName) | |
302 keyboard_type = GENERIC; | |
303 if (view->GetClassName() == RenderWidgetHostViewViews::kViewClassName) { | |
304 // Reset the state of the focused field in the current tab. | |
305 GetFocusedStateAccessor()->SetProperty( | |
306 controller->tab_contents()->property_bag(), false); | |
307 } | |
308 } | |
309 if (source_browser == browser) | |
310 UpdateKeyboardAndLayout(keyboard_type == GENERIC); | |
311 } else if (type == content::NOTIFICATION_TAB_CONTENTS_DESTROYED) { | |
312 GetFocusedStateAccessor()->DeleteProperty( | |
313 Source<TabContents>(source).ptr()->property_bag()); | |
314 } else if (type == chrome::NOTIFICATION_PREF_CHANGED) { | |
315 OpaqueBrowserFrameView::Observe(type, source, details); | |
316 } else if (type == chrome::NOTIFICATION_HIDE_KEYBOARD_INVOKED) { | |
317 TabContents* tab_contents = | |
318 browser_view()->browser()->GetSelectedTabContents(); | |
319 if (tab_contents) { | |
320 GetFocusedStateAccessor()->SetProperty(tab_contents->property_bag(), | |
321 false); | |
322 } | |
323 UpdateKeyboardAndLayout(false); | |
324 } else if (type == chrome::NOTIFICATION_SET_KEYBOARD_HEIGHT_INVOKED) { | |
325 // TODO(penghuang) Allow extension conrtol the virtual keyboard directly | |
326 // instead of using Notification. | |
327 int height = *reinterpret_cast<int*>(details.map_key()); | |
328 if (height != keyboard_height_) { | |
329 DCHECK_GE(height, 0) << "Height of the keyboard is less than 0."; | |
330 DCHECK_LE(height, View::height()) << "Height of the keyboard is greater " | |
331 "than the height of frame view."; | |
332 keyboard_height_ = height; | |
333 parent()->Layout(); | |
334 } | |
335 } else if (type == chrome::NOTIFICATION_EDITABLE_ELEMENT_TOUCHED) { | |
336 UpdateKeyboardAndLayout(true); | |
337 } | |
338 } | |
339 | |
340 /////////////////////////////////////////////////////////////////////////////// | |
341 // ui::AnimationDelegate implementation | |
342 void TouchBrowserFrameView::AnimationProgressed(const ui::Animation* anim) { | |
343 ui::Transform transform; | |
344 transform.SetTranslateY( | |
345 ui::Tween::ValueBetween(anim->GetCurrentValue(), keyboard_height_, 0)); | |
346 keyboard_->SetTransform(transform); | |
347 browser_view()->set_clip_y( | |
348 ui::Tween::ValueBetween(anim->GetCurrentValue(), 0, keyboard_height_)); | |
349 SchedulePaint(); | |
350 } | |
351 | |
352 void TouchBrowserFrameView::AnimationEnded(const ui::Animation* animation) { | |
353 browser_view()->set_clip_y(0); | |
354 if (keyboard_showing_) { | |
355 // Because the NonClientFrameView is a sibling of the ClientView, we rely on | |
356 // the parent to resize the ClientView instead of resizing it directly. | |
357 parent()->Layout(); | |
358 | |
359 // The keyboard that pops up may end up hiding the text entry. So make sure | |
360 // the renderer scrolls when necessary to keep the textfield visible. | |
361 RenderViewHost* host = | |
362 browser_view()->browser()->GetSelectedTabContents()->render_view_host(); | |
363 host->Send(new ViewMsg_ScrollFocusedEditableNodeIntoView( | |
364 host->routing_id())); | |
365 } else { | |
366 // Notify the keyboard that it is hidden now. | |
367 keyboard_->SetVisible(false); | |
368 } | |
369 SchedulePaint(); | |
370 } | |
371 | |
372 #if defined(OS_CHROMEOS) | |
373 void TouchBrowserFrameView::VirtualKeyboardChanged( | |
374 chromeos::input_method::InputMethodManager* manager, | |
375 const chromeos::input_method::VirtualKeyboard& virtual_keyboard, | |
376 const std::string& virtual_keyboard_layout) { | |
377 const GURL& new_url = | |
378 virtual_keyboard.GetURLForLayout(virtual_keyboard_layout); | |
379 if (new_url == url_) | |
380 return; | |
381 | |
382 url_ = new_url; | |
383 if (keyboard_) | |
384 keyboard_->LoadURL(url_); | |
385 // |keyboard_| can be NULL here. In that case, the |url_| will be used in | |
386 // InitVirtualKeyboard() later. | |
387 } | |
388 #endif | |
OLD | NEW |