Chromium Code Reviews| 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 7efcdd50994b888f980ff63ca467f5456aed2855..f1bf6d0194df04c0a3e3881cf946a150c7a56933 100644 |
| --- a/chrome/browser/chromeos/input_method/candidate_window_view.cc |
| +++ b/chrome/browser/chromeos/input_method/candidate_window_view.cc |
| @@ -5,213 +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->SetFont( |
| - shortcut_label->font().DeriveFont(kFontSizeDelta, gfx::Font::BOLD)); |
| - } else { |
| - shortcut_label->SetFont( |
| - shortcut_label->font().DeriveFont(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->SetFont( |
| - candidate_label->font().DeriveFont(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->SetFont( |
| - annotation_label->font().DeriveFont(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 |
| @@ -222,483 +81,192 @@ 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) { |
| -} |
| + set_parent_window(parent); |
| + set_margins(gfx::Insets()); |
| -CandidateWindowView::~CandidateWindowView() { |
| -} |
| - |
| -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(); |
| - UpdateParentArea(); |
| + candidate_area_->SetVisible(false); |
| + UpdateVisibility(); |
| } |
| void CandidateWindowView::HideAuxiliaryText() { |
| - header_area_->Hide(); |
| - footer_area_->Hide(); |
| - UpdateParentArea(); |
| + auxiliary_text_->SetVisible(false); |
| + UpdateVisibility(); |
| } |
| void CandidateWindowView::ShowAuxiliaryText() { |
| - // If candidate_area is not shown, shows auxiliary text at header_area. |
| - // We expect both header_area_ and footer_area_ contain same value. |
| - if (!candidate_area_->IsShown()) { |
| - header_area_->Show(); |
| - footer_area_->Hide(); |
| - } else { |
| - // If candidate_area is shown, shows auxiliary text with orientation. |
| - if (candidate_window_.orientation() == CandidateWindow::HORIZONTAL) { |
| - header_area_->Show(); |
| - footer_area_->Hide(); |
| - } else { |
| - footer_area_->Show(); |
| - header_area_->Hide(); |
| - } |
| - } |
| - UpdateParentArea(); |
| + auxiliary_text_->SetVisible(true); |
| + UpdateVisibility(); |
| } |
| void CandidateWindowView::UpdateAuxiliaryText(const std::string& utf8_text) { |
| - header_area_->SetText(utf8_text); |
| - footer_area_->SetText(utf8_text); |
| + auxiliary_text_->SetText(utf8_text); |
| ShowAuxiliaryText(); |
| } |
| 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(); |
| - 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); |
| + 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); |
|
Hiro Komatsu
2013/12/27 07:13:34
Please double check if TOP and BOTTOM are correct
Jun Mukai
2013/12/27 11:01:13
This is correct. When vertical, the aux text will
|
| + 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); |
| @@ -706,57 +274,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_. |
| @@ -774,136 +337,40 @@ 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; |
| } |
| } |
| } |
| +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(); |
| + const size_t page_size = candidate_window.page_size(); |
| - // Current column width. |
| - gfx::Size shortcut_column_size(0, 0); |
| - gfx::Size candidate_column_size(0,0); |
| - gfx::Size annotation_column_size(0, 0); |
| + // Reset all candidate_views_ when orientation changes. |
| + if (orientation != candidate_window_.orientation()) |
| + STLDeleteElements(&candidate_views_); |
| - // 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); |
| + while (page_size < candidate_views_.size()) { |
| + delete candidate_views_.back(); |
| + candidate_views_.pop_back(); |
| } |
| - |
| - // 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); |
| - } |
| - } |
| - |
| - // 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()); |
| - |
| - // 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); |
| - } |
| - |
| - 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) { |
| @@ -921,123 +388,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 |