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 "base/bind.h" 8 #include "base/command_line.h" 9 #include "ui/aura/layout_manager.h" 10 #include "ui/aura/window.h" 11 #include "ui/aura/window_delegate.h" 12 #include "ui/base/cursor/cursor.h" 13 #include "ui/base/hit_test.h" 14 #include "ui/base/ime/input_method.h" 15 #include "ui/base/ime/text_input_client.h" 16 #include "ui/base/ime/text_input_type.h" 17 #include "ui/gfx/path.h" 18 #include "ui/gfx/rect.h" 19 #include "ui/gfx/skia_util.h" 20 #include "ui/keyboard/keyboard_controller_observer.h" 21 #include "ui/keyboard/keyboard_controller_proxy.h" 22 #include "ui/keyboard/keyboard_switches.h" 23 #include "ui/keyboard/keyboard_util.h" 24 25 namespace { 26 27 const int kHideKeyboardDelayMs = 100; 28 29 gfx::Rect KeyboardBoundsFromWindowBounds(const gfx::Rect& window_bounds) { 30 const float kKeyboardHeightRatio = 31 keyboard::IsKeyboardUsabilityExperimentEnabled() ? 1.0f : 0.3f; 32 return gfx::Rect( 33 window_bounds.x(), 34 window_bounds.y() + window_bounds.height() * (1 - kKeyboardHeightRatio), 35 window_bounds.width(), 36 window_bounds.height() * kKeyboardHeightRatio); 37 } 38 39 // The KeyboardWindowDelegate makes sure the keyboard-window does not get focus. 40 // This is necessary to make sure that the synthetic key-events reach the target 41 // window. 42 // The delegate deletes itself when the window is destroyed. 43 class KeyboardWindowDelegate : public aura::WindowDelegate { 44 public: 45 explicit KeyboardWindowDelegate(keyboard::KeyboardControllerProxy* proxy) 46 : proxy_(proxy) {} 47 virtual ~KeyboardWindowDelegate() {} 48 49 private: 50 // Overridden from aura::WindowDelegate: 51 virtual gfx::Size GetMinimumSize() const OVERRIDE { return gfx::Size(); } 52 virtual gfx::Size GetMaximumSize() const OVERRIDE { return gfx::Size(); } 53 virtual void OnBoundsChanged(const gfx::Rect& old_bounds, 54 const gfx::Rect& new_bounds) OVERRIDE { 55 bounds_ = new_bounds; 56 } 57 virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE { 58 return gfx::kNullCursor; 59 } 60 virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE { 61 return HTNOWHERE; 62 } 63 virtual bool ShouldDescendIntoChildForEventHandling( 64 aura::Window* child, 65 const gfx::Point& location) OVERRIDE { 66 return true; 67 } 68 virtual bool CanFocus() OVERRIDE { return false; } 69 virtual void OnCaptureLost() OVERRIDE {} 70 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {} 71 virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {} 72 virtual void OnWindowDestroying() OVERRIDE {} 73 virtual void OnWindowDestroyed() OVERRIDE { delete this; } 74 virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {} 75 virtual bool HasHitTestMask() const OVERRIDE { return true; } 76 virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE { 77 gfx::Rect keyboard_bounds = proxy_ ? proxy_->GetKeyboardWindow()->bounds() : 78 KeyboardBoundsFromWindowBounds(bounds_); 79 mask->addRect(RectToSkRect(keyboard_bounds)); 80 } 81 virtual void DidRecreateLayer(ui::Layer* old_layer, 82 ui::Layer* new_layer) OVERRIDE {} 83 84 gfx::Rect bounds_; 85 keyboard::KeyboardControllerProxy* proxy_; 86 87 DISALLOW_COPY_AND_ASSIGN(KeyboardWindowDelegate); 88 }; 89 90 } // namespace 91 92 namespace keyboard { 93 94 // LayoutManager for the virtual keyboard container. Manages a single window 95 // (the virtual keyboard) and keeps it positioned at the bottom of the 96 // owner window. 97 class KeyboardLayoutManager : public aura::LayoutManager { 98 public: 99 explicit KeyboardLayoutManager(KeyboardController* controller) 100 : controller_(controller), keyboard_(NULL) { 101 } 102 103 // Overridden from aura::LayoutManager 104 virtual void OnWindowResized() OVERRIDE { 105 if (keyboard_ && !controller_->proxy()->resizing_from_contents()) 106 ResizeKeyboardToDefault(keyboard_); 107 } 108 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { 109 DCHECK(!keyboard_); 110 keyboard_ = child; 111 ResizeKeyboardToDefault(keyboard_); 112 } 113 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {} 114 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {} 115 virtual void OnChildWindowVisibilityChanged(aura::Window* child, 116 bool visible) OVERRIDE {} 117 virtual void SetChildBounds(aura::Window* child, 118 const gfx::Rect& requested_bounds) OVERRIDE { 119 // SetChildBounds can be invoked by resizing from the container or by 120 // resizing from the contents (through window.resizeTo call in JS). 121 // The flag resizing_from_contents() is used to determine the keyboard is 122 // resizing from which. 123 if (controller_->proxy()->resizing_from_contents()) { 124 controller_->NotifyKeyboardBoundsChanging(requested_bounds); 125 SetChildBoundsDirect(child, requested_bounds); 126 } 127 } 128 129 private: 130 void ResizeKeyboardToDefault(aura::Window* child) { 131 gfx::Rect keyboard_bounds = KeyboardBoundsFromWindowBounds( 132 controller_->GetContainerWindow()->bounds()); 133 SetChildBoundsDirect(child, keyboard_bounds); 134 } 135 136 KeyboardController* controller_; 137 aura::Window* keyboard_; 138 139 DISALLOW_COPY_AND_ASSIGN(KeyboardLayoutManager); 140 }; 141 142 KeyboardController::KeyboardController(KeyboardControllerProxy* proxy) 143 : proxy_(proxy), 144 input_method_(NULL), 145 keyboard_visible_(false), 146 lock_keyboard_(false), 147 weak_factory_(this) { 148 CHECK(proxy); 149 input_method_ = proxy_->GetInputMethod(); 150 input_method_->AddObserver(this); 151 } 152 153 KeyboardController::~KeyboardController() { 154 if (container_) { 155 container_->RemoveObserver(this); 156 // Remove the keyboard window from the children because the keyboard window 157 // is owned by proxy and it should be destroyed by proxy. 158 if (container_->Contains(proxy_->GetKeyboardWindow())) 159 container_->RemoveChild(proxy_->GetKeyboardWindow()); 160 } 161 if (input_method_) 162 input_method_->RemoveObserver(this); 163 } 164 165 aura::Window* KeyboardController::GetContainerWindow() { 166 if (!container_.get()) { 167 container_.reset(new aura::Window( 168 new KeyboardWindowDelegate(proxy_.get()))); 169 container_->SetName("KeyboardContainer"); 170 container_->set_owned_by_parent(false); 171 container_->Init(ui::LAYER_NOT_DRAWN); 172 container_->AddObserver(this); 173 container_->SetLayoutManager(new KeyboardLayoutManager(this)); 174 } 175 return container_.get(); 176 } 177 178 void KeyboardController::NotifyKeyboardBoundsChanging( 179 const gfx::Rect& new_bounds) { 180 if (proxy_->GetKeyboardWindow()->IsVisible()) { 181 FOR_EACH_OBSERVER(KeyboardControllerObserver, 182 observer_list_, 183 OnKeyboardBoundsChanging(new_bounds)); 184 } 185 } 186 187 void KeyboardController::HideKeyboard(HideReason reason) { 188 keyboard_visible_ = false; 189 190 keyboard::LogKeyboardControlEvent( 191 reason == HIDE_REASON_AUTOMATIC ? 192 keyboard::KEYBOARD_CONTROL_HIDE_AUTO : 193 keyboard::KEYBOARD_CONTROL_HIDE_USER); 194 195 NotifyKeyboardBoundsChanging(gfx::Rect()); 196 197 proxy_->HideKeyboardContainer(container_.get()); 198 } 199 200 void KeyboardController::AddObserver(KeyboardControllerObserver* observer) { 201 observer_list_.AddObserver(observer); 202 } 203 204 void KeyboardController::RemoveObserver(KeyboardControllerObserver* observer) { 205 observer_list_.RemoveObserver(observer); 206 } 207 208 void KeyboardController::OnWindowHierarchyChanged( 209 const HierarchyChangeParams& params) { 210 if (params.new_parent && params.target == container_.get()) 211 OnTextInputStateChanged(proxy_->GetInputMethod()->GetTextInputClient()); 212 } 213 214 void KeyboardController::SetOverrideContentUrl(const GURL& url) { 215 proxy_->SetOverrideContentUrl(url); 216 } 217 218 void KeyboardController::OnTextInputStateChanged( 219 const ui::TextInputClient* client) { 220 if (!container_.get()) 221 return; 222 223 bool was_showing = keyboard_visible_; 224 bool should_show = was_showing; 225 ui::TextInputType type = 226 client ? client->GetTextInputType() : ui::TEXT_INPUT_TYPE_NONE; 227 if (type == ui::TEXT_INPUT_TYPE_NONE && 228 !IsKeyboardUsabilityExperimentEnabled() && 229 !lock_keyboard_) { 230 should_show = false; 231 } else { 232 if (container_->children().empty()) { 233 keyboard::MarkKeyboardLoadStarted(); 234 aura::Window* keyboard = proxy_->GetKeyboardWindow(); 235 keyboard->Show(); 236 container_->AddChild(keyboard); 237 } 238 if (type != ui::TEXT_INPUT_TYPE_NONE) 239 proxy_->SetUpdateInputType(type); 240 container_->parent()->StackChildAtTop(container_.get()); 241 should_show = true; 242 } 243 244 if (was_showing != should_show) { 245 if (should_show) { 246 keyboard_visible_ = true; 247 248 // If the controller is in the process of hiding the keyboard, do not log 249 // the stat here since the keyboard will not actually be shown. 250 if (!WillHideKeyboard()) 251 keyboard::LogKeyboardControlEvent(keyboard::KEYBOARD_CONTROL_SHOW); 252 253 weak_factory_.InvalidateWeakPtrs(); 254 if (container_->IsVisible()) 255 return; 256 257 NotifyKeyboardBoundsChanging(container_->children()[0]->bounds()); 258 259 proxy_->ShowKeyboardContainer(container_.get()); 260 } else { 261 // Set the visibility state here so that any queries for visibility 262 // before the timer fires returns the correct future value. 263 keyboard_visible_ = false; 264 base::MessageLoop::current()->PostDelayedTask( 265 FROM_HERE, 266 base::Bind(&KeyboardController::HideKeyboard, 267 weak_factory_.GetWeakPtr(), HIDE_REASON_AUTOMATIC), 268 base::TimeDelta::FromMilliseconds(kHideKeyboardDelayMs)); 269 } 270 } 271 // TODO(bryeung): whenever the TextInputClient changes we need to notify the 272 // keyboard (with the TextInputType) so that it can reset it's state (e.g. 273 // abandon compositions in progress) 274 } 275 276 void KeyboardController::OnInputMethodDestroyed( 277 const ui::InputMethod* input_method) { 278 DCHECK_EQ(input_method_, input_method); 279 input_method_ = NULL; 280 } 281 282 bool KeyboardController::WillHideKeyboard() const { 283 return weak_factory_.HasWeakPtrs(); 284 } 285 286 } // namespace keyboard 287