candidate_window_controller_impl.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1// Copyright (c) 2012 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 "chrome/browser/chromeos/input_method/candidate_window_controller_impl.h"
6
7#include <string>
8#include <vector>
9
10#include "ash/ime/infolist_window.h"
11#include "ash/shell.h"
12#include "ash/shell_window_ids.h"
13#include "ash/wm/window_util.h"
14#include "base/logging.h"
15#include "chrome/browser/chromeos/input_method/mode_indicator_controller.h"
16#include "ui/gfx/screen.h"
17#include "ui/views/widget/widget.h"
18
19namespace chromeos {
20namespace input_method {
21
22namespace {
23
24}  // namespace
25
26CandidateWindowControllerImpl::CandidateWindowControllerImpl()
27    : candidate_window_view_(NULL),
28      infolist_window_(NULL) {
29  IMEBridge::Get()->SetCandidateWindowHandler(this);
30  // Create the mode indicator controller.
31  mode_indicator_controller_.reset(
32      new ModeIndicatorController(InputMethodManager::Get()));
33}
34
35CandidateWindowControllerImpl::~CandidateWindowControllerImpl() {
36  IMEBridge::Get()->SetCandidateWindowHandler(NULL);
37  if (candidate_window_view_) {
38    candidate_window_view_->RemoveObserver(this);
39    candidate_window_view_->GetWidget()->RemoveObserver(this);
40  }
41}
42
43void CandidateWindowControllerImpl::InitCandidateWindowView() {
44  if (candidate_window_view_)
45    return;
46
47  aura::Window* active_window = ash::wm::GetActiveWindow();
48  candidate_window_view_ =
49      new ash::ime::CandidateWindowView(ash::Shell::GetContainer(
50          active_window ? active_window->GetRootWindow()
51                        : ash::Shell::GetTargetRootWindow(),
52          ash::kShellWindowId_InputMethodContainer));
53  candidate_window_view_->AddObserver(this);
54  candidate_window_view_->SetCursorBounds(cursor_bounds_, composition_head_);
55  views::Widget* widget = candidate_window_view_->InitWidget();
56  widget->AddObserver(this);
57  widget->Show();
58  FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
59                    CandidateWindowOpened());
60}
61
62void CandidateWindowControllerImpl::Hide() {
63  if (candidate_window_view_)
64    candidate_window_view_->GetWidget()->Close();
65  if (infolist_window_)
66    infolist_window_->HideImmediately();
67}
68
69void CandidateWindowControllerImpl::SetCursorBounds(
70    const gfx::Rect& cursor_bounds,
71    const gfx::Rect& composition_head) {
72  // A workaround for http://crosbug.com/6460. We should ignore very short Y
73  // move to prevent the window from shaking up and down.
74  const int kKeepPositionThreshold = 2;  // px
75  gfx::Rect last_bounds;
76  if (candidate_window_view_)
77    last_bounds = candidate_window_view_->GetAnchorRect();
78
79  const int delta_y = abs(last_bounds.y() - cursor_bounds.y());
80  if ((last_bounds.x() == cursor_bounds.x()) &&
81      (delta_y <= kKeepPositionThreshold)) {
82    DVLOG(1) << "Ignored set_cursor_bounds signal to prevent window shake";
83    return;
84  }
85
86  cursor_bounds_ = cursor_bounds;
87  composition_head_ = composition_head;
88
89  // Remember the cursor bounds.
90  if (candidate_window_view_)
91    candidate_window_view_->SetCursorBounds(cursor_bounds, composition_head);
92
93  // Mode indicator controller also needs the cursor bounds.
94  mode_indicator_controller_->SetCursorBounds(cursor_bounds);
95}
96
97void CandidateWindowControllerImpl::FocusStateChanged(bool is_focused) {
98  mode_indicator_controller_->FocusStateChanged(is_focused);
99}
100
101void CandidateWindowControllerImpl::UpdateLookupTable(
102    const ui::CandidateWindow& candidate_window,
103    bool visible) {
104  // If it's not visible, hide the lookup table and return.
105  if (!visible) {
106    if (candidate_window_view_)
107      candidate_window_view_->HideLookupTable();
108    if (infolist_window_)
109      infolist_window_->HideImmediately();
110    // TODO(nona): Introduce unittests for crbug.com/170036.
111    latest_infolist_entries_.clear();
112    return;
113  }
114
115  if (!candidate_window_view_)
116    InitCandidateWindowView();
117  candidate_window_view_->UpdateCandidates(candidate_window);
118  candidate_window_view_->ShowLookupTable();
119
120  bool has_highlighted = false;
121  std::vector<ui::InfolistEntry> infolist_entries;
122  candidate_window.GetInfolistEntries(&infolist_entries, &has_highlighted);
123
124  // If there is no change, just return.
125  if (latest_infolist_entries_ == infolist_entries)
126    return;
127
128  latest_infolist_entries_ = infolist_entries;
129
130  if (infolist_entries.empty()) {
131    if (infolist_window_)
132      infolist_window_->HideImmediately();
133    return;
134  }
135
136  // Highlight moves out of the infolist entries.
137  if (!has_highlighted) {
138    if (infolist_window_)
139      infolist_window_->HideWithDelay();
140    return;
141  }
142
143  if (infolist_window_) {
144    infolist_window_->Relayout(infolist_entries);
145  } else {
146    infolist_window_ = new ash::ime::InfolistWindow(
147        candidate_window_view_, infolist_entries);
148    infolist_window_->InitWidget();
149    infolist_window_->GetWidget()->AddObserver(this);
150  }
151  infolist_window_->ShowWithDelay();
152}
153
154void CandidateWindowControllerImpl::UpdatePreeditText(
155    const base::string16& text, unsigned int cursor, bool visible) {
156  // If it's not visible, hide the preedit text and return.
157  if (!visible || text.empty()) {
158    if (candidate_window_view_)
159      candidate_window_view_->HidePreeditText();
160    return;
161  }
162  if (!candidate_window_view_)
163    InitCandidateWindowView();
164  candidate_window_view_->UpdatePreeditText(text);
165  candidate_window_view_->ShowPreeditText();
166}
167
168void CandidateWindowControllerImpl::OnCandidateCommitted(int index) {
169  FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
170                    CandidateClicked(index));
171}
172
173void CandidateWindowControllerImpl::OnWidgetClosing(views::Widget* widget) {
174  if (infolist_window_ && widget == infolist_window_->GetWidget()) {
175    widget->RemoveObserver(this);
176    infolist_window_ = NULL;
177  } else if (candidate_window_view_ &&
178             widget == candidate_window_view_->GetWidget()) {
179    widget->RemoveObserver(this);
180    candidate_window_view_->RemoveObserver(this);
181    candidate_window_view_ = NULL;
182    FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
183                      CandidateWindowClosed());
184  }
185}
186
187void CandidateWindowControllerImpl::AddObserver(
188    CandidateWindowController::Observer* observer) {
189  observers_.AddObserver(observer);
190}
191
192void CandidateWindowControllerImpl::RemoveObserver(
193    CandidateWindowController::Observer* observer) {
194  observers_.RemoveObserver(observer);
195}
196
197}  // namespace input_method
198}  // namespace chromeos
199