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