Add a minor text member to ui::MenuModel.
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / candidate_window_controller_impl.cc
blob54a6410d31dbc57b584e1a78c68314f4c6c01953
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.
5 #include "chrome/browser/chromeos/input_method/candidate_window_controller_impl.h"
7 #include <string>
8 #include <vector>
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/wm/window_animations.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/observer_list.h"
16 #include "chrome/browser/chromeos/input_method/candidate_window_view.h"
17 #include "chrome/browser/chromeos/input_method/delayable_widget.h"
18 #include "chrome/browser/chromeos/input_method/infolist_window_view.h"
19 #include "ui/views/widget/widget.h"
22 namespace chromeos {
23 namespace input_method {
25 namespace {
26 // The milliseconds of the delay to show the infolist window.
27 const int kInfolistShowDelayMilliSeconds = 500;
28 // The milliseconds of the delay to hide the infolist window.
29 const int kInfolistHideDelayMilliSeconds = 500;
31 // Converts from ibus::Rect to gfx::Rect.
32 gfx::Rect IBusRectToGfxRect(const ibus::Rect& rect) {
33 return gfx::Rect(rect.x, rect.y, rect.width, rect.height);
35 } // namespace
37 bool CandidateWindowControllerImpl::Init() {
38 // Create the candidate window view.
39 CreateView();
40 return true;
43 void CandidateWindowControllerImpl::Shutdown() {
46 void CandidateWindowControllerImpl::CreateView() {
47 // Create a non-decorated frame.
48 frame_.reset(new views::Widget);
49 // The size is initially zero.
50 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
51 // |frame_| and |infolist_window_| are owned by controller impl so
52 // they should use WIDGET_OWNS_NATIVE_WIDGET ownership.
53 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
54 // Show the candidate window always on top
55 params.parent = ash::Shell::GetContainer(
56 ash::Shell::GetActiveRootWindow(),
57 ash::internal::kShellWindowId_InputMethodContainer);
58 frame_->Init(params);
60 views::corewm::SetWindowVisibilityAnimationType(
61 frame_->GetNativeView(),
62 views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
64 // Create the candidate window.
65 candidate_window_ = new CandidateWindowView(frame_.get());
66 candidate_window_->Init();
67 candidate_window_->AddObserver(this);
69 frame_->SetContentsView(candidate_window_);
72 // Create the infolist window.
73 infolist_window_.reset(new DelayableWidget);
74 infolist_window_->Init(params);
76 views::corewm::SetWindowVisibilityAnimationType(
77 infolist_window_->GetNativeView(),
78 views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
80 InfolistWindowView* infolist_view = new InfolistWindowView;
81 infolist_view->Init();
82 infolist_window_->SetContentsView(infolist_view);
85 CandidateWindowControllerImpl::CandidateWindowControllerImpl()
86 : candidate_window_(NULL),
87 latest_infolist_focused_index_(InfolistWindowView::InvalidFocusIndex()) {
88 IBusBridge::Get()->SetCandidateWindowHandler(this);
91 CandidateWindowControllerImpl::~CandidateWindowControllerImpl() {
92 IBusBridge::Get()->SetCandidateWindowHandler(NULL);
93 candidate_window_->RemoveObserver(this);
96 void CandidateWindowControllerImpl::HideAuxiliaryText() {
97 candidate_window_->HideAuxiliaryText();
100 void CandidateWindowControllerImpl::HideLookupTable() {
101 candidate_window_->HideLookupTable();
102 infolist_window_->Hide();
105 void CandidateWindowControllerImpl::HidePreeditText() {
106 candidate_window_->HidePreeditText();
109 void CandidateWindowControllerImpl::SetCursorLocation(
110 const ibus::Rect& cursor_location,
111 const ibus::Rect& composition_head) {
112 // A workaround for http://crosbug.com/6460. We should ignore very short Y
113 // move to prevent the window from shaking up and down.
114 const int kKeepPositionThreshold = 2; // px
115 const gfx::Rect& last_location =
116 candidate_window_->cursor_location();
117 const int delta_y = abs(last_location.y() - cursor_location.y);
118 if ((last_location.x() == cursor_location.x) &&
119 (delta_y <= kKeepPositionThreshold)) {
120 DVLOG(1) << "Ignored set_cursor_location signal to prevent window shake";
121 return;
124 // Remember the cursor location.
125 candidate_window_->set_cursor_location(IBusRectToGfxRect(cursor_location));
126 candidate_window_->set_composition_head_location(
127 IBusRectToGfxRect(composition_head));
128 // Move the window per the cursor location.
129 candidate_window_->ResizeAndMoveParentFrame();
130 UpdateInfolistBounds();
133 void CandidateWindowControllerImpl::UpdateAuxiliaryText(
134 const std::string& utf8_text,
135 bool visible) {
136 // If it's not visible, hide the auxiliary text and return.
137 if (!visible) {
138 candidate_window_->HideAuxiliaryText();
139 return;
141 candidate_window_->UpdateAuxiliaryText(utf8_text);
142 candidate_window_->ShowAuxiliaryText();
145 // static
146 void CandidateWindowControllerImpl::ConvertLookupTableToInfolistEntry(
147 const IBusLookupTable& lookup_table,
148 std::vector<InfolistWindowView::Entry>* infolist_entries,
149 size_t* focused_index) {
150 DCHECK(focused_index);
151 DCHECK(infolist_entries);
152 *focused_index = InfolistWindowView::InvalidFocusIndex();
153 infolist_entries->clear();
155 const size_t cursor_index_in_page =
156 lookup_table.cursor_position() % lookup_table.page_size();
158 for (size_t i = 0; i < lookup_table.candidates().size(); ++i) {
159 const IBusLookupTable::Entry& ibus_entry =
160 lookup_table.candidates()[i];
161 if (ibus_entry.description_title.empty() &&
162 ibus_entry.description_body.empty())
163 continue;
164 InfolistWindowView::Entry entry;
165 entry.title = ibus_entry.description_title;
166 entry.body = ibus_entry.description_body;
167 infolist_entries->push_back(entry);
168 if (i == cursor_index_in_page)
169 *focused_index = infolist_entries->size() - 1;
173 // static
174 bool CandidateWindowControllerImpl::ShouldUpdateInfolist(
175 const std::vector<InfolistWindowView::Entry>& old_entries,
176 size_t old_focused_index,
177 const std::vector<InfolistWindowView::Entry>& new_entries,
178 size_t new_focused_index) {
179 if (old_entries.empty() && new_entries.empty())
180 return false;
181 if (old_entries.size() != new_entries.size())
182 return true;
183 if (old_focused_index != new_focused_index)
184 return true;
186 for (size_t i = 0; i < old_entries.size(); ++i) {
187 if (old_entries[i].title != new_entries[i].title ||
188 old_entries[i].body != new_entries[i].body ) {
189 return true;
192 return false;
195 void CandidateWindowControllerImpl::UpdateLookupTable(
196 const IBusLookupTable& lookup_table,
197 bool visible) {
198 // If it's not visible, hide the lookup table and return.
199 if (!visible) {
200 candidate_window_->HideLookupTable();
201 infolist_window_->Hide();
202 // TODO(nona): Introduce unittests for crbug.com/170036.
203 latest_infolist_entries_.clear();
204 return;
207 candidate_window_->UpdateCandidates(lookup_table);
208 candidate_window_->ShowLookupTable();
210 size_t focused_index = 0;
211 std::vector<InfolistWindowView::Entry> infolist_entries;
212 ConvertLookupTableToInfolistEntry(lookup_table, &infolist_entries,
213 &focused_index);
215 // If there is no infolist entry, just hide.
216 if (infolist_entries.empty()) {
217 infolist_window_->Hide();
218 return;
221 // If there is no change, just return.
222 if (!ShouldUpdateInfolist(latest_infolist_entries_,
223 latest_infolist_focused_index_,
224 infolist_entries,
225 focused_index)) {
226 return;
229 latest_infolist_entries_ = infolist_entries;
230 latest_infolist_focused_index_ = focused_index;
232 InfolistWindowView* view = static_cast<InfolistWindowView*>(
233 infolist_window_->GetContentsView());
234 if (!view) {
235 DLOG(ERROR) << "Contents View is not InfolistWindowView.";
236 return;
239 view->Relayout(infolist_entries, focused_index);
240 UpdateInfolistBounds();
242 if (focused_index < infolist_entries.size())
243 infolist_window_->DelayShow(kInfolistShowDelayMilliSeconds);
244 else
245 infolist_window_->DelayHide(kInfolistHideDelayMilliSeconds);
248 void CandidateWindowControllerImpl::UpdateInfolistBounds() {
249 InfolistWindowView* view = static_cast<InfolistWindowView*>(
250 infolist_window_->GetContentsView());
251 if (!view)
252 return;
253 const gfx::Rect current_bounds =
254 infolist_window_->GetClientAreaBoundsInScreen();
256 gfx::Rect new_bounds;
257 new_bounds.set_size(view->GetPreferredSize());
258 new_bounds.set_origin(GetInfolistWindowPosition(
259 frame_->GetClientAreaBoundsInScreen(),
260 ash::Shell::GetScreen()->GetDisplayNearestWindow(
261 infolist_window_->GetNativeView()).work_area(),
262 new_bounds.size()));
264 if (current_bounds != new_bounds)
265 infolist_window_->SetBounds(new_bounds);
268 void CandidateWindowControllerImpl::UpdatePreeditText(
269 const std::string& utf8_text, unsigned int cursor, bool visible) {
270 // If it's not visible, hide the preedit text and return.
271 if (!visible || utf8_text.empty()) {
272 candidate_window_->HidePreeditText();
273 return;
275 candidate_window_->UpdatePreeditText(utf8_text);
276 candidate_window_->ShowPreeditText();
279 void CandidateWindowControllerImpl::OnCandidateCommitted(int index,
280 int button,
281 int flags) {
282 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler();
283 if (engine)
284 engine->CandidateClicked(index,
285 static_cast<ibus::IBusMouseButton>(button),
286 flags);
289 void CandidateWindowControllerImpl::OnCandidateWindowOpened() {
290 FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
291 CandidateWindowOpened());
294 void CandidateWindowControllerImpl::OnCandidateWindowClosed() {
295 FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
296 CandidateWindowClosed());
299 void CandidateWindowControllerImpl::AddObserver(
300 CandidateWindowController::Observer* observer) {
301 observers_.AddObserver(observer);
304 void CandidateWindowControllerImpl::RemoveObserver(
305 CandidateWindowController::Observer* observer) {
306 observers_.RemoveObserver(observer);
309 // static
310 gfx::Point CandidateWindowControllerImpl::GetInfolistWindowPosition(
311 const gfx::Rect& candidate_window_rect,
312 const gfx::Rect& screen_rect,
313 const gfx::Size& infolist_window_size) {
314 gfx::Point result(candidate_window_rect.right(), candidate_window_rect.y());
316 if (candidate_window_rect.right() + infolist_window_size.width() >
317 screen_rect.right())
318 result.set_x(candidate_window_rect.x() - infolist_window_size.width());
320 if (candidate_window_rect.y() + infolist_window_size.height() >
321 screen_rect.bottom())
322 result.set_y(screen_rect.bottom() - infolist_window_size.height());
324 return result;
327 } // namespace input_method
328 } // namespace chromeos