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

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

Powered by Google App Engine
This is Rietveld 408576698