#include <string>
#include <vector>
+#include "ash/ime/infolist_window.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
-#include "ash/wm/window_animations.h"
+#include "ash/wm/window_util.h"
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/observer_list.h"
-#include "chrome/browser/chromeos/input_method/candidate_window_view.h"
-#include "chrome/browser/chromeos/input_method/delayable_widget.h"
-#include "chrome/browser/chromeos/input_method/infolist_window_view.h"
#include "chrome/browser/chromeos/input_method/mode_indicator_controller.h"
-#include "chrome/browser/chromeos/input_method/mode_indicator_widget.h"
+#include "ui/gfx/screen.h"
#include "ui/views/widget/widget.h"
-
namespace chromeos {
namespace input_method {
namespace {
-// The milliseconds of the delay to show the infolist window.
-const int kInfolistShowDelayMilliSeconds = 500;
-// The milliseconds of the delay to hide the infolist window.
-const int kInfolistHideDelayMilliSeconds = 500;
-// Converts from ibus::Rect to gfx::Rect.
-gfx::Rect IBusRectToGfxRect(const ibus::Rect& rect) {
- return gfx::Rect(rect.x, rect.y, rect.width, rect.height);
-}
} // namespace
-bool CandidateWindowControllerImpl::Init() {
- // Create the candidate window view.
- CreateView();
- return true;
-}
-
-void CandidateWindowControllerImpl::Shutdown() {
-}
-
-void CandidateWindowControllerImpl::CreateView() {
- // Create a non-decorated frame.
- frame_.reset(new views::Widget);
- // The size is initially zero.
- views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
- // |frame_| and |infolist_window_| are owned by controller impl so
- // they should use WIDGET_OWNS_NATIVE_WIDGET ownership.
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- // Show the candidate window always on top
- params.parent = ash::Shell::GetContainer(
- ash::Shell::GetTargetRootWindow(),
- ash::internal::kShellWindowId_InputMethodContainer);
- frame_->Init(params);
-
- views::corewm::SetWindowVisibilityAnimationType(
- frame_->GetNativeView(),
- views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-
- // Create the candidate window.
- candidate_window_view_ = new CandidateWindowView(frame_.get());
- candidate_window_view_->Init();
- candidate_window_view_->AddObserver(this);
-
- frame_->SetContentsView(candidate_window_view_);
-
- // Create the infolist window.
- infolist_window_.reset(new DelayableWidget);
- infolist_window_->Init(params);
-
- views::corewm::SetWindowVisibilityAnimationType(
- infolist_window_->GetNativeView(),
- views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-
- InfolistWindowView* infolist_view = new InfolistWindowView;
- infolist_view->Init();
- infolist_window_->SetContentsView(infolist_view);
-
- // Create the mode indicator controller.
- mode_indicator_controller_.reset(
- new ModeIndicatorController(new ModeIndicatorWidget));
-}
-
CandidateWindowControllerImpl::CandidateWindowControllerImpl()
: candidate_window_view_(NULL),
- latest_infolist_focused_index_(InfolistWindowView::InvalidFocusIndex()) {
- IBusBridge::Get()->SetCandidateWindowHandler(this);
+ infolist_window_(NULL) {
+ IMEBridge::Get()->SetCandidateWindowHandler(this);
+ // Create the mode indicator controller.
+ mode_indicator_controller_.reset(
+ new ModeIndicatorController(InputMethodManager::Get()));
}
CandidateWindowControllerImpl::~CandidateWindowControllerImpl() {
- IBusBridge::Get()->SetCandidateWindowHandler(NULL);
- candidate_window_view_->RemoveObserver(this);
+ IMEBridge::Get()->SetCandidateWindowHandler(NULL);
+ if (candidate_window_view_) {
+ candidate_window_view_->RemoveObserver(this);
+ candidate_window_view_->GetWidget()->RemoveObserver(this);
+ }
}
-void CandidateWindowControllerImpl::HideAuxiliaryText() {
- candidate_window_view_->HideAuxiliaryText();
-}
+void CandidateWindowControllerImpl::InitCandidateWindowView() {
+ if (candidate_window_view_)
+ return;
-void CandidateWindowControllerImpl::HideLookupTable() {
- candidate_window_view_->HideLookupTable();
- infolist_window_->Hide();
+ aura::Window* active_window = ash::wm::GetActiveWindow();
+ candidate_window_view_ =
+ new ash::ime::CandidateWindowView(ash::Shell::GetContainer(
+ active_window ?
+ active_window->GetRootWindow() : ash::Shell::GetTargetRootWindow(),
+ ash::internal::kShellWindowId_InputMethodContainer));
+ candidate_window_view_->AddObserver(this);
+ candidate_window_view_->SetCursorBounds(cursor_bounds_, composition_head_);
+ views::Widget* widget = candidate_window_view_->InitWidget();
+ widget->AddObserver(this);
+ widget->Show();
+ FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
+ CandidateWindowOpened());
}
-void CandidateWindowControllerImpl::HidePreeditText() {
- candidate_window_view_->HidePreeditText();
+void CandidateWindowControllerImpl::Hide() {
+ if (candidate_window_view_)
+ candidate_window_view_->GetWidget()->Close();
+ if (infolist_window_)
+ infolist_window_->HideImmediately();
}
-void CandidateWindowControllerImpl::SetCursorLocation(
- const ibus::Rect& cursor_location,
- const ibus::Rect& composition_head) {
+void CandidateWindowControllerImpl::SetCursorBounds(
+ const gfx::Rect& cursor_bounds,
+ const gfx::Rect& composition_head) {
// A workaround for http://crosbug.com/6460. We should ignore very short Y
// move to prevent the window from shaking up and down.
const int kKeepPositionThreshold = 2; // px
- const gfx::Rect& last_location =
- candidate_window_view_->cursor_location();
- const int delta_y = abs(last_location.y() - cursor_location.y);
- if ((last_location.x() == cursor_location.x) &&
- (delta_y <= kKeepPositionThreshold)) {
- DVLOG(1) << "Ignored set_cursor_location signal to prevent window shake";
- return;
- }
-
- const gfx::Rect gfx_cursor_location = IBusRectToGfxRect(cursor_location);
- // Remember the cursor location.
- candidate_window_view_->set_cursor_location(gfx_cursor_location);
- candidate_window_view_->set_composition_head_location(
- IBusRectToGfxRect(composition_head));
- // Move the window per the cursor location.
- candidate_window_view_->ResizeAndMoveParentFrame();
- UpdateInfolistBounds();
-
- // Mode indicator controller also needs the cursor location.
- mode_indicator_controller_->SetCursorLocation(gfx_cursor_location);
-}
+ gfx::Rect last_bounds;
+ if (candidate_window_view_)
+ last_bounds = candidate_window_view_->GetAnchorRect();
-void CandidateWindowControllerImpl::UpdateAuxiliaryText(
- const std::string& utf8_text,
- bool visible) {
- // If it's not visible, hide the auxiliary text and return.
- if (!visible) {
- candidate_window_view_->HideAuxiliaryText();
+ const int delta_y = abs(last_bounds.y() - cursor_bounds.y());
+ if ((last_bounds.x() == cursor_bounds.x()) &&
+ (delta_y <= kKeepPositionThreshold)) {
+ DVLOG(1) << "Ignored set_cursor_bounds signal to prevent window shake";
return;
}
- candidate_window_view_->UpdateAuxiliaryText(utf8_text);
- candidate_window_view_->ShowAuxiliaryText();
-}
-// static
-void CandidateWindowControllerImpl::ConvertLookupTableToInfolistEntry(
- const CandidateWindow& candidate_window,
- std::vector<InfolistWindowView::Entry>* infolist_entries,
- size_t* focused_index) {
- DCHECK(focused_index);
- DCHECK(infolist_entries);
- *focused_index = InfolistWindowView::InvalidFocusIndex();
- infolist_entries->clear();
+ cursor_bounds_ = cursor_bounds;
+ composition_head_ = composition_head;
- const size_t cursor_index_in_page =
- candidate_window.cursor_position() % candidate_window.page_size();
+ // Remember the cursor bounds.
+ if (candidate_window_view_)
+ candidate_window_view_->SetCursorBounds(cursor_bounds, composition_head);
- for (size_t i = 0; i < candidate_window.candidates().size(); ++i) {
- const CandidateWindow::Entry& ibus_entry =
- candidate_window.candidates()[i];
- if (ibus_entry.description_title.empty() &&
- ibus_entry.description_body.empty())
- continue;
- InfolistWindowView::Entry entry;
- entry.title = ibus_entry.description_title;
- entry.body = ibus_entry.description_body;
- infolist_entries->push_back(entry);
- if (i == cursor_index_in_page)
- *focused_index = infolist_entries->size() - 1;
- }
+ // Mode indicator controller also needs the cursor bounds.
+ mode_indicator_controller_->SetCursorBounds(cursor_bounds);
}
-// static
-bool CandidateWindowControllerImpl::ShouldUpdateInfolist(
- const std::vector<InfolistWindowView::Entry>& old_entries,
- size_t old_focused_index,
- const std::vector<InfolistWindowView::Entry>& new_entries,
- size_t new_focused_index) {
- if (old_entries.empty() && new_entries.empty())
- return false;
- if (old_entries.size() != new_entries.size())
- return true;
- if (old_focused_index != new_focused_index)
- return true;
-
- for (size_t i = 0; i < old_entries.size(); ++i) {
- if (old_entries[i].title != new_entries[i].title ||
- old_entries[i].body != new_entries[i].body ) {
- return true;
- }
- }
- return false;
+void CandidateWindowControllerImpl::FocusStateChanged(bool is_focused) {
+ mode_indicator_controller_->FocusStateChanged(is_focused);
}
void CandidateWindowControllerImpl::UpdateLookupTable(
- const CandidateWindow& candidate_window,
+ const ui::CandidateWindow& candidate_window,
bool visible) {
// If it's not visible, hide the lookup table and return.
if (!visible) {
- candidate_window_view_->HideLookupTable();
- infolist_window_->Hide();
+ if (candidate_window_view_)
+ candidate_window_view_->HideLookupTable();
+ if (infolist_window_)
+ infolist_window_->HideImmediately();
// TODO(nona): Introduce unittests for crbug.com/170036.
latest_infolist_entries_.clear();
return;
}
+ if (!candidate_window_view_)
+ InitCandidateWindowView();
candidate_window_view_->UpdateCandidates(candidate_window);
candidate_window_view_->ShowLookupTable();
- size_t focused_index = 0;
- std::vector<InfolistWindowView::Entry> infolist_entries;
- ConvertLookupTableToInfolistEntry(candidate_window, &infolist_entries,
- &focused_index);
-
- // If there is no infolist entry, just hide.
- if (infolist_entries.empty()) {
- infolist_window_->Hide();
- return;
- }
+ bool has_highlighted = false;
+ std::vector<ui::InfolistEntry> infolist_entries;
+ candidate_window.GetInfolistEntries(&infolist_entries, &has_highlighted);
// If there is no change, just return.
- if (!ShouldUpdateInfolist(latest_infolist_entries_,
- latest_infolist_focused_index_,
- infolist_entries,
- focused_index)) {
+ if (latest_infolist_entries_ == infolist_entries)
return;
- }
latest_infolist_entries_ = infolist_entries;
- latest_infolist_focused_index_ = focused_index;
- InfolistWindowView* view = static_cast<InfolistWindowView*>(
- infolist_window_->GetContentsView());
- if (!view) {
- DLOG(ERROR) << "Contents View is not InfolistWindowView.";
+ if (infolist_entries.empty()) {
+ if (infolist_window_)
+ infolist_window_->HideImmediately();
return;
}
- view->Relayout(infolist_entries, focused_index);
- UpdateInfolistBounds();
-
- if (focused_index < infolist_entries.size())
- infolist_window_->DelayShow(kInfolistShowDelayMilliSeconds);
- else
- infolist_window_->DelayHide(kInfolistHideDelayMilliSeconds);
-}
-
-void CandidateWindowControllerImpl::UpdateInfolistBounds() {
- InfolistWindowView* view = static_cast<InfolistWindowView*>(
- infolist_window_->GetContentsView());
- if (!view)
+ // Highlight moves out of the infolist entries.
+ if (!has_highlighted) {
+ if (infolist_window_)
+ infolist_window_->HideWithDelay();
return;
- const gfx::Rect current_bounds =
- infolist_window_->GetClientAreaBoundsInScreen();
-
- gfx::Rect new_bounds;
- new_bounds.set_size(view->GetPreferredSize());
- new_bounds.set_origin(GetInfolistWindowPosition(
- frame_->GetClientAreaBoundsInScreen(),
- ash::Shell::GetScreen()->GetDisplayNearestWindow(
- infolist_window_->GetNativeView()).work_area(),
- new_bounds.size()));
+ }
- if (current_bounds != new_bounds)
- infolist_window_->SetBounds(new_bounds);
+ if (infolist_window_) {
+ infolist_window_->Relayout(infolist_entries);
+ } else {
+ infolist_window_ = new ash::ime::InfolistWindow(
+ candidate_window_view_, infolist_entries);
+ infolist_window_->InitWidget();
+ infolist_window_->GetWidget()->AddObserver(this);
+ }
+ infolist_window_->ShowWithDelay();
}
void CandidateWindowControllerImpl::UpdatePreeditText(
- const std::string& utf8_text, unsigned int cursor, bool visible) {
+ const base::string16& text, unsigned int cursor, bool visible) {
// If it's not visible, hide the preedit text and return.
- if (!visible || utf8_text.empty()) {
- candidate_window_view_->HidePreeditText();
+ if (!visible || text.empty()) {
+ if (candidate_window_view_)
+ candidate_window_view_->HidePreeditText();
return;
}
- candidate_window_view_->UpdatePreeditText(utf8_text);
+ if (!candidate_window_view_)
+ InitCandidateWindowView();
+ candidate_window_view_->UpdatePreeditText(text);
candidate_window_view_->ShowPreeditText();
}
-void CandidateWindowControllerImpl::OnCandidateCommitted(int index,
- int button,
- int flags) {
- IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler();
- if (engine)
- engine->CandidateClicked(index,
- static_cast<ibus::IBusMouseButton>(button),
- flags);
-}
-
-void CandidateWindowControllerImpl::OnCandidateWindowOpened() {
+void CandidateWindowControllerImpl::OnCandidateCommitted(int index) {
FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
- CandidateWindowOpened());
-}
-
-void CandidateWindowControllerImpl::OnCandidateWindowClosed() {
- FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
- CandidateWindowClosed());
+ CandidateClicked(index));
+}
+
+void CandidateWindowControllerImpl::OnWidgetClosing(views::Widget* widget) {
+ if (infolist_window_ && widget == infolist_window_->GetWidget()) {
+ widget->RemoveObserver(this);
+ infolist_window_ = NULL;
+ } else if (candidate_window_view_ &&
+ widget == candidate_window_view_->GetWidget()) {
+ widget->RemoveObserver(this);
+ candidate_window_view_->RemoveObserver(this);
+ candidate_window_view_ = NULL;
+ FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
+ CandidateWindowClosed());
+ }
}
void CandidateWindowControllerImpl::AddObserver(
observers_.RemoveObserver(observer);
}
-// static
-gfx::Point CandidateWindowControllerImpl::GetInfolistWindowPosition(
- const gfx::Rect& candidate_window_view_rect,
- const gfx::Rect& screen_rect,
- const gfx::Size& infolist_window_size) {
- gfx::Point result(candidate_window_view_rect.right(),
- candidate_window_view_rect.y());
-
- if (candidate_window_view_rect.right() + infolist_window_size.width() >
- screen_rect.right())
- result.set_x(candidate_window_view_rect.x() - infolist_window_size.width());
-
- if (candidate_window_view_rect.y() + infolist_window_size.height() >
- screen_rect.bottom())
- result.set_y(screen_rect.bottom() - infolist_window_size.height());
-
- return result;
-}
-
} // namespace input_method
} // namespace chromeos