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 "grit/ui_resources.h"
10 #include "ui/app_list/app_list_model.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/l10n/l10n_util.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/events/event.h"
19 #include "ui/views/border.h"
20 #include "ui/views/controls/button/image_button.h"
21 #include "ui/views/controls/button/menu_button.h"
22 #include "ui/views/controls/image_view.h"
23 #include "ui/views/controls/textfield/textfield.h"
29 const int kPadding
= 14;
30 const int kIconDimension
= 32;
31 const int kPreferredWidth
= 360;
32 const int kPreferredHeight
= 48;
33 #if !defined(OS_CHROMEOS)
34 const int kMenuButtonDimension
= 29;
37 const SkColor kHintTextColor
= SkColorSetRGB(0xA0, 0xA0, 0xA0);
39 // Menu offset relative to the bottom-right corner of the menu button.
40 const int kMenuYOffsetFromButton
= -4;
41 const int kMenuXOffsetFromButton
= -7;
45 SearchBoxView::SearchBoxView(SearchBoxViewDelegate
* delegate
,
46 AppListViewDelegate
* view_delegate
)
47 : delegate_(delegate
),
48 view_delegate_(view_delegate
),
50 icon_view_(new views::ImageView
),
52 search_box_(new views::Textfield
),
53 contents_view_(NULL
) {
54 AddChildView(icon_view_
);
56 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
58 #if !defined(OS_CHROMEOS)
59 menu_button_
= new views::MenuButton(NULL
, base::string16(), this, false);
60 menu_button_
->SetBorder(views::Border::NullBorder());
61 menu_button_
->SetImage(views::Button::STATE_NORMAL
,
62 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL
));
63 menu_button_
->SetImage(views::Button::STATE_HOVERED
,
64 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER
));
65 menu_button_
->SetImage(views::Button::STATE_PRESSED
,
66 *rb
.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED
));
67 AddChildView(menu_button_
);
70 search_box_
->SetBorder(views::Border::NullBorder());
71 search_box_
->SetFontList(rb
.GetFontList(ui::ResourceBundle::MediumFont
));
72 search_box_
->set_placeholder_text_color(kHintTextColor
);
73 search_box_
->set_controller(this);
74 AddChildView(search_box_
);
76 view_delegate_
->GetSpeechUI()->AddObserver(this);
80 SearchBoxView::~SearchBoxView() {
81 view_delegate_
->GetSpeechUI()->RemoveObserver(this);
82 model_
->search_box()->RemoveObserver(this);
85 void SearchBoxView::ModelChanged() {
87 model_
->search_box()->RemoveObserver(this);
89 model_
= view_delegate_
->GetModel();
91 model_
->search_box()->AddObserver(this);
93 SpeechRecognitionButtonPropChanged();
97 bool SearchBoxView::HasSearch() const {
98 return !search_box_
->text().empty();
101 void SearchBoxView::ClearSearch() {
102 search_box_
->SetText(base::string16());
103 view_delegate_
->AutoLaunchCanceled();
104 // Updates model and fires query changed manually because SetText() above
105 // does not generate ContentsChanged() notification.
107 NotifyQueryChanged();
110 void SearchBoxView::InvalidateMenu() {
114 gfx::Size
SearchBoxView::GetPreferredSize() const {
115 return gfx::Size(kPreferredWidth
, kPreferredHeight
);
118 void SearchBoxView::Layout() {
119 gfx::Rect
rect(GetContentsBounds());
123 gfx::Rect
icon_frame(rect
);
124 icon_frame
.set_width(kIconDimension
+ 2 * kPadding
);
125 icon_view_
->SetBoundsRect(icon_frame
);
127 // Places |speech_button_| if exists. |speech_button_frame| holds its bounds
128 // to calculate the search box bounds.
129 gfx::Rect speech_button_frame
;
130 if (speech_button_
) {
131 speech_button_frame
= icon_frame
;
132 speech_button_frame
.set_x(rect
.right() - icon_frame
.width());
133 gfx::Size button_size
= speech_button_
->GetPreferredSize();
134 gfx::Point button_origin
= speech_button_frame
.CenterPoint();
135 button_origin
.Offset(-button_size
.width() / 2, -button_size
.height() / 2);
136 speech_button_
->SetBoundsRect(gfx::Rect(button_origin
, button_size
));
139 gfx::Rect
menu_button_frame(rect
);
140 #if !defined(OS_CHROMEOS)
141 menu_button_frame
.set_width(kMenuButtonDimension
);
142 menu_button_frame
.set_x(rect
.right() - menu_button_frame
.width() - kPadding
);
143 menu_button_frame
.ClampToCenteredSize(gfx::Size(menu_button_frame
.width(),
144 kMenuButtonDimension
));
145 menu_button_
->SetBoundsRect(menu_button_frame
);
147 menu_button_frame
.set_width(0);
150 gfx::Rect
edit_frame(rect
);
151 edit_frame
.set_x(icon_frame
.right());
152 int edit_frame_width
=
153 rect
.width() - icon_frame
.width() - kPadding
- menu_button_frame
.width();
154 if (!speech_button_frame
.IsEmpty())
155 edit_frame_width
-= speech_button_frame
.width() + kPadding
;
156 edit_frame
.set_width(edit_frame_width
);
157 edit_frame
.ClampToCenteredSize(
158 gfx::Size(edit_frame
.width(), search_box_
->GetPreferredSize().height()));
159 search_box_
->SetBoundsRect(edit_frame
);
162 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent
& event
) {
164 return contents_view_
->OnMouseWheel(event
);
169 void SearchBoxView::UpdateModel() {
170 // Temporarily remove from observer to ignore notifications caused by us.
171 model_
->search_box()->RemoveObserver(this);
172 model_
->search_box()->SetText(search_box_
->text());
173 model_
->search_box()->SetSelectionModel(search_box_
->GetSelectionModel());
174 model_
->search_box()->AddObserver(this);
177 void SearchBoxView::NotifyQueryChanged() {
179 delegate_
->QueryChanged(this);
182 void SearchBoxView::ContentsChanged(views::Textfield
* sender
,
183 const base::string16
& new_contents
) {
185 view_delegate_
->AutoLaunchCanceled();
186 NotifyQueryChanged();
189 bool SearchBoxView::HandleKeyEvent(views::Textfield
* sender
,
190 const ui::KeyEvent
& key_event
) {
191 bool handled
= false;
192 if (contents_view_
&& contents_view_
->visible())
193 handled
= contents_view_
->OnKeyPressed(key_event
);
198 void SearchBoxView::ButtonPressed(views::Button
* sender
,
199 const ui::Event
& event
) {
200 DCHECK(speech_button_
&& sender
== speech_button_
);
201 view_delegate_
->ToggleSpeechRecognition();
204 void SearchBoxView::OnMenuButtonClicked(View
* source
, const gfx::Point
& point
) {
206 menu_
.reset(new AppListMenuViews(view_delegate_
));
208 const gfx::Point menu_location
=
209 menu_button_
->GetBoundsInScreen().bottom_right() +
210 gfx::Vector2d(kMenuXOffsetFromButton
, kMenuYOffsetFromButton
);
211 menu_
->RunMenuAt(menu_button_
, menu_location
);
214 void SearchBoxView::IconChanged() {
215 icon_view_
->SetImage(model_
->search_box()->icon());
218 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
219 const SearchBoxModel::SpeechButtonProperty
* speech_button_prop
=
220 model_
->search_box()->speech_button();
221 if (speech_button_prop
) {
222 if (!speech_button_
) {
223 speech_button_
= new views::ImageButton(this);
224 AddChildView(speech_button_
);
227 if (view_delegate_
->GetSpeechUI()->state() ==
228 SPEECH_RECOGNITION_HOTWORD_LISTENING
) {
229 speech_button_
->SetImage(
230 views::Button::STATE_NORMAL
, &speech_button_prop
->on_icon
);
231 speech_button_
->SetTooltipText(speech_button_prop
->on_tooltip
);
233 speech_button_
->SetImage(
234 views::Button::STATE_NORMAL
, &speech_button_prop
->off_icon
);
235 speech_button_
->SetTooltipText(speech_button_prop
->off_tooltip
);
238 if (speech_button_
) {
239 // Deleting a view will detach it from its parent.
240 delete speech_button_
;
241 speech_button_
= NULL
;
246 void SearchBoxView::HintTextChanged() {
247 search_box_
->set_placeholder_text(model_
->search_box()->hint_text());
250 void SearchBoxView::SelectionModelChanged() {
251 search_box_
->SelectSelectionModel(model_
->search_box()->selection_model());
254 void SearchBoxView::TextChanged() {
255 search_box_
->SetText(model_
->search_box()->text());
256 NotifyQueryChanged();
259 void SearchBoxView::OnSpeechRecognitionStateChanged(
260 SpeechRecognitionState new_state
) {
261 SpeechRecognitionButtonPropChanged();
265 } // namespace app_list