1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/ime/infolist_window.h"
10 #include "ash/ime/candidate_window_constants.h"
11 #include "base/logging.h"
12 #include "grit/ash_strings.h"
13 #include "ui/base/l10n/l10n_util.h"
14 #include "ui/gfx/color_utils.h"
15 #include "ui/gfx/font.h"
16 #include "ui/native_theme/native_theme.h"
17 #include "ui/views/background.h"
18 #include "ui/views/border.h"
19 #include "ui/views/bubble/bubble_border.h"
20 #include "ui/views/bubble/bubble_frame_view.h"
21 #include "ui/views/controls/label.h"
22 #include "ui/views/corewm/window_animations.h"
23 #include "ui/views/layout/box_layout.h"
24 #include "ui/views/widget/widget.h"
30 // The width of an info-list.
31 const int kInfolistEntryWidth
= 200;
33 // The milliseconds of the delay to show the infolist window.
34 const int kInfolistShowDelayMilliSeconds
= 500;
35 // The milliseconds of the delay to hide the infolist window.
36 const int kInfolistHideDelayMilliSeconds
= 500;
38 ///////////////////////////////////////////////////////////////////////////////
40 // The BubbleBorder subclass to draw the border and determine its position.
41 class InfolistBorder
: public views::BubbleBorder
{
44 virtual ~InfolistBorder();
46 // views::BubbleBorder implementation.
47 virtual gfx::Rect
GetBounds(const gfx::Rect
& anchor_rect
,
48 const gfx::Size
& contents_size
) const OVERRIDE
;
49 virtual gfx::Insets
GetInsets() const OVERRIDE
;
52 DISALLOW_COPY_AND_ASSIGN(InfolistBorder
);
55 InfolistBorder::InfolistBorder()
56 : views::BubbleBorder(views::BubbleBorder::LEFT_CENTER
,
57 views::BubbleBorder::NO_SHADOW
,
58 SK_ColorTRANSPARENT
) {
59 set_paint_arrow(views::BubbleBorder::PAINT_NONE
);
62 InfolistBorder::~InfolistBorder() {}
64 gfx::Rect
InfolistBorder::GetBounds(const gfx::Rect
& anchor_rect
,
65 const gfx::Size
& contents_size
) const {
66 gfx::Rect
bounds(contents_size
);
67 bounds
.set_x(is_arrow_on_left(arrow()) ?
68 anchor_rect
.right() : anchor_rect
.x() - contents_size
.width());
69 // InfolistBorder modifies the vertical position based on the arrow offset
70 // although it doesn't draw the arrow. The arrow offset is the half of
71 // |contents_size| by default but can be modified through the off-screen logic
72 // in BubbleFrameView.
73 bounds
.set_y(anchor_rect
.y() + contents_size
.height() / 2 -
74 GetArrowOffset(contents_size
));
78 gfx::Insets
InfolistBorder::GetInsets() const {
79 // This has to be specified and return empty insets to place the infolist
80 // window without the gap.
86 // InfolistRow renderes a row of a infolist.
87 class InfolistEntryView
: public views::View
{
89 InfolistEntryView(const ui::InfolistEntry
& entry
,
90 const gfx::FontList
& title_font
,
91 const gfx::FontList
& description_font
);
92 virtual ~InfolistEntryView();
94 void SetEntry(const ui::InfolistEntry
& entry
);
97 // views::View implementation.
98 virtual gfx::Size
GetPreferredSize() OVERRIDE
;
100 void UpdateBackground();
102 ui::InfolistEntry entry_
;
104 // The title label. Owned by views hierarchy.
105 views::Label
* title_label_
;
107 // The description label. Owned by views hierarchy.
108 views::Label
* description_label_
;
110 DISALLOW_COPY_AND_ASSIGN(InfolistEntryView
);
113 InfolistEntryView::InfolistEntryView(const ui::InfolistEntry
& entry
,
114 const gfx::FontList
& title_font
,
115 const gfx::FontList
& description_font
)
117 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
119 title_label_
= new views::Label(entry
.title
);
120 title_label_
->SetPosition(gfx::Point(0, 0));
121 title_label_
->SetFontList(title_font
);
122 title_label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
123 title_label_
->set_border(
124 views::Border::CreateEmptyBorder(4, 7, 2, 4));
126 description_label_
= new views::Label(entry
.body
);
127 description_label_
->SetPosition(gfx::Point(0, 0));
128 description_label_
->SetFontList(description_font
);
129 description_label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
130 description_label_
->SetMultiLine(true);
131 description_label_
->SizeToFit(kInfolistEntryWidth
);
132 description_label_
->set_border(
133 views::Border::CreateEmptyBorder(2, 17, 4, 4));
134 AddChildView(title_label_
);
135 AddChildView(description_label_
);
139 InfolistEntryView::~InfolistEntryView() {}
141 void InfolistEntryView::SetEntry(const ui::InfolistEntry
& entry
) {
146 title_label_
->SetText(entry_
.title
);
147 description_label_
->SetText(entry_
.body
);
151 gfx::Size
InfolistEntryView::GetPreferredSize() {
152 return gfx::Size(kInfolistEntryWidth
, GetHeightForWidth(kInfolistEntryWidth
));
155 void InfolistEntryView::UpdateBackground() {
156 if (entry_
.highlighted
) {
158 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
159 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused
)));
161 views::Border::CreateSolidBorder(1, GetNativeTheme()->GetSystemColor(
162 ui::NativeTheme::kColorId_FocusedBorderColor
)));
164 set_background(NULL
);
165 set_border(views::Border::CreateEmptyBorder(1, 1, 1, 1));
170 ///////////////////////////////////////////////////////////////////////////////
173 InfolistWindow::InfolistWindow(views::View
* candidate_window
,
174 const std::vector
<ui::InfolistEntry
>& entries
)
175 : views::BubbleDelegateView(candidate_window
, views::BubbleBorder::NONE
),
176 title_font_(gfx::Font(kJapaneseFontName
, kFontSizeDelta
+ 15)),
177 description_font_(gfx::Font(kJapaneseFontName
, kFontSizeDelta
+ 11)) {
178 set_move_with_anchor(true);
179 set_margins(gfx::Insets());
182 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
183 ui::NativeTheme::kColorId_WindowBackground
)));
185 views::Border::CreateSolidBorder(1, GetNativeTheme()->GetSystemColor(
186 ui::NativeTheme::kColorId_MenuBorderColor
)));
188 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
190 views::Label
* caption_label
= new views::Label(
191 l10n_util::GetStringUTF16(IDS_ASH_IME_INFOLIST_WINDOW_TITLE
));
192 caption_label
->SetFontList(
193 caption_label
->font_list().DeriveFontList(kFontSizeDelta
- 2));
194 caption_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
195 caption_label
->SetEnabledColor(GetNativeTheme()->GetSystemColor(
196 ui::NativeTheme::kColorId_LabelEnabledColor
));
197 caption_label
->set_border(views::Border::CreateEmptyBorder(2, 2, 2, 2));
198 caption_label
->set_background(views::Background::CreateSolidBackground(
199 color_utils::AlphaBlend(SK_ColorBLACK
,
200 GetNativeTheme()->GetSystemColor(
201 ui::NativeTheme::kColorId_WindowBackground
),
204 AddChildView(caption_label
);
206 for (size_t i
= 0; i
< entries
.size(); ++i
) {
207 entry_views_
.push_back(
208 new InfolistEntryView(entries
[i
], title_font_
, description_font_
));
209 AddChildView(entry_views_
.back());
213 InfolistWindow::~InfolistWindow() {
216 void InfolistWindow::InitWidget() {
217 views::Widget
* widget
= views::BubbleDelegateView::CreateBubble(this);
218 views::corewm::SetWindowVisibilityAnimationType(
219 widget
->GetNativeView(),
220 views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
);
222 // BubbleFrameView will be initialized through CreateBubble.
223 GetBubbleFrameView()->SetBubbleBorder(new InfolistBorder());
227 void InfolistWindow::Relayout(const std::vector
<ui::InfolistEntry
>& entries
) {
229 for (; i
< entries
.size(); ++i
) {
230 if (i
< entry_views_
.size()) {
231 entry_views_
[i
]->SetEntry(entries
[i
]);
233 InfolistEntryView
* new_entry
= new InfolistEntryView(
234 entries
[i
], title_font_
, description_font_
);
235 AddChildView(new_entry
);
236 entry_views_
.push_back(new_entry
);
240 if (i
< entry_views_
.size()) {
241 for (; i
< entry_views_
.size(); ++i
)
242 delete entry_views_
[i
];
243 entry_views_
.resize(entries
.size());
247 GetBubbleFrameView()->bubble_border()->set_arrow_offset(0);
251 void InfolistWindow::ShowWithDelay() {
252 show_hide_timer_
.Start(
254 base::TimeDelta::FromMilliseconds(kInfolistShowDelayMilliSeconds
),
256 &views::Widget::Show
);
259 void InfolistWindow::HideWithDelay() {
260 show_hide_timer_
.Start(
262 base::TimeDelta::FromMilliseconds(kInfolistHideDelayMilliSeconds
),
264 &views::Widget::Close
);
267 void InfolistWindow::ShowImmediately() {
268 show_hide_timer_
.Stop();
272 void InfolistWindow::HideImmediately() {
273 show_hide_timer_
.Stop();
274 GetWidget()->Close();
277 void InfolistWindow::WindowClosing() {
278 show_hide_timer_
.Stop();