Index: chrome/browser/chromeos/input_method/candidate_window_view.cc |
diff --git a/chrome/browser/chromeos/input_method/candidate_window_view.cc b/chrome/browser/chromeos/input_method/candidate_window_view.cc |
index bd97f6ea069fbf9a50e3b22226cd011ca65bf0c8..23acaa922c54ad65a5d1d5af3928e1f926c410c2 100644 |
--- a/chrome/browser/chromeos/input_method/candidate_window_view.cc |
+++ b/chrome/browser/chromeos/input_method/candidate_window_view.cc |
@@ -5,216 +5,72 @@ |
#include <string> |
-#include "ash/shell.h" |
#include "base/strings/utf_string_conversions.h" |
#include "chrome/browser/chromeos/input_method/candidate_view.h" |
#include "chrome/browser/chromeos/input_method/candidate_window_constants.h" |
-#include "chrome/browser/chromeos/input_method/hidable_area.h" |
#include "chromeos/ime/candidate_window.h" |
#include "ui/gfx/color_utils.h" |
+#include "ui/gfx/screen.h" |
#include "ui/native_theme/native_theme.h" |
#include "ui/views/background.h" |
#include "ui/views/border.h" |
+#include "ui/views/bubble/bubble_frame_view.h" |
#include "ui/views/controls/label.h" |
-#include "ui/views/layout/grid_layout.h" |
-#include "ui/views/widget/widget.h" |
+#include "ui/views/corewm/window_animations.h" |
+#include "ui/views/layout/box_layout.h" |
+#include "ui/views/layout/fill_layout.h" |
namespace chromeos { |
namespace input_method { |
namespace { |
-// VerticalCandidateLabel is used for rendering candidate text in |
-// the vertical candidate window. |
-class VerticalCandidateLabel : public views::Label { |
- public: |
- VerticalCandidateLabel() {} |
- |
- private: |
- virtual ~VerticalCandidateLabel() {} |
- // Returns the preferred size, but guarantees that the width has at |
- // least kMinCandidateLabelWidth pixels. |
- virtual gfx::Size GetPreferredSize() OVERRIDE { |
- gfx::Size size = Label::GetPreferredSize(); |
- // Hack. +2 is needed to prevent labels from getting elided like |
- // "abc..." in some cases. TODO(satorux): Figure out why it's |
- // necessary. |
- size.set_width(size.width() + 2); |
- if (size.width() < kMinCandidateLabelWidth) { |
- size.set_width(kMinCandidateLabelWidth); |
- } |
- if (size.width() > kMaxCandidateLabelWidth) { |
- size.set_width(kMaxCandidateLabelWidth); |
- } |
- return size; |
+class CandidateWindowBorder : public views::BubbleBorder { |
+ public: |
+ explicit CandidateWindowBorder(gfx::NativeView parent) |
+ : views::BubbleBorder(views::BubbleBorder::TOP_CENTER, |
+ views::BubbleBorder::NO_SHADOW, |
+ SK_ColorTRANSPARENT), |
+ parent_(parent), |
+ offset_(0) { |
+ set_paint_arrow(views::BubbleBorder::PAINT_NONE); |
} |
+ virtual ~CandidateWindowBorder() {} |
- DISALLOW_COPY_AND_ASSIGN(VerticalCandidateLabel); |
-}; |
- |
-// Wraps the given view with some padding, and returns it. |
-views::View* WrapWithPadding(views::View* view, const gfx::Insets& insets) { |
- views::View* wrapper = new views::View; |
- // Use GridLayout to give some insets inside. |
- views::GridLayout* layout = new views::GridLayout(wrapper); |
- wrapper->SetLayoutManager(layout); // |wrapper| owns |layout|. |
- layout->SetInsets(insets); |
- |
- views::ColumnSet* column_set = layout->AddColumnSet(0); |
- column_set->AddColumn( |
- views::GridLayout::FILL, views::GridLayout::FILL, |
- 1, views::GridLayout::USE_PREF, 0, 0); |
- layout->StartRow(0, 0); |
- |
- // Add the view contents. |
- layout->AddView(view); // |view| is owned by |wraper|, not |layout|. |
- return wrapper; |
-} |
- |
-// Creates shortcut text from the given index and the orientation. |
-base::string16 CreateShortcutText(size_t index, |
- const CandidateWindow& candidate_window) { |
- if (index >= candidate_window.candidates().size()) |
- return base::string16(); |
- std::string shortcut_text = candidate_window.candidates()[index].label; |
- if (!shortcut_text.empty() && |
- candidate_window.orientation() != CandidateWindow::VERTICAL) |
- shortcut_text += '.'; |
- return base::UTF8ToUTF16(shortcut_text); |
-} |
- |
-// Creates the shortcut label, and returns it (never returns NULL). |
-// The label text is not set in this function. |
-views::Label* CreateShortcutLabel( |
- CandidateWindow::Orientation orientation, const ui::NativeTheme& theme) { |
- // Create the shortcut label. The label will be owned by |
- // |wrapped_shortcut_label|, hence it's deleted when |
- // |wrapped_shortcut_label| is deleted. |
- views::Label* shortcut_label = new views::Label; |
- |
- if (orientation == CandidateWindow::VERTICAL) { |
- shortcut_label->SetFontList( |
- shortcut_label->font_list().DeriveFontListWithSizeDeltaAndStyle( |
- kFontSizeDelta, gfx::Font::BOLD)); |
- } else { |
- shortcut_label->SetFontList( |
- shortcut_label->font_list().DeriveFontListWithSizeDelta( |
- kFontSizeDelta)); |
- } |
- // TODO(satorux): Maybe we need to use language specific fonts for |
- // candidate_label, like Chinese font for Chinese input method? |
- shortcut_label->SetEnabledColor(theme.GetSystemColor( |
- ui::NativeTheme::kColorId_LabelEnabledColor)); |
- shortcut_label->SetDisabledColor(theme.GetSystemColor( |
- ui::NativeTheme::kColorId_LabelDisabledColor)); |
+ void set_offset(int offset) { offset_ = offset; } |
- return shortcut_label; |
-} |
+ private: |
+ // Overridden from views::BubbleBorder: |
+ virtual gfx::Rect GetBounds(const gfx::Rect& anchor_rect, |
+ const gfx::Size& content_size) const OVERRIDE { |
+ gfx::Rect bounds(content_size); |
+ bounds.set_origin(gfx::Point( |
+ anchor_rect.x() - offset_, |
+ is_arrow_on_top(arrow()) ? |
+ anchor_rect.bottom() : anchor_rect.y() - content_size.height())); |
-// Wraps the shortcut label, then decorates wrapped shortcut label |
-// and returns it (never returns NULL). |
-// The label text is not set in this function. |
-views::View* CreateWrappedShortcutLabel( |
- views::Label* shortcut_label, |
- CandidateWindow::Orientation orientation, |
- const ui::NativeTheme& theme) { |
- // Wrap it with padding. |
- const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6); |
- const gfx::Insets kHorizontalShortcutLabelInsets(1, 3, 1, 0); |
- const gfx::Insets insets = |
- (orientation == CandidateWindow::VERTICAL ? |
- kVerticalShortcutLabelInsets : |
- kHorizontalShortcutLabelInsets); |
- views::View* wrapped_shortcut_label = |
- WrapWithPadding(shortcut_label, insets); |
+ // It cannot use the normal logic of arrow offset for horizontal offscreen, |
+ // because the arrow must be in the content's edge. But CandidateWindow has |
+ // to be visible even when |anchor_rect| is out of the screen. |
+ gfx::Rect work_area = gfx::Screen::GetNativeScreen()-> |
+ GetDisplayNearestWindow(parent_).work_area(); |
+ if (bounds.right() > work_area.right()) |
+ bounds.set_x(work_area.right() - bounds.width()); |
+ if (bounds.x() < work_area.x()) |
+ bounds.set_x(work_area.x()); |
- // Add decoration based on the orientation. |
- if (orientation == CandidateWindow::VERTICAL) { |
- // Set the background color. |
- SkColor blackish = color_utils::AlphaBlend( |
- SK_ColorBLACK, |
- theme.GetSystemColor(ui::NativeTheme::kColorId_WindowBackground), |
- 0x40); |
- SkColor transparent_blakish = color_utils::AlphaBlend( |
- SK_ColorTRANSPARENT, blackish, 0xE0); |
- wrapped_shortcut_label->set_background( |
- views::Background::CreateSolidBackground(transparent_blakish)); |
- shortcut_label->SetBackgroundColor( |
- wrapped_shortcut_label->background()->get_color()); |
+ return bounds; |
} |
- return wrapped_shortcut_label; |
-} |
- |
-// Creates the candidate label, and returns it (never returns NULL). |
-// The label text is not set in this function. |
-views::Label* CreateCandidateLabel( |
- CandidateWindow::Orientation orientation) { |
- views::Label* candidate_label = NULL; |
- |
- // Create the candidate label. The label will be added to |this| as a |
- // child view, hence it's deleted when |this| is deleted. |
- if (orientation == CandidateWindow::VERTICAL) { |
- candidate_label = new VerticalCandidateLabel; |
- } else { |
- candidate_label = new views::Label; |
+ virtual gfx::Insets GetInsets() const OVERRIDE { |
+ return gfx::Insets(); |
} |
- // Change the font size. |
- candidate_label->SetFontList( |
- candidate_label->font_list().DeriveFontListWithSizeDelta(kFontSizeDelta)); |
- candidate_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
- |
- return candidate_label; |
-} |
- |
-// Creates the annotation label, and return it (never returns NULL). |
-// The label text is not set in this function. |
-views::Label* CreateAnnotationLabel( |
- CandidateWindow::Orientation orientation, const ui::NativeTheme& theme) { |
- // Create the annotation label. |
- views::Label* annotation_label = new views::Label; |
- |
- // Change the font size and color. |
- annotation_label->SetFontList( |
- annotation_label->font_list().DeriveFontListWithSizeDelta( |
- kFontSizeDelta)); |
- annotation_label->SetEnabledColor(theme.GetSystemColor( |
- ui::NativeTheme::kColorId_LabelDisabledColor)); |
- annotation_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
- |
- return annotation_label; |
-} |
- |
-// Computes shortcut column size. |
-gfx::Size ComputeShortcutColumnSize( |
- const CandidateWindow& candidate_window, |
- const ui::NativeTheme& theme) { |
- int shortcut_column_width = 0; |
- int shortcut_column_height = 0; |
- // Create the shortcut label. The label will be owned by |
- // |wrapped_shortcut_label|, hence it's deleted when |
- // |wrapped_shortcut_label| is deleted. |
- views::Label* shortcut_label = CreateShortcutLabel( |
- candidate_window.orientation(), theme); |
- scoped_ptr<views::View> wrapped_shortcut_label( |
- CreateWrappedShortcutLabel(shortcut_label, |
- candidate_window.orientation(), |
- theme)); |
- |
- // Compute the max width and height in shortcut labels. |
- // We'll create temporary shortcut labels, and choose the largest width and |
- // height. |
- for (size_t i = 0; i < candidate_window.page_size(); ++i) { |
- shortcut_label->SetText(CreateShortcutText(i, candidate_window)); |
- gfx::Size text_size = wrapped_shortcut_label->GetPreferredSize(); |
- shortcut_column_width = std::max(shortcut_column_width, text_size.width()); |
- shortcut_column_height = std::max(shortcut_column_height, |
- text_size.height()); |
- } |
+ gfx::NativeView parent_; |
+ int offset_; |
- return gfx::Size(shortcut_column_width, shortcut_column_height); |
-} |
+ DISALLOW_COPY_AND_ASSIGN(CandidateWindowBorder); |
+}; |
// Computes the page index. For instance, if the page size is 9, and the |
// cursor is pointing to 13th candidate, the page index will be 1 (2nd |
@@ -225,467 +81,178 @@ int ComputePageIndex(const CandidateWindow& candidate_window) { |
return -1; |
} |
-// Computes candidate column size. |
-gfx::Size ComputeCandidateColumnSize( |
- const CandidateWindow& candidate_window) { |
- int candidate_column_width = 0; |
- int candidate_column_height = 0; |
- scoped_ptr<views::Label> candidate_label( |
- CreateCandidateLabel(candidate_window.orientation())); |
- |
- // Compute the start index of |candidate_window_|. |
- const int current_page_index = ComputePageIndex(candidate_window); |
- if (current_page_index < 0) |
- return gfx::Size(0, 0); |
- const size_t start_from = current_page_index * candidate_window.page_size(); |
- |
- // Compute the max width and height in candidate labels. |
- // We'll create temporary candidate labels, and choose the largest width and |
- // height. |
- for (size_t i = 0; |
- i + start_from < candidate_window.candidates().size(); |
- ++i) { |
- const size_t index = start_from + i; |
- |
- candidate_label->SetText( |
- base::UTF8ToUTF16(candidate_window.candidates()[index].value)); |
- gfx::Size text_size = candidate_label->GetPreferredSize(); |
- candidate_column_width = std::max(candidate_column_width, |
- text_size.width()); |
- candidate_column_height = std::max(candidate_column_height, |
- text_size.height()); |
- } |
- |
- return gfx::Size(candidate_column_width, candidate_column_height); |
-} |
- |
-// Computes annotation column size. |
-gfx::Size ComputeAnnotationColumnSize( |
- const CandidateWindow& candidate_window, const ui::NativeTheme& theme) { |
- int annotation_column_width = 0; |
- int annotation_column_height = 0; |
- scoped_ptr<views::Label> annotation_label( |
- CreateAnnotationLabel(candidate_window.orientation(), theme)); |
- |
- // Compute the start index of |candidate_window_|. |
- const int current_page_index = ComputePageIndex(candidate_window); |
- if (current_page_index < 0) |
- return gfx::Size(0, 0); |
- const size_t start_from = current_page_index * candidate_window.page_size(); |
- |
- // Compute max width and height in annotation labels. |
- // We'll create temporary annotation labels, and choose the largest width and |
- // height. |
- for (size_t i = 0; |
- i + start_from < candidate_window.candidates().size(); |
- ++i) { |
- const size_t index = start_from + i; |
- |
- annotation_label->SetText( |
- base::UTF8ToUTF16(candidate_window.candidates()[index].annotation)); |
- gfx::Size text_size = annotation_label->GetPreferredSize(); |
- annotation_column_width = std::max(annotation_column_width, |
- text_size.width()); |
- annotation_column_height = std::max(annotation_column_height, |
- text_size.height()); |
- } |
- |
- return gfx::Size(annotation_column_width, annotation_column_height); |
-} |
- |
} // namespace |
-// InformationTextArea is a HidableArea having a single Label in it. |
-class InformationTextArea : public HidableArea { |
+class InformationTextArea : public views::View { |
public: |
+ // InformationTextArea's border is drawn as a separator, it should appear |
+ // at either top or bottom. |
+ enum BorderPosition { |
+ TOP, |
+ BOTTOM |
+ }; |
+ |
// Specify the alignment and initialize the control. |
- InformationTextArea(gfx::HorizontalAlignment align, int minWidth) |
- : minWidth_(minWidth) { |
+ InformationTextArea(gfx::HorizontalAlignment align, int min_width) |
+ : min_width_(min_width) { |
label_ = new views::Label; |
label_->SetHorizontalAlignment(align); |
+ label_->set_border(views::Border::CreateEmptyBorder(2, 2, 2, 4)); |
- const gfx::Insets kInsets(2, 2, 2, 4); |
- views::View* contents = WrapWithPadding(label_, kInsets); |
- SetContents(contents); |
- contents->set_border(views::Border::CreateSolidBorder( |
- 1, |
- GetNativeTheme()->GetSystemColor( |
- ui::NativeTheme::kColorId_MenuBorderColor))); |
- contents->set_background(views::Background::CreateSolidBackground( |
+ SetLayoutManager(new views::FillLayout()); |
+ AddChildView(label_); |
+ set_background(views::Background::CreateSolidBackground( |
color_utils::AlphaBlend(SK_ColorBLACK, |
GetNativeTheme()->GetSystemColor( |
ui::NativeTheme::kColorId_WindowBackground), |
0x10))); |
- label_->SetBackgroundColor(contents->background()->get_color()); |
} |
- // Set the displayed text. |
+ // Sets the text alignment. |
+ void SetAlignment(gfx::HorizontalAlignment alignment) { |
+ label_->SetHorizontalAlignment(alignment); |
+ } |
+ |
+ // Sets the displayed text. |
void SetText(const std::string& utf8_text) { |
label_->SetText(base::UTF8ToUTF16(utf8_text)); |
} |
+ // Sets the border thickness for top/bottom. |
+ void SetBorder(BorderPosition position) { |
+ set_border(views::Border::CreateSolidSidedBorder( |
+ (position == TOP) ? 1 : 0, 0, (position == BOTTOM) ? 1 : 0, 0, |
+ GetNativeTheme()->GetSystemColor( |
+ ui::NativeTheme::kColorId_MenuBorderColor))); |
+ } |
+ |
protected: |
virtual gfx::Size GetPreferredSize() OVERRIDE { |
- gfx::Size size = HidableArea::GetPreferredSize(); |
- // Hack. +2 is needed as the same reason as in VerticalCandidateLabel |
- size.set_width(size.width() + 2); |
- if (size.width() < minWidth_) { |
- size.set_width(minWidth_); |
- } |
+ gfx::Size size = views::View::GetPreferredSize(); |
+ size.SetToMax(gfx::Size(min_width_, 0)); |
return size; |
} |
private: |
views::Label* label_; |
- int minWidth_; |
+ int min_width_; |
DISALLOW_COPY_AND_ASSIGN(InformationTextArea); |
}; |
-CandidateView::CandidateView( |
- CandidateWindowView* parent_candidate_window, |
- int index_in_page, |
- CandidateWindow::Orientation orientation) |
- : index_in_page_(index_in_page), |
- orientation_(orientation), |
- parent_candidate_window_(parent_candidate_window), |
- shortcut_label_(NULL), |
- candidate_label_(NULL), |
- annotation_label_(NULL), |
- infolist_icon_(NULL), |
- infolist_icon_enabled_(false) { |
-} |
- |
-void CandidateView::Init(int shortcut_column_width, |
- int candidate_column_width, |
- int annotation_column_width, |
- int column_height) { |
- views::GridLayout* layout = new views::GridLayout(this); |
- SetLayoutManager(layout); // |this| owns |layout|. |
- |
- // Create Labels. |
- const ui::NativeTheme& theme = *GetNativeTheme(); |
- shortcut_label_ = CreateShortcutLabel(orientation_, theme); |
- views::View* wrapped_shortcut_label = |
- CreateWrappedShortcutLabel(shortcut_label_, orientation_, theme); |
- candidate_label_ = CreateCandidateLabel(orientation_); |
- annotation_label_ = CreateAnnotationLabel(orientation_, theme); |
- |
- // Initialize the column set with three columns. |
- views::ColumnSet* column_set = layout->AddColumnSet(0); |
- |
- // If orientation is vertical, each column width is fixed. |
- // Otherwise the width is resizable. |
- const views::GridLayout::SizeType column_type = |
- orientation_ == CandidateWindow::VERTICAL ? |
- views::GridLayout::FIXED : views::GridLayout::USE_PREF; |
- |
- const int padding_column_width = |
- orientation_ == CandidateWindow::VERTICAL ? 4 : 6; |
- |
- // Set shortcut column type and width. |
- column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, |
- 0, column_type, shortcut_column_width, 0); |
- column_set->AddPaddingColumn(0, padding_column_width); |
- |
- // Set candidate column type and width. |
- column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, |
- 1, views::GridLayout::USE_PREF, 0, |
- orientation_ == CandidateWindow::VERTICAL ? |
- candidate_column_width : 0); |
- column_set->AddPaddingColumn(0, padding_column_width); |
- |
- // Set annotation column type and width. |
- column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, |
- 0, column_type, annotation_column_width, 0); |
- |
- if (orientation_ == CandidateWindow::VERTICAL) { |
- column_set->AddPaddingColumn(0, 1); |
- column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, |
- views::GridLayout::FIXED, kInfolistIndicatorIconWidth, |
- 0); |
- column_set->AddPaddingColumn(0, 2); |
- } else { |
- column_set->AddPaddingColumn(0, padding_column_width); |
- } |
- |
- // Add the shortcut label, the candidate label, and annotation label. |
- layout->StartRow(0, 0); |
- // |wrapped_shortcut_label|, |candidate_label_|, and |annotation_label_| |
- // will be owned by |this|. |
- layout->AddView(wrapped_shortcut_label, |
- 1, // Column span. |
- 1, // Row span. |
- views::GridLayout::FILL, // Horizontal alignment. |
- views::GridLayout::FILL, // Vertical alignment. |
- -1, // Preferred width, not specified. |
- column_height); // Preferred height. |
- layout->AddView(candidate_label_, |
- 1, // Column span. |
- 1, // Row span. |
- views::GridLayout::FILL, // Horizontal alignment. |
- views::GridLayout::FILL, // Vertical alignment. |
- -1, // Preferred width, not specified. |
- column_height); // Preferred height. |
- layout->AddView(annotation_label_, |
- 1, // Column span. |
- 1, // Row span. |
- views::GridLayout::FILL, // Horizontal alignment. |
- views::GridLayout::FILL, // Vertical alignemnt. |
- -1, // Preferred width, not specified. |
- column_height); // Preferred height. |
- if (orientation_ == CandidateWindow::VERTICAL) { |
- infolist_icon_ = new views::View; |
- views::View* infolist_icon_wrapper = new views::View; |
- views::GridLayout* infolist_icon_layout = |
- new views::GridLayout(infolist_icon_wrapper); |
- // |infolist_icon_layout| is owned by |infolist_icon_wrapper|. |
- infolist_icon_wrapper->SetLayoutManager(infolist_icon_layout); |
- infolist_icon_layout->AddColumnSet(0)->AddColumn( |
- views::GridLayout::FILL, views::GridLayout::FILL, |
- 0, views::GridLayout::FIXED, kInfolistIndicatorIconWidth, 0); |
- infolist_icon_layout->AddPaddingRow(0, kInfolistIndicatorIconPadding); |
- infolist_icon_layout->StartRow(1.0, 0); // infolist_icon_ is resizable. |
- // |infolist_icon_| is owned by |infolist_icon_wrapper|. |
- infolist_icon_layout->AddView(infolist_icon_); |
- infolist_icon_layout->AddPaddingRow(0, kInfolistIndicatorIconPadding); |
- // |infolist_icon_wrapper| is owned by |this|. |
- layout->AddView(infolist_icon_wrapper); |
- } |
- UpdateLabelBackgroundColors(); |
-} |
- |
-void CandidateView::SetCandidateText(const base::string16& text) { |
- candidate_label_->SetText(text); |
-} |
- |
-void CandidateView::SetShortcutText(const base::string16& text) { |
- shortcut_label_->SetText(text); |
-} |
- |
-void CandidateView::SetAnnotationText(const base::string16& text) { |
- annotation_label_->SetText(text); |
-} |
- |
-void CandidateView::SetInfolistIcon(bool enable) { |
- if (!infolist_icon_ || (infolist_icon_enabled_ == enable)) |
- return; |
- infolist_icon_enabled_ = enable; |
- infolist_icon_->set_background( |
- enable ? |
- views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( |
- ui::NativeTheme::kColorId_FocusedBorderColor)) : |
- NULL); |
- UpdateLabelBackgroundColors(); |
- SchedulePaint(); |
-} |
- |
-void CandidateView::Select() { |
- set_background( |
- views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( |
- ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); |
- set_border(views::Border::CreateSolidBorder( |
- 1, GetNativeTheme()->GetSystemColor( |
- ui::NativeTheme::kColorId_FocusedBorderColor))); |
- UpdateLabelBackgroundColors(); |
- // Need to call SchedulePaint() for background and border color changes. |
- SchedulePaint(); |
-} |
- |
-void CandidateView::Unselect() { |
- set_background(NULL); |
- set_border(NULL); |
- UpdateLabelBackgroundColors(); |
- SchedulePaint(); // See comments at Select(). |
-} |
- |
-void CandidateView::SetRowEnabled(bool enabled) { |
- shortcut_label_->SetEnabled(enabled); |
-} |
- |
-gfx::Point CandidateView::GetCandidateLabelPosition() const { |
- return candidate_label_->GetMirroredPosition(); |
-} |
- |
-bool CandidateView::OnMousePressed(const ui::MouseEvent& event) { |
- // TODO(kinaba): On Windows and MacOS, candidate windows typically commits a |
- // candidate at OnMouseReleased event. We have chosen OnMousePressed here for |
- // working around several obstacle rising from views implementation over GTK. |
- // See: http://crosbug.com/11423#c11. Since we have moved from GTK to Aura, |
- // the reasoning should have became obsolete. We might want to reconsider |
- // implementing mouse-up selection. |
- SelectCandidateAt(event.location()); |
- return false; |
-} |
- |
-void CandidateView::OnGestureEvent(ui::GestureEvent* event) { |
- if (event->type() == ui::ET_GESTURE_TAP) { |
- SelectCandidateAt(event->location()); |
- event->SetHandled(); |
- return; |
- } |
- View::OnGestureEvent(event); |
-} |
- |
-void CandidateView::SelectCandidateAt(const gfx::Point& location) { |
- gfx::Point location_in_candidate_window = location; |
- views::View::ConvertPointToTarget(this, parent_candidate_window_, |
- &location_in_candidate_window); |
- parent_candidate_window_->OnCandidatePressed(location_in_candidate_window); |
- parent_candidate_window_->CommitCandidate(); |
-} |
- |
-void CandidateView::UpdateLabelBackgroundColors() { |
- SkColor color = background() ? |
- background()->get_color() : |
- GetNativeTheme()->GetSystemColor( |
- ui::NativeTheme::kColorId_WindowBackground); |
- if (orientation_ != CandidateWindow::VERTICAL) |
- shortcut_label_->SetBackgroundColor(color); |
- candidate_label_->SetBackgroundColor(color); |
- annotation_label_->SetBackgroundColor(color); |
-} |
- |
-CandidateWindowView::CandidateWindowView(views::Widget* parent_frame) |
+CandidateWindowView::CandidateWindowView(gfx::NativeView parent) |
: selected_candidate_index_in_page_(-1), |
- parent_frame_(parent_frame), |
- preedit_area_(NULL), |
- header_area_(NULL), |
- candidate_area_(NULL), |
- footer_area_(NULL), |
- previous_shortcut_column_size_(0, 0), |
- previous_candidate_column_size_(0, 0), |
- previous_annotation_column_size_(0, 0), |
should_show_at_composition_head_(false), |
should_show_upper_side_(false), |
was_candidate_window_open_(false) { |
-} |
- |
-CandidateWindowView::~CandidateWindowView() { |
-} |
+ set_parent_window(parent); |
+ set_margins(gfx::Insets()); |
-void CandidateWindowView::Init() { |
// Set the background and the border of the view. |
+ ui::NativeTheme* theme = GetNativeTheme(); |
set_background( |
- views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( |
+ views::Background::CreateSolidBackground(theme->GetSystemColor( |
ui::NativeTheme::kColorId_WindowBackground))); |
set_border(views::Border::CreateSolidBorder( |
- 1, GetNativeTheme()->GetSystemColor( |
- ui::NativeTheme::kColorId_MenuBorderColor))); |
- |
- // Create areas. |
- preedit_area_ = new InformationTextArea(gfx::ALIGN_LEFT, |
- kMinPreeditAreaWidth); |
- header_area_ = new InformationTextArea(gfx::ALIGN_LEFT, 0); |
- candidate_area_ = new HidableArea; |
- candidate_area_->SetContents(new views::View); |
- footer_area_ = new InformationTextArea(gfx::ALIGN_RIGHT, 0); |
- |
- // Set the window layout of the view |
- views::GridLayout* layout = new views::GridLayout(this); |
- SetLayoutManager(layout); // |this| owns |layout|. |
- views::ColumnSet* column_set = layout->AddColumnSet(0); |
- column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, |
- 0, views::GridLayout::USE_PREF, 0, 0); |
- |
- // Add the preedit area |
- layout->StartRow(0, 0); |
- layout->AddView(preedit_area_); // |preedit_area_| is owned by |this|. |
+ 1, theme->GetSystemColor(ui::NativeTheme::kColorId_MenuBorderColor))); |
+ |
+ SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); |
+ auxiliary_text_ = new InformationTextArea(gfx::ALIGN_RIGHT, 0); |
+ preedit_ = new InformationTextArea(gfx::ALIGN_LEFT, kMinPreeditAreaWidth); |
+ candidate_area_ = new views::View; |
+ auxiliary_text_->SetVisible(false); |
+ preedit_->SetVisible(false); |
+ candidate_area_->SetVisible(false); |
+ preedit_->SetBorder(InformationTextArea::BOTTOM); |
+ if (candidate_window_.orientation() == CandidateWindow::VERTICAL) { |
+ AddChildView(preedit_); |
+ AddChildView(candidate_area_); |
+ AddChildView(auxiliary_text_); |
+ auxiliary_text_->SetBorder(InformationTextArea::TOP); |
+ candidate_area_->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kVertical, 0, 0, 0)); |
+ } else { |
+ AddChildView(preedit_); |
+ AddChildView(auxiliary_text_); |
+ AddChildView(candidate_area_); |
+ auxiliary_text_->SetAlignment(gfx::ALIGN_LEFT); |
+ auxiliary_text_->SetBorder(InformationTextArea::BOTTOM); |
+ candidate_area_->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kHorizontal, 0, 0, 0)); |
+ } |
+} |
- // Add the header area. |
- layout->StartRow(0, 0); |
- layout->AddView(header_area_); // |header_area_| is owned by |this|. |
+CandidateWindowView::~CandidateWindowView() { |
+} |
- // Add the candidate area. |
- layout->StartRow(0, 0); |
- layout->AddView(candidate_area_); // |candidate_area_| is owned by |this|. |
+views::Widget* CandidateWindowView::InitWidget() { |
+ views::Widget* widget = BubbleDelegateView::CreateBubble(this); |
- // Add the footer area. |
- layout->StartRow(0, 0); |
- layout->AddView(footer_area_); // |footer_area_| is owned by |this|. |
-} |
+ views::corewm::SetWindowVisibilityAnimationType( |
+ widget->GetNativeView(), |
+ views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE); |
-void CandidateWindowView::HideAll() { |
- parent_frame_->Hide(); |
- NotifyIfCandidateWindowOpenedOrClosed(); |
+ GetBubbleFrameView()->SetBubbleBorder( |
+ new CandidateWindowBorder(parent_window())); |
+ return widget; |
} |
-void CandidateWindowView::UpdateParentArea() { |
- if (candidate_area_->IsShown() || |
- header_area_->IsShown() || |
- footer_area_->IsShown() || |
- preedit_area_->IsShown()) { |
- ResizeAndMoveParentFrame(); |
- parent_frame_->Show(); |
+void CandidateWindowView::UpdateVisibility() { |
+ if (candidate_area_->visible() || auxiliary_text_->visible() || |
+ preedit_->visible()) { |
+ SizeToContents(); |
} else { |
- parent_frame_->Hide(); |
+ GetWidget()->Close(); |
} |
- NotifyIfCandidateWindowOpenedOrClosed(); |
} |
void CandidateWindowView::HideLookupTable() { |
- candidate_area_->Hide(); |
- header_area_->Hide(); |
- footer_area_->Hide(); |
- UpdateParentArea(); |
+ candidate_area_->SetVisible(false); |
+ UpdateVisibility(); |
} |
void CandidateWindowView::HidePreeditText() { |
- preedit_area_->Hide(); |
- UpdateParentArea(); |
+ preedit_->SetVisible(false); |
+ UpdateVisibility(); |
} |
void CandidateWindowView::ShowPreeditText() { |
- preedit_area_->Show(); |
- UpdateParentArea(); |
+ preedit_->SetVisible(true); |
+ UpdateVisibility(); |
} |
void CandidateWindowView::UpdatePreeditText(const std::string& utf8_text) { |
- preedit_area_->SetText(utf8_text); |
+ preedit_->SetText(utf8_text); |
} |
void CandidateWindowView::ShowLookupTable() { |
- if (!candidate_area_->IsShown()) |
- should_show_upper_side_ = false; |
- candidate_area_->Show(); |
- |
- // Show auxiliary text. |
- if (!candidate_window_.is_auxiliary_text_visible()) { |
- header_area_->Hide(); |
- footer_area_->Hide(); |
- } else if (candidate_window_.orientation() == CandidateWindow::HORIZONTAL) { |
- header_area_->Show(); |
- footer_area_->Hide(); |
- } else { |
- header_area_->Hide(); |
- footer_area_->Show(); |
- } |
- |
- UpdateParentArea(); |
-} |
- |
-void CandidateWindowView::NotifyIfCandidateWindowOpenedOrClosed() { |
- bool is_open = IsCandidateWindowOpen(); |
- if (!was_candidate_window_open_ && is_open) { |
- FOR_EACH_OBSERVER(Observer, observers_, OnCandidateWindowOpened()); |
- } else if (was_candidate_window_open_ && !is_open) { |
- FOR_EACH_OBSERVER(Observer, observers_, OnCandidateWindowClosed()); |
- } |
- was_candidate_window_open_ = is_open; |
-} |
- |
-bool CandidateWindowView::ShouldUpdateCandidateViews( |
- const CandidateWindow& old_candidate_window, |
- const CandidateWindow& new_candidate_window) { |
- return !old_candidate_window.IsEqual(new_candidate_window); |
+ candidate_area_->SetVisible(true); |
+ auxiliary_text_->SetVisible(candidate_window_.is_auxiliary_text_visible()); |
+ UpdateVisibility(); |
} |
void CandidateWindowView::UpdateCandidates( |
const CandidateWindow& new_candidate_window) { |
- const bool should_update = ShouldUpdateCandidateViews(candidate_window_, |
- new_candidate_window); |
// Updating the candidate views is expensive. We'll skip this if possible. |
- if (should_update) { |
+ if (!candidate_window_.IsEqual(new_candidate_window)) { |
+ if (candidate_window_.orientation() != new_candidate_window.orientation()) { |
+ // If the new layout is vertical, the aux text should appear at the |
+ // bottom. If horizontal, it should appear between preedit and candidates. |
+ if (new_candidate_window.orientation() == CandidateWindow::VERTICAL) { |
+ ReorderChildView(auxiliary_text_, -1); |
+ auxiliary_text_->SetAlignment(gfx::ALIGN_RIGHT); |
+ auxiliary_text_->SetBorder(InformationTextArea::TOP); |
+ candidate_area_->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kVertical, 0, 0, 0)); |
+ } else { |
+ ReorderChildView(auxiliary_text_, 1); |
+ auxiliary_text_->SetAlignment(gfx::ALIGN_LEFT); |
+ auxiliary_text_->SetBorder(InformationTextArea::BOTTOM); |
+ candidate_area_->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kHorizontal, 0, 0, 0)); |
+ } |
+ } |
+ |
// Initialize candidate views if necessary. |
MaybeInitializeCandidateViews(new_candidate_window); |
@@ -693,57 +260,52 @@ void CandidateWindowView::UpdateCandidates( |
= new_candidate_window.show_window_at_composition(); |
// Compute the index of the current page. |
const int current_page_index = ComputePageIndex(new_candidate_window); |
- if (current_page_index < 0) { |
+ if (current_page_index < 0) |
return; |
- } |
// Update the candidates in the current page. |
const size_t start_from = |
current_page_index * new_candidate_window.page_size(); |
- // In some cases, engines send empty shortcut labels. For instance, |
- // ibus-mozc sends empty labels when they show suggestions. In this |
- // case, we should not show shortcut labels. |
- bool no_shortcut_mode = true; |
- for (size_t i = 0; i < new_candidate_window.candidates().size(); ++i) { |
- if (!new_candidate_window.candidates()[i].label.empty()) { |
- no_shortcut_mode = false; |
- break; |
- } |
- } |
- |
+ int max_shortcut_width = 0; |
+ int max_candidate_width = 0; |
for (size_t i = 0; i < candidate_views_.size(); ++i) { |
const size_t index_in_page = i; |
const size_t candidate_index = start_from + index_in_page; |
CandidateView* candidate_view = candidate_views_[index_in_page]; |
- // Set the shortcut text. |
- if (no_shortcut_mode) { |
- candidate_view->SetShortcutText(base::string16()); |
- } else { |
- // At this moment, we don't use labels sent from engines for UX |
- // reasons. First, we want to show shortcut labels in empty rows |
- // (ex. show 6, 7, 8, ... in empty rows when the number of |
- // candidates is 5). Second, we want to add a period after each |
- // shortcut label when the candidate window is horizontal. |
- candidate_view->SetShortcutText( |
- CreateShortcutText(i, new_candidate_window)); |
- } |
// Set the candidate text. |
- if (candidate_index < new_candidate_window.candidates().size()) { |
- const CandidateWindow::Entry& entry = |
- new_candidate_window.candidates()[candidate_index]; |
- candidate_view->SetCandidateText(base::UTF8ToUTF16(entry.value)); |
- candidate_view->SetAnnotationText(base::UTF8ToUTF16(entry.annotation)); |
- candidate_view->SetRowEnabled(true); |
- candidate_view->SetInfolistIcon(!entry.description_title.empty()); |
+ if (candidate_index < new_candidate_window.candidates().size()) { |
+ const CandidateWindow::Entry& entry = |
+ new_candidate_window.candidates()[candidate_index]; |
+ candidate_view->SetEntry(entry); |
+ candidate_view->SetState(views::Button::STATE_NORMAL); |
+ candidate_view->SetInfolistIcon(!entry.description_title.empty()); |
} else { |
// Disable the empty row. |
- candidate_view->SetCandidateText(base::string16()); |
- candidate_view->SetAnnotationText(base::string16()); |
- candidate_view->SetRowEnabled(false); |
+ candidate_view->SetEntry(CandidateWindow::Entry()); |
+ candidate_view->SetState(views::Button::STATE_DISABLED); |
candidate_view->SetInfolistIcon(false); |
} |
+ if (new_candidate_window.orientation() == CandidateWindow::VERTICAL) { |
+ int shortcut_width = 0; |
+ int candidate_width = 0; |
+ candidate_views_[i]->GetPreferredWidths( |
+ &shortcut_width, &candidate_width); |
+ max_shortcut_width = std::max(max_shortcut_width, shortcut_width); |
+ max_candidate_width = std::max(max_candidate_width, candidate_width); |
+ } |
+ } |
+ if (new_candidate_window.orientation() == CandidateWindow::VERTICAL) { |
+ for (size_t i = 0; i < candidate_views_.size(); ++i) |
+ candidate_views_[i]->SetWidths(max_shortcut_width, max_candidate_width); |
} |
+ |
+ CandidateWindowBorder* border = static_cast<CandidateWindowBorder*>( |
+ GetBubbleFrameView()->bubble_border()); |
+ if (new_candidate_window.orientation() == CandidateWindow::VERTICAL) |
+ border->set_offset(max_shortcut_width); |
+ else |
+ border->set_offset(0); |
} |
// Update the current candidate window. We'll use candidate_window_ from here. |
// Note that SelectCandidateAt() uses candidate_window_. |
@@ -761,142 +323,44 @@ void CandidateWindowView::UpdateCandidates( |
if (0 <= selected_candidate_index_in_page_ && |
static_cast<size_t>(selected_candidate_index_in_page_) < |
candidate_views_.size()) { |
- candidate_views_[selected_candidate_index_in_page_]->Unselect(); |
+ candidate_views_[selected_candidate_index_in_page_]->SetState( |
+ views::Button::STATE_NORMAL); |
selected_candidate_index_in_page_ = -1; |
} |
} |
// Updates auxiliary text |
- if (candidate_window_.is_auxiliary_text_visible()) { |
- header_area_->SetText(candidate_window_.auxiliary_text()); |
- footer_area_->SetText(candidate_window_.auxiliary_text()); |
- } |
+ auxiliary_text_->SetVisible(candidate_window_.is_auxiliary_text_visible()); |
+ auxiliary_text_->SetText(candidate_window_.auxiliary_text()); |
+} |
+ |
+void CandidateWindowView::SetCursorBounds(const gfx::Rect& cursor_bounds, |
+ const gfx::Rect& composition_head) { |
+ if (candidate_window_.show_window_at_composition()) |
+ SetAnchorRect(composition_head); |
+ else |
+ SetAnchorRect(cursor_bounds); |
} |
void CandidateWindowView::MaybeInitializeCandidateViews( |
const CandidateWindow& candidate_window) { |
const CandidateWindow::Orientation orientation = |
candidate_window.orientation(); |
- const int page_size = candidate_window.page_size(); |
- views::View* candidate_area_contents = candidate_area_->contents(); |
- |
- // Current column width. |
- gfx::Size shortcut_column_size(0, 0); |
- gfx::Size candidate_column_size(0,0); |
- gfx::Size annotation_column_size(0, 0); |
- |
- // If orientation is horizontal, don't need to compute width, |
- // because each label is left aligned. |
- if (orientation == CandidateWindow::VERTICAL) { |
- const ui::NativeTheme& theme = *GetNativeTheme(); |
- shortcut_column_size = ComputeShortcutColumnSize(candidate_window, theme); |
- candidate_column_size = ComputeCandidateColumnSize(candidate_window); |
- annotation_column_size = ComputeAnnotationColumnSize(candidate_window, |
- theme); |
- } |
- |
- // If the requested number of views matches the number of current views, and |
- // previous and current column width are same, just reuse these. |
- // |
- // Note that the early exit logic is not only useful for improving |
- // performance, but also necessary for the horizontal candidate window |
- // to be redrawn properly. If we get rid of the logic, the horizontal |
- // candidate window won't get redrawn properly for some reason when |
- // there is no size change. You can test this by removing "return" here |
- // and type "ni" with Pinyin input method. |
- if (static_cast<int>(candidate_views_.size()) == page_size && |
- candidate_window_.orientation() == orientation && |
- previous_shortcut_column_size_ == shortcut_column_size && |
- previous_candidate_column_size_ == candidate_column_size && |
- previous_annotation_column_size_ == annotation_column_size) { |
- return; |
- } |
- |
- // Update the previous column widths. |
- previous_shortcut_column_size_ = shortcut_column_size; |
- previous_candidate_column_size_ = candidate_column_size; |
- previous_annotation_column_size_ = annotation_column_size; |
- |
- // Clear the existing candidate_views if any. |
- for (size_t i = 0; i < candidate_views_.size(); ++i) { |
- candidate_area_contents->RemoveChildView(candidate_views_[i]); |
- // Delete the view after getting out the current message loop iteration. |
- base::MessageLoop::current()->DeleteSoon(FROM_HERE, candidate_views_[i]); |
- } |
- candidate_views_.clear(); |
- selected_candidate_index_in_page_ = -1; // Invalidates the index. |
- |
- views::GridLayout* layout = new views::GridLayout(candidate_area_contents); |
- // |candidate_area_contents| owns |layout|. |
- candidate_area_contents->SetLayoutManager(layout); |
- // Initialize the column set. |
- views::ColumnSet* column_set = layout->AddColumnSet(0); |
- if (orientation == CandidateWindow::VERTICAL) { |
- column_set->AddColumn(views::GridLayout::FILL, |
- views::GridLayout::FILL, |
- 1, views::GridLayout::USE_PREF, 0, 0); |
- } else { |
- for (int i = 0; i < page_size; ++i) { |
- column_set->AddColumn(views::GridLayout::FILL, |
- views::GridLayout::FILL, |
- 0, views::GridLayout::USE_PREF, 0, 0); |
- } |
- } |
+ const size_t page_size = candidate_window.page_size(); |
- // Set insets so the border of the selected candidate is drawn inside of |
- // the border of the main candidate window, but we don't have the inset |
- // at the top and the bottom as we have the borders of the header and |
- // footer areas. |
- const gfx::Insets kCandidateAreaInsets(0, 1, 0, 1); |
- layout->SetInsets(kCandidateAreaInsets.top(), |
- kCandidateAreaInsets.left(), |
- kCandidateAreaInsets.bottom(), |
- kCandidateAreaInsets.right()); |
+ // Reset all candidate_views_ when orientation changes. |
+ if (orientation != candidate_window_.orientation()) |
+ STLDeleteElements(&candidate_views_); |
- // Use maximum height for all rows in candidate area. |
- const int kColumnHeight = std::max(shortcut_column_size.height(), |
- std::max(candidate_column_size.height(), |
- annotation_column_size.height())); |
- |
- // Add views to the candidate area. |
- if (orientation == CandidateWindow::HORIZONTAL) { |
- layout->StartRow(0, 0); |
+ while (page_size < candidate_views_.size()) { |
+ delete candidate_views_.back(); |
+ candidate_views_.pop_back(); |
} |
- |
- for (int i = 0; i < page_size; ++i) { |
- CandidateView* candidate_row = new CandidateView(this, i, orientation); |
- candidate_row->Init(shortcut_column_size.width(), |
- candidate_column_size.width(), |
- annotation_column_size.width(), |
- kColumnHeight); |
- candidate_views_.push_back(candidate_row); |
- if (orientation == CandidateWindow::VERTICAL) { |
- layout->StartRow(0, 0); |
- } |
- // |candidate_row| will be owned by |candidate_area_contents|. |
- layout->AddView(candidate_row, |
- 1, // Column span. |
- 1, // Row span. |
- // Horizontal alignment. |
- orientation == CandidateWindow::VERTICAL ? |
- views::GridLayout::FILL : views::GridLayout::CENTER, |
- views::GridLayout::CENTER, // Vertical alignment. |
- -1, // Preferred width, not specified. |
- kColumnHeight); // Preferred height. |
+ while (page_size > candidate_views_.size()) { |
+ CandidateView* new_candidate = new CandidateView(this, orientation); |
+ candidate_area_->AddChildView(new_candidate); |
+ candidate_views_.push_back(new_candidate); |
} |
- |
- // Compute views size in |layout|. |
- // If we don't call this function, GetHorizontalOffset() often |
- // returns invalid value (returns 0), then candidate window |
- // moves right from the correct position in ResizeAndMoveParentFrame(). |
- // TODO(nhiroki): Figure out why it returns invalid value. |
- // It seems that the x-position of the candidate labels is not set. |
- layout->Layout(candidate_area_contents); |
-} |
- |
-bool CandidateWindowView::IsCandidateWindowOpen() const { |
- return !should_show_at_composition_head_ && |
- candidate_area_->visible() && candidate_area_->IsShown(); |
} |
void CandidateWindowView::SelectCandidateAt(int index_in_page) { |
@@ -914,123 +378,25 @@ void CandidateWindowView::SelectCandidateAt(int index_in_page) { |
return; |
} |
- // Unselect the currently selected candidate. |
- if (0 <= selected_candidate_index_in_page_ && |
- static_cast<size_t>(selected_candidate_index_in_page_) < |
- candidate_views_.size()) { |
- candidate_views_[selected_candidate_index_in_page_]->Unselect(); |
- } |
// Remember the currently selected candidate index in the current page. |
selected_candidate_index_in_page_ = index_in_page; |
// Select the candidate specified by index_in_page. |
- candidate_views_[index_in_page]->Select(); |
+ candidate_views_[index_in_page]->SetState(views::Button::STATE_PRESSED); |
// Update the cursor indexes in the model. |
candidate_window_.set_cursor_position(cursor_absolute_index); |
} |
-void CandidateWindowView::OnCandidatePressed( |
- const gfx::Point& location) { |
+void CandidateWindowView::ButtonPressed(views::Button* sender, |
+ const ui::Event& event) { |
for (size_t i = 0; i < candidate_views_.size(); ++i) { |
- gfx::Point converted_location = location; |
- views::View::ConvertPointToTarget(this, candidate_views_[i], |
- &converted_location); |
- if (candidate_views_[i]->HitTestPoint(converted_location)) { |
- SelectCandidateAt(i); |
- break; |
+ if (sender == candidate_views_[i]) { |
+ FOR_EACH_OBSERVER(Observer, observers_, OnCandidateCommitted(i)); |
+ return; |
} |
} |
} |
-void CandidateWindowView::CommitCandidate() { |
- if (!(0 <= selected_candidate_index_in_page_ && |
- static_cast<size_t>(selected_candidate_index_in_page_) < |
- candidate_views_.size())) { |
- return; // Out of range, do nothing. |
- } |
- |
- FOR_EACH_OBSERVER(Observer, observers_, |
- OnCandidateCommitted(selected_candidate_index_in_page_)); |
-} |
- |
-void CandidateWindowView::ResizeAndMoveParentFrame() { |
- // If rendering operation comes from mozc-engine, uses mozc specific bounds, |
- // otherwise candidate window is shown under the cursor. |
- const int x = should_show_at_composition_head_? |
- composition_head_bounds_.x() : cursor_bounds_.x(); |
- // To avoid candidate-window overlapping, uses maximum y-position of mozc |
- // specific bounds and cursor bounds, because mozc-engine does not |
- // consider about multi-line composition. |
- const int y = should_show_at_composition_head_? |
- std::max(composition_head_bounds_.y(), cursor_bounds_.y()) : |
- cursor_bounds_.y(); |
- const int height = cursor_bounds_.height(); |
- const int horizontal_offset = GetHorizontalOffset(); |
- |
- gfx::Rect old_bounds = parent_frame_->GetClientAreaBoundsInScreen(); |
- gfx::Rect screen_bounds = ash::Shell::GetScreen()->GetDisplayMatching( |
- cursor_bounds_).work_area(); |
- // The size. |
- gfx::Rect frame_bounds = old_bounds; |
- frame_bounds.set_size(GetPreferredSize()); |
- |
- // The default position. |
- frame_bounds.set_x(x + horizontal_offset); |
- frame_bounds.set_y(y + height); |
- |
- // Handle overflow at the left and the top. |
- frame_bounds.set_x(std::max(frame_bounds.x(), screen_bounds.x())); |
- frame_bounds.set_y(std::max(frame_bounds.y(), screen_bounds.y())); |
- |
- // Handle overflow at the right. |
- const int right_overflow = frame_bounds.right() - screen_bounds.right(); |
- if (right_overflow > 0) { |
- frame_bounds.set_x(frame_bounds.x() - right_overflow); |
- } |
- |
- // Handle overflow at the bottom. |
- const int bottom_overflow = frame_bounds.bottom() - screen_bounds.bottom(); |
- |
- // To avoid flickering window position, the candidate window should be shown |
- // on upper side of composition string if it was shown there. |
- if (should_show_upper_side_ || bottom_overflow > 0) { |
- frame_bounds.set_y(frame_bounds.y() - height - frame_bounds.height()); |
- should_show_upper_side_ = true; |
- } |
- |
- // TODO(nona): check top_overflow here. |
- |
- // Move the window per the cursor bounds. |
- // SetBounds() is not cheap. Only call this when it is really changed. |
- if (frame_bounds != old_bounds) |
- parent_frame_->SetBounds(frame_bounds); |
-} |
- |
-int CandidateWindowView::GetHorizontalOffset() { |
- // Compute the horizontal offset if the candidate window is vertical. |
- if (!candidate_views_.empty() && |
- candidate_window_.orientation() == CandidateWindow::VERTICAL) { |
- return - candidate_views_[0]->GetCandidateLabelPosition().x(); |
- } |
- return 0; |
-} |
- |
-void CandidateWindowView::VisibilityChanged(View* starting_from, |
- bool is_visible) { |
- if (is_visible) { |
- // If the visibility of candidate window is changed, |
- // we should move the frame to the right position. |
- ResizeAndMoveParentFrame(); |
- } |
-} |
- |
-void CandidateWindowView::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
- // If the bounds(size) of candidate window is changed, |
- // we should move the frame to the right position. |
- View::OnBoundsChanged(previous_bounds); |
- ResizeAndMoveParentFrame(); |
-} |
- |
} // namespace input_method |
} // namespace chromeos |