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