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 20 namespace { 21 22 gfx::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. 35 class 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 79 namespace 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. 84 class 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 118 KeyboardController::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 127 KeyboardController::~KeyboardController() { 128 if (container_) 129 container_->RemoveObserver(this); 130 if (input_method_) 131 input_method_->RemoveObserver(this); 132 } 133 134 aura::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 145 void KeyboardController::AddObserver(KeyboardControllerObserver* observer) { 146 observer_list_.AddObserver(observer); 147 } 148 149 void KeyboardController::RemoveObserver(KeyboardControllerObserver* observer) { 150 observer_list_.RemoveObserver(observer); 151 } 152 153 void KeyboardController::OnWindowHierarchyChanged( 154 const HierarchyChangeParams& params) { 155 if (params.new_parent && params.target == container_) 156 OnTextInputStateChanged(proxy_->GetInputMethod()->GetTextInputClient()); 157 } 158 159 void KeyboardController::OnWindowDestroying(aura::Window* window) { 160 DCHECK_EQ(container_, window); 161 container_ = NULL; 162 } 163 164 void 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 204 void 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