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 19 namespace chromeos { 20 namespace input_method { 21 22 namespace { 23 24 } // namespace 25 26 CandidateWindowControllerImpl::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 35 CandidateWindowControllerImpl::~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 43 void 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_SettingBubbleContainer)); 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 62 void CandidateWindowControllerImpl::Hide() { 63 if (candidate_window_view_) 64 candidate_window_view_->GetWidget()->Close(); 65 if (infolist_window_) 66 infolist_window_->HideImmediately(); 67 } 68 69 void 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 97 void CandidateWindowControllerImpl::FocusStateChanged(bool is_focused) { 98 mode_indicator_controller_->FocusStateChanged(is_focused); 99 } 100 101 void 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 154 void 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 168 void CandidateWindowControllerImpl::OnCandidateCommitted(int index) { 169 FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_, 170 CandidateClicked(index)); 171 } 172 173 void 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 187 void CandidateWindowControllerImpl::AddObserver( 188 CandidateWindowController::Observer* observer) { 189 observers_.AddObserver(observer); 190 } 191 192 void CandidateWindowControllerImpl::RemoveObserver( 193 CandidateWindowController::Observer* observer) { 194 observers_.RemoveObserver(observer); 195 } 196 197 } // namespace input_method 198 } // namespace chromeos 199