Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(202)

Side by Side Diff: chrome/browser/ui/touch/keyboard/keyboard_manager.cc

Issue 7302015: A keyboard widget that manages itself (the animation and all that). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/keyboard/keyboard_manager.h"
6
7 #include "chrome/browser/profiles/profile.h"
8 #include "chrome/browser/profiles/profile_manager.h"
9 #include "chrome/browser/tabs/tab_strip_model.h"
10 #include "chrome/browser/ui/browser_list.h"
11 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
12 #include "chrome/browser/ui/views/dom_view.h"
13 #include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
14 #include "chrome/common/extensions/extension_messages.h"
15 #include "chrome/common/url_constants.h"
16 #include "content/browser/site_instance.h"
17 #include "content/browser/tab_contents/tab_contents.h"
18 #include "content/common/notification_service.h"
19 #include "ui/base/animation/slide_animation.h"
20 #include "ui/base/ime/text_input_type.h"
21 #include "ui/gfx/interpolated_transform.h"
22 #include "views/ime/text_input_client.h"
23 #include "views/widget/widget.h"
24
25 #if defined(OS_CHROMEOS)
26 #include "chrome/browser/chromeos/input_method/virtual_keyboard_selector.h"
27 #endif
28
29 namespace {
30
31 const int kDefaultKeyboardHeight = 300;
32 const int kKeyboardSlideDuration = 300; // In milliseconds
33
34 PropertyAccessor<bool>* GetFocusedStateAccessor() {
35 static PropertyAccessor<bool> state;
36 return &state;
37 }
38
39 bool TabContentsCanAffectKeyboard(const TabContents* tab_contents) {
sky 2011/07/06 15:08:57 Add a description.
sadrul 2011/07/06 15:38:35 Done.
40 // There may not be a browser, e.g. for the login window. But if there is
41 // a browser, then |tab_contents| should be the active tab.
42 Browser* browser = Browser::GetBrowserForController(
43 &tab_contents->controller(), NULL);
44 return browser == NULL ||
45 (browser == BrowserList::GetLastActive() &&
46 browser->GetSelectedTabContents() == tab_contents);
47 }
48
49 } // namespace
50
51 // TODO(sad): Is the default profile always going to be the one we want?
52
53 KeyboardManager::KeyboardManager()
54 : views::Widget::Widget(),
55 dom_view_(new DOMView),
56 ALLOW_THIS_IN_INITIALIZER_LIST(
57 extension_dispatcher_(ProfileManager::GetDefaultProfile(), this)),
58 target_(NULL),
59 keyboard_height_(kDefaultKeyboardHeight) {
60
61 // Initialize the widget first.
62 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
63 params.transparent = true;
64 Init(params);
65
66 // Setup the DOM view to host the keyboard.
67 Profile* profile = ProfileManager::GetDefaultProfile();
68 GURL keyboard_url(chrome::kChromeUIKeyboardURL);
69 dom_view_->Init(profile,
70 SiteInstance::CreateSiteInstanceForURL(profile, keyboard_url));
71 dom_view_->LoadURL(keyboard_url);
72 dom_view_->SetVisible(true);
73 SetContentsView(dom_view_);
74
75 // Setup observer so the events from the keyboard can be handled.
76 TabContentsObserver::Observe(dom_view_->tab_contents());
77
78 // Initialize the animation.
79 animation_.reset(new ui::SlideAnimation(this));
80 animation_->SetTweenType(ui::Tween::LINEAR);
81 animation_->SetSlideDuration(kKeyboardSlideDuration);
82
83 // Start listening to notifications to maintain the keyboard visibility, size
84 // etc.
85 registrar_.Add(this,
86 NotificationType::NAV_ENTRY_COMMITTED,
87 NotificationService::AllSources());
88 registrar_.Add(this,
89 NotificationType::FOCUS_CHANGED_IN_PAGE,
90 NotificationService::AllSources());
91 registrar_.Add(this,
92 NotificationType::TAB_CONTENTS_DESTROYED,
93 NotificationService::AllSources());
94 registrar_.Add(this,
95 NotificationType::HIDE_KEYBOARD_INVOKED,
96 NotificationService::AllSources());
97 registrar_.Add(this,
98 NotificationType::SET_KEYBOARD_HEIGHT_INVOKED,
99 NotificationService::AllSources());
100 registrar_.Add(this,
101 NotificationType::EDITABLE_ELEMENT_TOUCHED,
102 NotificationService::AllSources());
103 registrar_.Add(this,
104 NotificationType::BROWSER_OPENED,
105 NotificationService::AllSources());
106 registrar_.Add(this,
107 NotificationType::BROWSER_CLOSING,
108 NotificationService::AllSources());
109 registrar_.Add(this,
110 NotificationType::APP_EXITING,
111 NotificationService::AllSources());
112
113 #if defined(OS_CHROMEOS)
114 chromeos::input_method::InputMethodManager* manager =
115 chromeos::input_method::InputMethodManager::GetInstance();
116 manager->AddVirtualKeyboardObserver(this);
117 #endif
118 }
119
120 KeyboardManager::~KeyboardManager() {
121 #if defined(OS_CHROMEOS)
122 chromeos::input_method::InputMethodManager* manager =
123 chromeos::input_method::InputMethodManager::GetInstance();
124 manager->RemoveVirtualKeyboardObserver(this);
125 #endif
126 // TODO(sad): Do anything else?
127 }
128
129 void KeyboardManager::ShowKeyboardForWidget(views::Widget* widget) {
130 target_ = widget;
131 // TODO(sad): There needs to be some way to:
132 // - reset |target_| when it is destroyed
133 // - make |target_| the parent of this Widget
134
135 gfx::Rect rect = target_->GetWindowScreenBounds();
136 rect.set_y(rect.y() + rect.height() - keyboard_height_);
137 rect.set_height(keyboard_height_);
138 SetBounds(rect);
139
140 transform_.reset(new ui::InterpolatedTranslation(
141 gfx::Point(0, keyboard_height_), gfx::Point()));
142
143 GetRootView()->SetTransform(
144 transform_->Interpolate(animation_->GetCurrentValue()));
145 animation_->Show();
146
147 MoveToTop();
148 Show();
149 }
150
151 void KeyboardManager::Hide() {
152 animation_->Hide();
153 }
154
155 bool KeyboardManager::OnKeyEvent(const views::KeyEvent& event) {
156 return target_ ? target_->OnKeyEvent(event) : false;
157 }
158
159 void KeyboardManager::AnimationProgressed(const ui::Animation* animation) {
160 GetRootView()->SetTransform(
161 transform_->Interpolate(animation_->GetCurrentValue()));
162 }
163
164 void KeyboardManager::AnimationEnded(const ui::Animation* animation) {
165 if (animation_->GetCurrentValue() < 0.01)
166 Widget::Hide();
167 }
168
169 bool KeyboardManager::OnMessageReceived(const IPC::Message& message) {
170 bool handled = true;
171 IPC_BEGIN_MESSAGE_MAP(KeyboardManager, message)
172 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
173 IPC_MESSAGE_UNHANDLED(handled = false)
174 IPC_END_MESSAGE_MAP()
175 return handled;
176 }
177
178 void KeyboardManager::OnRequest(
179 const ExtensionHostMsg_Request_Params& request) {
180 extension_dispatcher_.Dispatch(request,
181 dom_view_->tab_contents()->render_view_host());
182 }
183
184 void KeyboardManager::ActiveTabChanged(TabContentsWrapper* old_contents,
185 TabContentsWrapper* new_contents,
186 int index,
187 bool user_gesture) {
188 TabContents* contents = new_contents->tab_contents();
189 if (!TabContentsCanAffectKeyboard(contents))
190 return;
191
192 // If the tab contents does not have the focus, then it should not affect the
193 // keyboard visibility.
194 views::View* view = static_cast<TabContentsViewTouch*>(contents->view());
195 views::FocusManager* fmanager = view ? view->GetFocusManager() : NULL;
196 if (!fmanager || !view->Contains(fmanager->GetFocusedView()))
197 return;
198
199 bool* editable = GetFocusedStateAccessor()->GetProperty(
200 contents->property_bag());
201 if (editable && *editable)
202 ShowKeyboardForWidget(view->GetWidget());
203 else
204 Hide();
205 }
206
207 Browser* KeyboardManager::GetBrowser() {
208 // TODO(sad): Find a better way. Perhaps just return NULL, and fix
sky 2011/07/06 15:08:57 Do you want BrowserList::GetLastActive here?
sadrul 2011/07/06 15:38:35 Cool. I think that'd be better. Done.
209 // SendKeyboardEventInputFunction::GetTopLevelWidget to somehow interact with
210 // the WM to find the top level widget?
211 return BrowserList::FindTabbedBrowser(
212 ProfileManager::GetDefaultProfile(), true);
213 }
214
215 gfx::NativeView KeyboardManager::GetNativeViewOfHost() {
216 return dom_view_->native_view();
217 }
218
219 TabContents* KeyboardManager::GetAssociatedTabContents() const {
220 return dom_view_->tab_contents();
221 }
222
223 #if defined(OS_CHROMEOS)
224 void KeyboardManager::VirtualKeyboardChanged(
225 chromeos::input_method::InputMethodManager* manager,
226 const chromeos::input_method::VirtualKeyboard& virtual_keyboard,
227 const std::string& virtual_keyboard_layout) {
228 const GURL& url = virtual_keyboard.GetURLForLayout(virtual_keyboard_layout);
229 dom_view_->LoadURL(url);
230 VLOG(1) << "VirtualKeyboardChanged: Switched to " << url.spec();
231 }
232 #endif
233
234 void KeyboardManager::Observe(NotificationType type,
235 const NotificationSource& source,
236 const NotificationDetails& details) {
237 switch (type.value) {
238 case NotificationType::NAV_ENTRY_COMMITTED: {
239 // When a navigation happens, we want to hide the keyboard if the focus is
240 // in the web-page. Otherwise, the keyboard visibility should not change.
241 NavigationController* controller =
242 Source<NavigationController>(source).ptr();
243 TabContents* tab_contents = controller->tab_contents();
244 GetFocusedStateAccessor()->SetProperty(tab_contents->property_bag(),
245 false);
246 if (!TabContentsCanAffectKeyboard(tab_contents))
247 break;
248
249 TabContentsViewTouch* view =
250 static_cast<TabContentsViewTouch*>(tab_contents->view());
251 views::View* focused = view->GetFocusManager()->GetFocusedView();
252 views::TextInputClient* input =
253 focused ? focused->GetTextInputClient() : NULL;
254 // Show the keyboard if the focused view supports text-input.
255 if (input && input->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE)
256 ShowKeyboardForWidget(focused->GetWidget());
257 else
258 Hide();
259 break;
260 }
261
262 case NotificationType::FOCUS_CHANGED_IN_PAGE: {
263 // If the focus in the page moved to an editable field, then the keyboard
264 // should be visible, otherwise not.
265 TabContents* tab_contents = Source<TabContents>(source).ptr();
266 const bool editable = *Details<const bool>(details).ptr();
267 GetFocusedStateAccessor()->SetProperty(tab_contents->property_bag(),
268 editable);
269 if (!TabContentsCanAffectKeyboard(tab_contents))
270 break;
271
272 if (editable) {
273 TabContentsViewTouch* view =
274 static_cast<TabContentsViewTouch*>(tab_contents->view());
275 ShowKeyboardForWidget(view->GetWidget());
276 } else {
277 Hide();
278 }
279
280 break;
281 }
282
283 case NotificationType::TAB_CONTENTS_DESTROYED: {
284 // Tab content was destroyed. Forget everything about it.
285 GetFocusedStateAccessor()->DeleteProperty(
286 Source<TabContents>(source).ptr()->property_bag());
287 break;
288 }
289
290 case NotificationType::HIDE_KEYBOARD_INVOKED: {
291 // The keyboard is hiding itself.
292 Browser* browser = BrowserList::GetLastActive();
293 if (browser) {
294 TabContents* tab_contents = browser->GetSelectedTabContents();
295 if (tab_contents) {
296 GetFocusedStateAccessor()->SetProperty(tab_contents->property_bag(),
297 false);
298 }
299 }
300
301 Hide();
302 break;
303 }
304
305 case NotificationType::SET_KEYBOARD_HEIGHT_INVOKED: {
306 // The keyboard is resizing itself.
307
308 // TODO(penghuang) Allow extension conrtol the virtual keyboard directly
309 // instead of using Notification.
310 int height = *Details<int>(details).ptr();
311 if (height != keyboard_height_) {
312 DCHECK_GE(height, 0) << "Keyboard height should not be negative.";
313
314 keyboard_height_ = height;
315 gfx::Size size = GetWindowScreenBounds().size();
316 size.set_height(keyboard_height_);
317 SetSize(size);
318
319 // TODO(sad): Notify the target widget that the size has changed so it
320 // can update its display accordingly if it wanted to.
321 }
322 break;
323 }
324
325 case NotificationType::EDITABLE_ELEMENT_TOUCHED: {
326 // In case the keyboard hid itself and the focus is still in an editable
327 // field, and the user touches the field, then we want to show the
328 // keyboard again.
329 views::View* src = Source<views::View>(source).ptr();
330 ShowKeyboardForWidget(src->GetWidget());
331 break;
332 }
333
334 case NotificationType::BROWSER_OPENED: {
sky 2011/07/06 15:08:57 I think KeyboardManager should be a BrowserList::O
sadrul 2011/07/06 15:38:35 Ah, nice. Didn't know about BrowserList::Observer.
335 // A tabstrip observer is installed for the new Browser so that the
336 // keyboard can be updated when the selected tab changes in the Browser.
337 Browser* browser = Source<Browser>(source).ptr();
338 browser->tabstrip_model()->AddObserver(this);
339 break;
340 }
341
342 case NotificationType::BROWSER_CLOSING: {
343 // Remove the tabstrip observer.
344 Browser* browser = Source<Browser>(source).ptr();
345 browser->tabstrip_model()->RemoveObserver(this);
346 break;
347 }
348
349 case NotificationType::APP_EXITING: {
350 // Ideally KeyboardManager would Close itself, but that ends up destroying
351 // the singleton object, which causes a crash when all the singleton
352 // objects are destroyed from the AtExitManager. So just destroy the
353 // RootView here.
354 DestroyRootView();
355 break;
356 }
357
358 default:
359 NOTREACHED();
360 }
361 }
362
363 // static
364 KeyboardManager* KeyboardManager::GetInstance() {
365 return Singleton<KeyboardManager>::get();
366 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698