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 "ui/chromeos/ime/infolist_window.h"
10 #include "base/logging.h"
11 #include "ui/base/l10n/l10n_util.h"
12 #include "ui/chromeos/ime/candidate_window_constants.h"
13 #include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
14 #include "ui/gfx/color_utils.h"
15 #include "ui/native_theme/native_theme.h"
16 #include "ui/views/background.h"
17 #include "ui/views/border.h"
18 #include "ui/views/bubble/bubble_border.h"
19 #include "ui/views/bubble/bubble_frame_view.h"
20 #include "ui/views/controls/label.h"
21 #include "ui/views/layout/box_layout.h"
22 #include "ui/views/widget/widget.h"
23 #include "ui/wm/core/window_animations.h"
29 // The width of an info-list.
30 const int kInfolistEntryWidth
= 200;
32 // The milliseconds of the delay to show the infolist window.
33 const int kInfolistShowDelayMilliSeconds
= 500;
34 // The milliseconds of the delay to hide the infolist window.
35 const int kInfolistHideDelayMilliSeconds
= 500;
37 ///////////////////////////////////////////////////////////////////////////////
39 // The BubbleBorder subclass to draw the border and determine its position.
40 class InfolistBorder
: public views::BubbleBorder
{
43 ~InfolistBorder() override
;
45 // views::BubbleBorder implementation.
46 gfx::Rect
GetBounds(const gfx::Rect
& anchor_rect
,
47 const gfx::Size
& contents_size
) const override
;
48 gfx::Insets
GetInsets() const override
;
51 DISALLOW_COPY_AND_ASSIGN(InfolistBorder
);
54 InfolistBorder::InfolistBorder()
55 : views::BubbleBorder(views::BubbleBorder::LEFT_CENTER
,
56 views::BubbleBorder::NO_SHADOW
,
57 SK_ColorTRANSPARENT
) {
58 set_paint_arrow(views::BubbleBorder::PAINT_NONE
);
61 InfolistBorder::~InfolistBorder() {}
63 gfx::Rect
InfolistBorder::GetBounds(const gfx::Rect
& anchor_rect
,
64 const gfx::Size
& contents_size
) const {
65 gfx::Rect
bounds(contents_size
);
66 bounds
.set_x(is_arrow_on_left(arrow()) ?
67 anchor_rect
.right() : anchor_rect
.x() - contents_size
.width());
68 // InfolistBorder modifies the vertical position based on the arrow offset
69 // although it doesn't draw the arrow. The arrow offset is the half of
70 // |contents_size| by default but can be modified through the off-screen logic
71 // in BubbleFrameView.
72 bounds
.set_y(anchor_rect
.y() + contents_size
.height() / 2 -
73 GetArrowOffset(contents_size
));
77 gfx::Insets
InfolistBorder::GetInsets() const {
78 // This has to be specified and return empty insets to place the infolist
79 // window without the gap.
85 // InfolistRow renderes a row of a infolist.
86 class InfolistEntryView
: public views::View
{
88 InfolistEntryView(const ui::InfolistEntry
& entry
,
89 const gfx::FontList
& title_font_list
,
90 const gfx::FontList
& description_font_list
);
91 ~InfolistEntryView() override
;
93 void SetEntry(const ui::InfolistEntry
& entry
);
96 // views::View implementation.
97 gfx::Size
GetPreferredSize() const override
;
99 void UpdateBackground();
101 ui::InfolistEntry entry_
;
103 // The title label. Owned by views hierarchy.
104 views::Label
* title_label_
;
106 // The description label. Owned by views hierarchy.
107 views::Label
* description_label_
;
109 DISALLOW_COPY_AND_ASSIGN(InfolistEntryView
);
112 InfolistEntryView::InfolistEntryView(const ui::InfolistEntry
& entry
,
113 const gfx::FontList
& title_font_list
,
114 const gfx::FontList
& description_font_list
)
116 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
118 title_label_
= new views::Label(entry
.title
, title_font_list
);
119 title_label_
->SetPosition(gfx::Point(0, 0));
120 title_label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
121 title_label_
->SetBorder(views::Border::CreateEmptyBorder(4, 7, 2, 4));
123 description_label_
= new views::Label(entry
.body
, description_font_list
);
124 description_label_
->SetPosition(gfx::Point(0, 0));
125 description_label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
126 description_label_
->SetMultiLine(true);
127 description_label_
->SizeToFit(kInfolistEntryWidth
);
128 description_label_
->SetBorder(views::Border::CreateEmptyBorder(2, 17, 4, 4));
129 AddChildView(title_label_
);
130 AddChildView(description_label_
);
134 InfolistEntryView::~InfolistEntryView() {}
136 void InfolistEntryView::SetEntry(const ui::InfolistEntry
& entry
) {
141 title_label_
->SetText(entry_
.title
);
142 description_label_
->SetText(entry_
.body
);
146 gfx::Size
InfolistEntryView::GetPreferredSize() const {
147 return gfx::Size(kInfolistEntryWidth
, GetHeightForWidth(kInfolistEntryWidth
));
150 void InfolistEntryView::UpdateBackground() {
151 if (entry_
.highlighted
) {
153 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
154 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused
)));
155 SetBorder(views::Border::CreateSolidBorder(
157 GetNativeTheme()->GetSystemColor(
158 ui::NativeTheme::kColorId_FocusedBorderColor
)));
160 set_background(NULL
);
161 SetBorder(views::Border::CreateEmptyBorder(1, 1, 1, 1));
166 ///////////////////////////////////////////////////////////////////////////////
169 InfolistWindow::InfolistWindow(views::View
* candidate_window
,
170 const std::vector
<ui::InfolistEntry
>& entries
)
171 : views::BubbleDelegateView(candidate_window
, views::BubbleBorder::NONE
),
172 title_font_list_(gfx::Font(kJapaneseFontName
, kFontSizeDelta
+ 15)),
173 description_font_list_(gfx::Font(kJapaneseFontName
,
174 kFontSizeDelta
+ 11)) {
175 set_can_activate(false);
176 set_accept_events(false);
177 set_margins(gfx::Insets());
180 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
181 ui::NativeTheme::kColorId_WindowBackground
)));
182 SetBorder(views::Border::CreateSolidBorder(
184 GetNativeTheme()->GetSystemColor(
185 ui::NativeTheme::kColorId_MenuBorderColor
)));
187 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
189 views::Label
* caption_label
= new views::Label(
190 l10n_util::GetStringUTF16(IDS_CHROMEOS_IME_INFOLIST_WINDOW_TITLE
));
191 caption_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
192 caption_label
->SetEnabledColor(GetNativeTheme()->GetSystemColor(
193 ui::NativeTheme::kColorId_LabelEnabledColor
));
194 caption_label
->SetBorder(views::Border::CreateEmptyBorder(2, 2, 2, 2));
195 caption_label
->set_background(views::Background::CreateSolidBackground(
196 color_utils::AlphaBlend(SK_ColorBLACK
,
197 GetNativeTheme()->GetSystemColor(
198 ui::NativeTheme::kColorId_WindowBackground
),
201 AddChildView(caption_label
);
203 for (size_t i
= 0; i
< entries
.size(); ++i
) {
204 entry_views_
.push_back(new InfolistEntryView(
205 entries
[i
], title_font_list_
, description_font_list_
));
206 AddChildView(entry_views_
.back());
210 InfolistWindow::~InfolistWindow() {
213 void InfolistWindow::InitWidget() {
214 views::Widget
* widget
= views::BubbleDelegateView::CreateBubble(this);
215 wm::SetWindowVisibilityAnimationType(
216 widget
->GetNativeView(),
217 wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
);
219 // BubbleFrameView will be initialized through CreateBubble.
220 GetBubbleFrameView()->SetBubbleBorder(
221 scoped_ptr
<views::BubbleBorder
>(new InfolistBorder()));
225 void InfolistWindow::Relayout(const std::vector
<ui::InfolistEntry
>& entries
) {
227 for (; i
< entries
.size(); ++i
) {
228 if (i
< entry_views_
.size()) {
229 entry_views_
[i
]->SetEntry(entries
[i
]);
231 InfolistEntryView
* new_entry
= new InfolistEntryView(
232 entries
[i
], title_font_list_
, description_font_list_
);
233 AddChildView(new_entry
);
234 entry_views_
.push_back(new_entry
);
238 if (i
< entry_views_
.size()) {
239 for (; i
< entry_views_
.size(); ++i
)
240 delete entry_views_
[i
];
241 entry_views_
.resize(entries
.size());
245 GetBubbleFrameView()->bubble_border()->set_arrow_offset(0);
249 void InfolistWindow::ShowWithDelay() {
250 show_hide_timer_
.Start(
252 base::TimeDelta::FromMilliseconds(kInfolistShowDelayMilliSeconds
),
254 &views::Widget::Show
);
257 void InfolistWindow::HideWithDelay() {
258 show_hide_timer_
.Start(
260 base::TimeDelta::FromMilliseconds(kInfolistHideDelayMilliSeconds
),
262 &views::Widget::Close
);
265 void InfolistWindow::ShowImmediately() {
266 show_hide_timer_
.Stop();
270 void InfolistWindow::HideImmediately() {
271 show_hide_timer_
.Stop();
272 GetWidget()->Close();
275 const char* InfolistWindow::GetClassName() const {
276 return "InfolistWindow";
279 void InfolistWindow::WindowClosing() {
280 show_hide_timer_
.Stop();