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_model.h"
10 #include "ui/app_list/app_list_switches.h"
11 #include "ui/app_list/app_list_view_delegate.h"
12 #include "ui/app_list/search_box_model.h"
13 #include "ui/app_list/speech_ui_model.h"
14 #include "ui/app_list/views/app_list_menu_views.h"
15 #include "ui/app_list/views/search_box_view_delegate.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/events/event.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/resources/grit/ui_resources.h"
20 #include "ui/views/border.h"
21 #include "ui/views/controls/button/image_button.h"
22 #include "ui/views/controls/button/menu_button.h"
23 #include "ui/views/controls/image_view.h"
24 #include "ui/views/controls/textfield/textfield.h"
25 #include "ui/views/layout/box_layout.h"
31 const int kPadding
= 14;
32 const int kPreferredWidth
= 360;
33 const int kPreferredHeight
= 48;
35 const SkColor kHintTextColor
= SkColorSetRGB(0xA0, 0xA0, 0xA0);
37 // Menu offset relative to the bottom-right corner of the menu button.
38 const int kMenuYOffsetFromButton
= -4;
39 const int kMenuXOffsetFromButton
= -7;
41 // Experimental app list constants.
42 const int kExperimentalSearchBoxHeight
= 37;
44 const int kBackgroundBorderWidth
= 1;
45 const int kBackgroundBorderBottomWidth
= 1;
46 const int kBackgroundBorderCornerRadius
= 2;
47 const SkColor kBackgroundBorderColor
= SkColorSetRGB(0xEE, 0xEE, 0xEE);
48 const SkColor kBackgroundBorderBottomColor
= SkColorSetRGB(0xCC, 0xCC, 0xCC);
50 // A background that paints a solid white rounded rect with a thin grey border.
51 class SearchBoxBackground
: public views::Background
{
53 SearchBoxBackground() {}
54 virtual ~SearchBoxBackground() {}
57 // views::Background overrides:
58 virtual void Paint(gfx::Canvas
* canvas
, views::View
* view
) const OVERRIDE
{
59 gfx::Rect bounds
= view
->GetContentsBounds();
62 paint
.setFlags(SkPaint::kAntiAlias_Flag
);
63 paint
.setColor(kBackgroundBorderColor
);
64 canvas
->DrawRoundRect(bounds
, kBackgroundBorderCornerRadius
, paint
);
65 bounds
.Inset(kBackgroundBorderWidth
,
66 kBackgroundBorderWidth
,
67 kBackgroundBorderWidth
,
69 paint
.setColor(kBackgroundBorderBottomColor
);
70 canvas
->DrawRoundRect(bounds
, kBackgroundBorderCornerRadius
, paint
);
71 bounds
.Inset(0, 0, 0, kBackgroundBorderBottomWidth
);
72 paint
.setColor(SK_ColorWHITE
);
73 canvas
->DrawRoundRect(bounds
, kBackgroundBorderCornerRadius
, paint
);
76 DISALLOW_COPY_AND_ASSIGN(SearchBoxBackground
);
81 SearchBoxView::SearchBoxView(SearchBoxViewDelegate
* delegate
,
82 AppListViewDelegate
* view_delegate
)
83 : delegate_(delegate
),
84 view_delegate_(view_delegate
),
86 icon_view_(new views::ImageView
),
88 search_box_(new views::Textfield
),
89 contents_view_(NULL
) {
90 AddChildView(icon_view_
);
91 if (switches::IsExperimentalAppListEnabled())
92 set_background(new SearchBoxBackground());
94 views::BoxLayout
* layout
= new views::BoxLayout(
95 views::BoxLayout::kHorizontal
, kPadding
, 0, kPadding
);
96 SetLayoutManager(layout
);
97 layout
->set_cross_axis_alignment(
98 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER
);
99 layout
->set_minimum_cross_axis_size(switches::IsExperimentalAppListEnabled()
100 ? kExperimentalSearchBoxHeight
103 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
105 search_box_
->SetBorder(views::Border::NullBorder());
106 search_box_
->SetFontList(rb
.GetFontList(ui::ResourceBundle::MediumFont
));
107 search_box_
->set_placeholder_text_color(kHintTextColor
);
108 search_box_
->set_controller(this);
109 AddChildView(search_box_
);
110 layout
->SetFlexForView(search_box_
, 1);
112 #if !defined(OS_CHROMEOS)
113 menu_button_
= new views::MenuButton(NULL
, base::string16(), this, false);
114 menu_button_
->SetBorder(views::Border::NullBorder());
115 menu_button_
->SetImage(views::Button::STATE_NORMAL
,
116 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL
));
117 menu_button_
->SetImage(views::Button::STATE_HOVERED
,
118 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER
));
119 menu_button_
->SetImage(views::Button::STATE_PRESSED
,
120 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED
));
121 AddChildView(menu_button_
);
124 view_delegate_
->GetSpeechUI()->AddObserver(this);
128 SearchBoxView::~SearchBoxView() {
129 view_delegate_
->GetSpeechUI()->RemoveObserver(this);
130 model_
->search_box()->RemoveObserver(this);
133 void SearchBoxView::ModelChanged() {
135 model_
->search_box()->RemoveObserver(this);
137 model_
= view_delegate_
->GetModel();
139 model_
->search_box()->AddObserver(this);
141 SpeechRecognitionButtonPropChanged();
145 bool SearchBoxView::HasSearch() const {
146 return !search_box_
->text().empty();
149 void SearchBoxView::ClearSearch() {
150 search_box_
->SetText(base::string16());
151 view_delegate_
->AutoLaunchCanceled();
152 // Updates model and fires query changed manually because SetText() above
153 // does not generate ContentsChanged() notification.
155 NotifyQueryChanged();
158 void SearchBoxView::InvalidateMenu() {
162 gfx::Size
SearchBoxView::GetPreferredSize() const {
163 return gfx::Size(kPreferredWidth
,
164 switches::IsExperimentalAppListEnabled()
165 ? kExperimentalSearchBoxHeight
169 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent
& event
) {
171 return contents_view_
->OnMouseWheel(event
);
176 void SearchBoxView::UpdateModel() {
177 // Temporarily remove from observer to ignore notifications caused by us.
178 model_
->search_box()->RemoveObserver(this);
179 model_
->search_box()->SetText(search_box_
->text());
180 model_
->search_box()->SetSelectionModel(search_box_
->GetSelectionModel());
181 model_
->search_box()->AddObserver(this);
184 void SearchBoxView::NotifyQueryChanged() {
186 delegate_
->QueryChanged(this);
189 void SearchBoxView::ContentsChanged(views::Textfield
* sender
,
190 const base::string16
& new_contents
) {
192 view_delegate_
->AutoLaunchCanceled();
193 NotifyQueryChanged();
196 bool SearchBoxView::HandleKeyEvent(views::Textfield
* sender
,
197 const ui::KeyEvent
& key_event
) {
198 bool handled
= false;
199 if (contents_view_
&& contents_view_
->visible())
200 handled
= contents_view_
->OnKeyPressed(key_event
);
205 void SearchBoxView::ButtonPressed(views::Button
* sender
,
206 const ui::Event
& event
) {
207 DCHECK(speech_button_
&& sender
== speech_button_
);
208 view_delegate_
->ToggleSpeechRecognition();
211 void SearchBoxView::OnMenuButtonClicked(View
* source
, const gfx::Point
& point
) {
213 menu_
.reset(new AppListMenuViews(view_delegate_
));
215 const gfx::Point menu_location
=
216 menu_button_
->GetBoundsInScreen().bottom_right() +
217 gfx::Vector2d(kMenuXOffsetFromButton
, kMenuYOffsetFromButton
);
218 menu_
->RunMenuAt(menu_button_
, menu_location
);
221 void SearchBoxView::IconChanged() {
222 icon_view_
->SetImage(model_
->search_box()->icon());
225 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
226 const SearchBoxModel::SpeechButtonProperty
* speech_button_prop
=
227 model_
->search_box()->speech_button();
228 if (speech_button_prop
) {
229 if (!speech_button_
) {
230 speech_button_
= new views::ImageButton(this);
231 AddChildView(speech_button_
);
234 if (view_delegate_
->GetSpeechUI()->state() ==
235 SPEECH_RECOGNITION_HOTWORD_LISTENING
) {
236 speech_button_
->SetImage(
237 views::Button::STATE_NORMAL
, &speech_button_prop
->on_icon
);
238 speech_button_
->SetTooltipText(speech_button_prop
->on_tooltip
);
240 speech_button_
->SetImage(
241 views::Button::STATE_NORMAL
, &speech_button_prop
->off_icon
);
242 speech_button_
->SetTooltipText(speech_button_prop
->off_tooltip
);
245 if (speech_button_
) {
246 // Deleting a view will detach it from its parent.
247 delete speech_button_
;
248 speech_button_
= NULL
;
253 void SearchBoxView::HintTextChanged() {
254 search_box_
->set_placeholder_text(model_
->search_box()->hint_text());
257 void SearchBoxView::SelectionModelChanged() {
258 search_box_
->SelectSelectionModel(model_
->search_box()->selection_model());
261 void SearchBoxView::TextChanged() {
262 search_box_
->SetText(model_
->search_box()->text());
263 NotifyQueryChanged();
266 void SearchBoxView::OnSpeechRecognitionStateChanged(
267 SpeechRecognitionState new_state
) {
268 SpeechRecognitionButtonPropChanged();
272 } // namespace app_list