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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/touch/keyboard/keyboard_manager.cc
diff --git a/chrome/browser/ui/touch/keyboard/keyboard_manager.cc b/chrome/browser/ui/touch/keyboard/keyboard_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c21f20522b46f2e308063ddc7c36b8f4e817b3d9
--- /dev/null
+++ b/chrome/browser/ui/touch/keyboard/keyboard_manager.cc
@@ -0,0 +1,366 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/touch/keyboard/keyboard_manager.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
+#include "chrome/browser/ui/views/dom_view.h"
+#include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/site_instance.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/common/notification_service.h"
+#include "ui/base/animation/slide_animation.h"
+#include "ui/base/ime/text_input_type.h"
+#include "ui/gfx/interpolated_transform.h"
+#include "views/ime/text_input_client.h"
+#include "views/widget/widget.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/input_method/virtual_keyboard_selector.h"
+#endif
+
+namespace {
+
+const int kDefaultKeyboardHeight = 300;
+const int kKeyboardSlideDuration = 300; // In milliseconds
+
+PropertyAccessor<bool>* GetFocusedStateAccessor() {
+ static PropertyAccessor<bool> state;
+ return &state;
+}
+
+bool TabContentsCanAffectKeyboard(const TabContents* tab_contents) {
sky 2011/07/06 15:08:57 Add a description.
sadrul 2011/07/06 15:38:35 Done.
+ // There may not be a browser, e.g. for the login window. But if there is
+ // a browser, then |tab_contents| should be the active tab.
+ Browser* browser = Browser::GetBrowserForController(
+ &tab_contents->controller(), NULL);
+ return browser == NULL ||
+ (browser == BrowserList::GetLastActive() &&
+ browser->GetSelectedTabContents() == tab_contents);
+}
+
+} // namespace
+
+// TODO(sad): Is the default profile always going to be the one we want?
+
+KeyboardManager::KeyboardManager()
+ : views::Widget::Widget(),
+ dom_view_(new DOMView),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ extension_dispatcher_(ProfileManager::GetDefaultProfile(), this)),
+ target_(NULL),
+ keyboard_height_(kDefaultKeyboardHeight) {
+
+ // Initialize the widget first.
+ views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
+ params.transparent = true;
+ Init(params);
+
+ // Setup the DOM view to host the keyboard.
+ Profile* profile = ProfileManager::GetDefaultProfile();
+ GURL keyboard_url(chrome::kChromeUIKeyboardURL);
+ dom_view_->Init(profile,
+ SiteInstance::CreateSiteInstanceForURL(profile, keyboard_url));
+ dom_view_->LoadURL(keyboard_url);
+ dom_view_->SetVisible(true);
+ SetContentsView(dom_view_);
+
+ // Setup observer so the events from the keyboard can be handled.
+ TabContentsObserver::Observe(dom_view_->tab_contents());
+
+ // Initialize the animation.
+ animation_.reset(new ui::SlideAnimation(this));
+ animation_->SetTweenType(ui::Tween::LINEAR);
+ animation_->SetSlideDuration(kKeyboardSlideDuration);
+
+ // Start listening to notifications to maintain the keyboard visibility, size
+ // etc.
+ registrar_.Add(this,
+ NotificationType::NAV_ENTRY_COMMITTED,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::FOCUS_CHANGED_IN_PAGE,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::TAB_CONTENTS_DESTROYED,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::HIDE_KEYBOARD_INVOKED,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::SET_KEYBOARD_HEIGHT_INVOKED,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::EDITABLE_ELEMENT_TOUCHED,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::BROWSER_OPENED,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::BROWSER_CLOSING,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::APP_EXITING,
+ NotificationService::AllSources());
+
+#if defined(OS_CHROMEOS)
+ chromeos::input_method::InputMethodManager* manager =
+ chromeos::input_method::InputMethodManager::GetInstance();
+ manager->AddVirtualKeyboardObserver(this);
+#endif
+}
+
+KeyboardManager::~KeyboardManager() {
+#if defined(OS_CHROMEOS)
+ chromeos::input_method::InputMethodManager* manager =
+ chromeos::input_method::InputMethodManager::GetInstance();
+ manager->RemoveVirtualKeyboardObserver(this);
+#endif
+ // TODO(sad): Do anything else?
+}
+
+void KeyboardManager::ShowKeyboardForWidget(views::Widget* widget) {
+ target_ = widget;
+ // TODO(sad): There needs to be some way to:
+ // - reset |target_| when it is destroyed
+ // - make |target_| the parent of this Widget
+
+ gfx::Rect rect = target_->GetWindowScreenBounds();
+ rect.set_y(rect.y() + rect.height() - keyboard_height_);
+ rect.set_height(keyboard_height_);
+ SetBounds(rect);
+
+ transform_.reset(new ui::InterpolatedTranslation(
+ gfx::Point(0, keyboard_height_), gfx::Point()));
+
+ GetRootView()->SetTransform(
+ transform_->Interpolate(animation_->GetCurrentValue()));
+ animation_->Show();
+
+ MoveToTop();
+ Show();
+}
+
+void KeyboardManager::Hide() {
+ animation_->Hide();
+}
+
+bool KeyboardManager::OnKeyEvent(const views::KeyEvent& event) {
+ return target_ ? target_->OnKeyEvent(event) : false;
+}
+
+void KeyboardManager::AnimationProgressed(const ui::Animation* animation) {
+ GetRootView()->SetTransform(
+ transform_->Interpolate(animation_->GetCurrentValue()));
+}
+
+void KeyboardManager::AnimationEnded(const ui::Animation* animation) {
+ if (animation_->GetCurrentValue() < 0.01)
+ Widget::Hide();
+}
+
+bool KeyboardManager::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(KeyboardManager, message)
+ IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void KeyboardManager::OnRequest(
+ const ExtensionHostMsg_Request_Params& request) {
+ extension_dispatcher_.Dispatch(request,
+ dom_view_->tab_contents()->render_view_host());
+}
+
+void KeyboardManager::ActiveTabChanged(TabContentsWrapper* old_contents,
+ TabContentsWrapper* new_contents,
+ int index,
+ bool user_gesture) {
+ TabContents* contents = new_contents->tab_contents();
+ if (!TabContentsCanAffectKeyboard(contents))
+ return;
+
+ // If the tab contents does not have the focus, then it should not affect the
+ // keyboard visibility.
+ views::View* view = static_cast<TabContentsViewTouch*>(contents->view());
+ views::FocusManager* fmanager = view ? view->GetFocusManager() : NULL;
+ if (!fmanager || !view->Contains(fmanager->GetFocusedView()))
+ return;
+
+ bool* editable = GetFocusedStateAccessor()->GetProperty(
+ contents->property_bag());
+ if (editable && *editable)
+ ShowKeyboardForWidget(view->GetWidget());
+ else
+ Hide();
+}
+
+Browser* KeyboardManager::GetBrowser() {
+ // 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.
+ // SendKeyboardEventInputFunction::GetTopLevelWidget to somehow interact with
+ // the WM to find the top level widget?
+ return BrowserList::FindTabbedBrowser(
+ ProfileManager::GetDefaultProfile(), true);
+}
+
+gfx::NativeView KeyboardManager::GetNativeViewOfHost() {
+ return dom_view_->native_view();
+}
+
+TabContents* KeyboardManager::GetAssociatedTabContents() const {
+ return dom_view_->tab_contents();
+}
+
+#if defined(OS_CHROMEOS)
+void KeyboardManager::VirtualKeyboardChanged(
+ chromeos::input_method::InputMethodManager* manager,
+ const chromeos::input_method::VirtualKeyboard& virtual_keyboard,
+ const std::string& virtual_keyboard_layout) {
+ const GURL& url = virtual_keyboard.GetURLForLayout(virtual_keyboard_layout);
+ dom_view_->LoadURL(url);
+ VLOG(1) << "VirtualKeyboardChanged: Switched to " << url.spec();
+}
+#endif
+
+void KeyboardManager::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::NAV_ENTRY_COMMITTED: {
+ // When a navigation happens, we want to hide the keyboard if the focus is
+ // in the web-page. Otherwise, the keyboard visibility should not change.
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ TabContents* tab_contents = controller->tab_contents();
+ GetFocusedStateAccessor()->SetProperty(tab_contents->property_bag(),
+ false);
+ if (!TabContentsCanAffectKeyboard(tab_contents))
+ break;
+
+ TabContentsViewTouch* view =
+ static_cast<TabContentsViewTouch*>(tab_contents->view());
+ views::View* focused = view->GetFocusManager()->GetFocusedView();
+ views::TextInputClient* input =
+ focused ? focused->GetTextInputClient() : NULL;
+ // Show the keyboard if the focused view supports text-input.
+ if (input && input->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE)
+ ShowKeyboardForWidget(focused->GetWidget());
+ else
+ Hide();
+ break;
+ }
+
+ case NotificationType::FOCUS_CHANGED_IN_PAGE: {
+ // If the focus in the page moved to an editable field, then the keyboard
+ // should be visible, otherwise not.
+ TabContents* tab_contents = Source<TabContents>(source).ptr();
+ const bool editable = *Details<const bool>(details).ptr();
+ GetFocusedStateAccessor()->SetProperty(tab_contents->property_bag(),
+ editable);
+ if (!TabContentsCanAffectKeyboard(tab_contents))
+ break;
+
+ if (editable) {
+ TabContentsViewTouch* view =
+ static_cast<TabContentsViewTouch*>(tab_contents->view());
+ ShowKeyboardForWidget(view->GetWidget());
+ } else {
+ Hide();
+ }
+
+ break;
+ }
+
+ case NotificationType::TAB_CONTENTS_DESTROYED: {
+ // Tab content was destroyed. Forget everything about it.
+ GetFocusedStateAccessor()->DeleteProperty(
+ Source<TabContents>(source).ptr()->property_bag());
+ break;
+ }
+
+ case NotificationType::HIDE_KEYBOARD_INVOKED: {
+ // The keyboard is hiding itself.
+ Browser* browser = BrowserList::GetLastActive();
+ if (browser) {
+ TabContents* tab_contents = browser->GetSelectedTabContents();
+ if (tab_contents) {
+ GetFocusedStateAccessor()->SetProperty(tab_contents->property_bag(),
+ false);
+ }
+ }
+
+ Hide();
+ break;
+ }
+
+ case NotificationType::SET_KEYBOARD_HEIGHT_INVOKED: {
+ // The keyboard is resizing itself.
+
+ // TODO(penghuang) Allow extension conrtol the virtual keyboard directly
+ // instead of using Notification.
+ int height = *Details<int>(details).ptr();
+ if (height != keyboard_height_) {
+ DCHECK_GE(height, 0) << "Keyboard height should not be negative.";
+
+ keyboard_height_ = height;
+ gfx::Size size = GetWindowScreenBounds().size();
+ size.set_height(keyboard_height_);
+ SetSize(size);
+
+ // TODO(sad): Notify the target widget that the size has changed so it
+ // can update its display accordingly if it wanted to.
+ }
+ break;
+ }
+
+ case NotificationType::EDITABLE_ELEMENT_TOUCHED: {
+ // In case the keyboard hid itself and the focus is still in an editable
+ // field, and the user touches the field, then we want to show the
+ // keyboard again.
+ views::View* src = Source<views::View>(source).ptr();
+ ShowKeyboardForWidget(src->GetWidget());
+ break;
+ }
+
+ 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.
+ // A tabstrip observer is installed for the new Browser so that the
+ // keyboard can be updated when the selected tab changes in the Browser.
+ Browser* browser = Source<Browser>(source).ptr();
+ browser->tabstrip_model()->AddObserver(this);
+ break;
+ }
+
+ case NotificationType::BROWSER_CLOSING: {
+ // Remove the tabstrip observer.
+ Browser* browser = Source<Browser>(source).ptr();
+ browser->tabstrip_model()->RemoveObserver(this);
+ break;
+ }
+
+ case NotificationType::APP_EXITING: {
+ // Ideally KeyboardManager would Close itself, but that ends up destroying
+ // the singleton object, which causes a crash when all the singleton
+ // objects are destroyed from the AtExitManager. So just destroy the
+ // RootView here.
+ DestroyRootView();
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+// static
+KeyboardManager* KeyboardManager::GetInstance() {
+ return Singleton<KeyboardManager>::get();
+}

Powered by Google App Engine
This is Rietveld 408576698