1 // Copyright (c) 2012 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/app_list/views/search_box_view.h"
9 #include "ui/app_list/app_list_constants.h"
10 #include "ui/app_list/app_list_model.h"
11 #include "ui/app_list/app_list_switches.h"
12 #include "ui/app_list/app_list_view_delegate.h"
13 #include "ui/app_list/search_box_model.h"
14 #include "ui/app_list/speech_ui_model.h"
15 #include "ui/app_list/views/app_list_menu_views.h"
16 #include "ui/app_list/views/search_box_view_delegate.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/events/event.h"
19 #include "ui/gfx/canvas.h"
20 #include "ui/resources/grit/ui_resources.h"
21 #include "ui/views/border.h"
22 #include "ui/views/controls/button/image_button.h"
23 #include "ui/views/controls/button/menu_button.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/textfield/textfield.h"
26 #include "ui/views/layout/box_layout.h"
32 const int kPadding
= 14;
33 const int kPreferredWidth
= 360;
34 const int kPreferredHeight
= 48;
36 const SkColor kHintTextColor
= SkColorSetRGB(0xA0, 0xA0, 0xA0);
38 // Menu offset relative to the bottom-right corner of the menu button.
39 const int kMenuYOffsetFromButton
= -4;
40 const int kMenuXOffsetFromButton
= -7;
42 const int kBackgroundBorderWidth
= 1;
43 const int kBackgroundBorderBottomWidth
= 1;
44 const int kBackgroundBorderCornerRadius
= 2;
45 const SkColor kBackgroundBorderColor
= SkColorSetRGB(0xEE, 0xEE, 0xEE);
46 const SkColor kBackgroundBorderBottomColor
= SkColorSetRGB(0xCC, 0xCC, 0xCC);
48 // A background that paints a solid white rounded rect with a thin grey border.
49 class ExperimentalSearchBoxBackground
: public views::Background
{
51 ExperimentalSearchBoxBackground() {}
52 virtual ~ExperimentalSearchBoxBackground() {}
55 // views::Background overrides:
56 virtual void Paint(gfx::Canvas
* canvas
, views::View
* view
) const override
{
57 gfx::Rect bounds
= view
->GetContentsBounds();
60 paint
.setFlags(SkPaint::kAntiAlias_Flag
);
61 paint
.setColor(kBackgroundBorderColor
);
62 canvas
->DrawRoundRect(bounds
, kBackgroundBorderCornerRadius
, paint
);
63 bounds
.Inset(kBackgroundBorderWidth
,
64 kBackgroundBorderWidth
,
65 kBackgroundBorderWidth
,
67 paint
.setColor(kBackgroundBorderBottomColor
);
68 canvas
->DrawRoundRect(bounds
, kBackgroundBorderCornerRadius
, paint
);
69 bounds
.Inset(0, 0, 0, kBackgroundBorderBottomWidth
);
70 paint
.setColor(kSearchBoxBackground
);
71 canvas
->DrawRoundRect(bounds
, kBackgroundBorderCornerRadius
, paint
);
74 DISALLOW_COPY_AND_ASSIGN(ExperimentalSearchBoxBackground
);
79 SearchBoxView::SearchBoxView(SearchBoxViewDelegate
* delegate
,
80 AppListViewDelegate
* view_delegate
)
81 : delegate_(delegate
),
82 view_delegate_(view_delegate
),
86 search_box_(new views::Textfield
),
87 contents_view_(NULL
) {
88 if (switches::IsExperimentalAppListEnabled()) {
89 set_background(new ExperimentalSearchBoxBackground());
92 views::Background::CreateSolidBackground(kSearchBoxBackground
));
94 views::Border::CreateSolidSidedBorder(0, 0, 1, 0, kTopSeparatorColor
));
95 icon_view_
= new views::ImageView
;
96 AddChildView(icon_view_
);
99 views::BoxLayout
* layout
=
100 new views::BoxLayout(views::BoxLayout::kHorizontal
,
103 kPadding
- views::Textfield::kTextPadding
);
104 SetLayoutManager(layout
);
105 layout
->set_cross_axis_alignment(
106 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER
);
107 layout
->set_minimum_cross_axis_size(kPreferredHeight
);
109 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
111 search_box_
->SetBorder(views::Border::NullBorder());
112 search_box_
->SetFontList(rb
.GetFontList(ui::ResourceBundle::MediumFont
));
113 search_box_
->set_placeholder_text_color(kHintTextColor
);
114 search_box_
->set_controller(this);
115 AddChildView(search_box_
);
116 layout
->SetFlexForView(search_box_
, 1);
118 #if !defined(OS_CHROMEOS)
119 menu_button_
= new views::MenuButton(NULL
, base::string16(), this, false);
120 menu_button_
->SetBorder(views::Border::NullBorder());
121 menu_button_
->SetImage(views::Button::STATE_NORMAL
,
122 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL
));
123 menu_button_
->SetImage(views::Button::STATE_HOVERED
,
124 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER
));
125 menu_button_
->SetImage(views::Button::STATE_PRESSED
,
126 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED
));
127 AddChildView(menu_button_
);
130 view_delegate_
->GetSpeechUI()->AddObserver(this);
134 SearchBoxView::~SearchBoxView() {
135 view_delegate_
->GetSpeechUI()->RemoveObserver(this);
136 model_
->search_box()->RemoveObserver(this);
139 void SearchBoxView::ModelChanged() {
141 model_
->search_box()->RemoveObserver(this);
143 model_
= view_delegate_
->GetModel();
145 model_
->search_box()->AddObserver(this);
147 SpeechRecognitionButtonPropChanged();
151 bool SearchBoxView::HasSearch() const {
152 return !search_box_
->text().empty();
155 void SearchBoxView::ClearSearch() {
156 search_box_
->SetText(base::string16());
157 view_delegate_
->AutoLaunchCanceled();
158 // Updates model and fires query changed manually because SetText() above
159 // does not generate ContentsChanged() notification.
161 NotifyQueryChanged();
164 void SearchBoxView::InvalidateMenu() {
168 gfx::Size
SearchBoxView::GetPreferredSize() const {
169 return gfx::Size(kPreferredWidth
, kPreferredHeight
);
172 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent
& event
) {
174 return contents_view_
->OnMouseWheel(event
);
179 void SearchBoxView::UpdateModel() {
180 // Temporarily remove from observer to ignore notifications caused by us.
181 model_
->search_box()->RemoveObserver(this);
182 model_
->search_box()->SetText(search_box_
->text());
183 model_
->search_box()->SetSelectionModel(search_box_
->GetSelectionModel());
184 model_
->search_box()->AddObserver(this);
187 void SearchBoxView::NotifyQueryChanged() {
189 delegate_
->QueryChanged(this);
192 void SearchBoxView::ContentsChanged(views::Textfield
* sender
,
193 const base::string16
& new_contents
) {
195 view_delegate_
->AutoLaunchCanceled();
196 NotifyQueryChanged();
199 bool SearchBoxView::HandleKeyEvent(views::Textfield
* sender
,
200 const ui::KeyEvent
& key_event
) {
201 bool handled
= false;
202 if (contents_view_
&& contents_view_
->visible())
203 handled
= contents_view_
->OnKeyPressed(key_event
);
208 void SearchBoxView::ButtonPressed(views::Button
* sender
,
209 const ui::Event
& event
) {
210 DCHECK(speech_button_
&& sender
== speech_button_
);
211 view_delegate_
->ToggleSpeechRecognition();
214 void SearchBoxView::OnMenuButtonClicked(View
* source
, const gfx::Point
& point
) {
216 menu_
.reset(new AppListMenuViews(view_delegate_
));
218 const gfx::Point menu_location
=
219 menu_button_
->GetBoundsInScreen().bottom_right() +
220 gfx::Vector2d(kMenuXOffsetFromButton
, kMenuYOffsetFromButton
);
221 menu_
->RunMenuAt(menu_button_
, menu_location
);
224 void SearchBoxView::IconChanged() {
226 icon_view_
->SetImage(model_
->search_box()->icon());
229 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
230 const SearchBoxModel::SpeechButtonProperty
* speech_button_prop
=
231 model_
->search_box()->speech_button();
232 if (speech_button_prop
) {
233 if (!speech_button_
) {
234 speech_button_
= new views::ImageButton(this);
235 AddChildView(speech_button_
);
238 if (view_delegate_
->GetSpeechUI()->state() ==
239 SPEECH_RECOGNITION_HOTWORD_LISTENING
) {
240 speech_button_
->SetImage(
241 views::Button::STATE_NORMAL
, &speech_button_prop
->on_icon
);
242 speech_button_
->SetTooltipText(speech_button_prop
->on_tooltip
);
244 speech_button_
->SetImage(
245 views::Button::STATE_NORMAL
, &speech_button_prop
->off_icon
);
246 speech_button_
->SetTooltipText(speech_button_prop
->off_tooltip
);
249 if (speech_button_
) {
250 // Deleting a view will detach it from its parent.
251 delete speech_button_
;
252 speech_button_
= NULL
;
257 void SearchBoxView::HintTextChanged() {
258 search_box_
->set_placeholder_text(model_
->search_box()->hint_text());
261 void SearchBoxView::SelectionModelChanged() {
262 search_box_
->SelectSelectionModel(model_
->search_box()->selection_model());
265 void SearchBoxView::TextChanged() {
266 search_box_
->SetText(model_
->search_box()->text());
267 NotifyQueryChanged();
270 void SearchBoxView::OnSpeechRecognitionStateChanged(
271 SpeechRecognitionState new_state
) {
272 SpeechRecognitionButtonPropChanged();
276 } // namespace app_list