1// Copyright (c) 2013 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 "ui/keyboard/keyboard_controller.h" 6 7#include "ui/aura/layout_manager.h" 8#include "ui/aura/window.h" 9#include "ui/aura/window_delegate.h" 10#include "ui/base/hit_test.h" 11#include "ui/base/ime/input_method.h" 12#include "ui/base/ime/text_input_client.h" 13#include "ui/base/ime/text_input_type.h" 14#include "ui/gfx/path.h" 15#include "ui/gfx/rect.h" 16#include "ui/gfx/skia_util.h" 17#include "ui/keyboard/keyboard_controller_observer.h" 18#include "ui/keyboard/keyboard_controller_proxy.h" 19 20namespace { 21 22gfx::Rect KeyboardBoundsFromWindowBounds(const gfx::Rect& window_bounds) { 23 const float kKeyboardHeightRatio = 0.3f; 24 return gfx::Rect( 25 window_bounds.x(), 26 window_bounds.y() + window_bounds.height() * (1 - kKeyboardHeightRatio), 27 window_bounds.width(), 28 window_bounds.height() * kKeyboardHeightRatio); 29} 30 31// The KeyboardWindowDelegate makes sure the keyboard-window does not get focus. 32// This is necessary to make sure that the synthetic key-events reach the target 33// window. 34// The delegate deletes itself when the window is destroyed. 35class KeyboardWindowDelegate : public aura::WindowDelegate { 36 public: 37 KeyboardWindowDelegate() {} 38 virtual ~KeyboardWindowDelegate() {} 39 40 private: 41 // Overridden from aura::WindowDelegate: 42 virtual gfx::Size GetMinimumSize() const OVERRIDE { return gfx::Size(); } 43 virtual gfx::Size GetMaximumSize() const OVERRIDE { return gfx::Size(); } 44 virtual void OnBoundsChanged(const gfx::Rect& old_bounds, 45 const gfx::Rect& new_bounds) OVERRIDE { 46 bounds_ = new_bounds; 47 } 48 virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE { 49 return gfx::kNullCursor; 50 } 51 virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE { 52 return HTNOWHERE; 53 } 54 virtual bool ShouldDescendIntoChildForEventHandling( 55 aura::Window* child, 56 const gfx::Point& location) OVERRIDE { 57 return true; 58 } 59 virtual bool CanFocus() OVERRIDE { return false; } 60 virtual void OnCaptureLost() OVERRIDE {} 61 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {} 62 virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {} 63 virtual void OnWindowDestroying() OVERRIDE {} 64 virtual void OnWindowDestroyed() OVERRIDE { delete this; } 65 virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {} 66 virtual bool HasHitTestMask() const OVERRIDE { return true; } 67 virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE { 68 gfx::Rect keyboard_bounds = KeyboardBoundsFromWindowBounds(bounds_); 69 mask->addRect(RectToSkRect(keyboard_bounds)); 70 } 71 virtual scoped_refptr<ui::Texture> CopyTexture() OVERRIDE { return NULL; } 72 73 gfx::Rect bounds_; 74 DISALLOW_COPY_AND_ASSIGN(KeyboardWindowDelegate); 75}; 76 77} // namespace 78 79namespace keyboard { 80 81// LayoutManager for the virtual keyboard container. Manages a single window 82// (the virtual keyboard) and keeps it positioned at the bottom of the 83// owner window. 84class KeyboardLayoutManager : public aura::LayoutManager { 85 public: 86 KeyboardLayoutManager(aura::Window* container) 87 : container_(container), keyboard_(NULL) { 88 CHECK(container_); 89 } 90 91 // Overridden from aura::LayoutManager 92 virtual void OnWindowResized() OVERRIDE { 93 if (!keyboard_) 94 return; 95 SetChildBoundsDirect(keyboard_, 96 KeyboardBoundsFromWindowBounds(container_->bounds())); 97 } 98 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { 99 DCHECK(!keyboard_); 100 keyboard_ = child; 101 } 102 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {} 103 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {} 104 virtual void OnChildWindowVisibilityChanged(aura::Window* child, 105 bool visible) OVERRIDE {} 106 virtual void SetChildBounds(aura::Window* child, 107 const gfx::Rect& requested_bounds) OVERRIDE { 108 // Drop these: the size should only be set in OnWindowResized. 109 } 110 111 private: 112 aura::Window* container_; 113 aura::Window* keyboard_; 114 115 DISALLOW_COPY_AND_ASSIGN(KeyboardLayoutManager); 116}; 117 118KeyboardController::KeyboardController(KeyboardControllerProxy* proxy) 119 : proxy_(proxy), 120 container_(NULL), 121 input_method_(NULL) { 122 CHECK(proxy); 123 input_method_ = proxy_->GetInputMethod(); 124 input_method_->AddObserver(this); 125} 126 127KeyboardController::~KeyboardController() { 128 if (container_) 129 container_->RemoveObserver(this); 130 if (input_method_) 131 input_method_->RemoveObserver(this); 132} 133 134aura::Window* KeyboardController::GetContainerWindow() { 135 if (!container_) { 136 container_ = new aura::Window(new KeyboardWindowDelegate()); 137 container_->SetName("KeyboardContainer"); 138 container_->Init(ui::LAYER_NOT_DRAWN); 139 container_->AddObserver(this); 140 container_->SetLayoutManager(new KeyboardLayoutManager(container_)); 141 } 142 return container_; 143} 144 145void KeyboardController::AddObserver(KeyboardControllerObserver* observer) { 146 observer_list_.AddObserver(observer); 147} 148 149void KeyboardController::RemoveObserver(KeyboardControllerObserver* observer) { 150 observer_list_.RemoveObserver(observer); 151} 152 153void KeyboardController::OnWindowHierarchyChanged( 154 const HierarchyChangeParams& params) { 155 if (params.new_parent && params.target == container_) 156 OnTextInputStateChanged(proxy_->GetInputMethod()->GetTextInputClient()); 157} 158 159void KeyboardController::OnWindowDestroying(aura::Window* window) { 160 DCHECK_EQ(container_, window); 161 container_ = NULL; 162} 163 164void KeyboardController::OnTextInputStateChanged( 165 const ui::TextInputClient* client) { 166 if (!container_) 167 return; 168 169 bool was_showing = container_->IsVisible(); 170 bool should_show = was_showing; 171 if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) { 172 should_show = false; 173 } else { 174 if (container_->children().empty()) { 175 aura::Window* keyboard = proxy_->GetKeyboardWindow(); 176 keyboard->Show(); 177 container_->AddChild(keyboard); 178 container_->layout_manager()->OnWindowResized(); 179 } 180 container_->parent()->StackChildAtTop(container_); 181 should_show = true; 182 } 183 184 if (was_showing != should_show) { 185 gfx::Rect new_bounds( 186 should_show ? container_->children()[0]->bounds() : gfx::Rect()); 187 188 FOR_EACH_OBSERVER( 189 KeyboardControllerObserver, 190 observer_list_, 191 OnKeyboardBoundsChanging(new_bounds)); 192 193 if (should_show) 194 proxy_->ShowKeyboardContainer(container_); 195 else 196 proxy_->HideKeyboardContainer(container_); 197 } 198 199 // TODO(bryeung): whenever the TextInputClient changes we need to notify the 200 // keyboard (with the TextInputType) so that it can reset it's state (e.g. 201 // abandon compositions in progress) 202} 203 204void KeyboardController::OnInputMethodDestroyed( 205 const ui::InputMethod* input_method) { 206 DCHECK_EQ(input_method_, input_method); 207 input_method_ = NULL; 208} 209 210} // namespace keyboard 211