Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / search_box_view.cc
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.
4
5 #include "ui/app_list/views/search_box_view.h"
6
7 #include <algorithm>
8
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/resource/resource_bundle.h"
17 #include "ui/events/event.h"
18 #include "ui/views/border.h"
19 #include "ui/views/controls/button/image_button.h"
20 #include "ui/views/controls/button/menu_button.h"
21 #include "ui/views/controls/image_view.h"
22 #include "ui/views/controls/textfield/textfield.h"
23
24 namespace app_list {
25
26 namespace {
27
28 const int kPadding = 14;
29 const int kIconDimension = 32;
30 const int kPreferredWidth = 360;
31 const int kPreferredHeight = 48;
32 #if !defined(OS_CHROMEOS)
33 const int kMenuButtonDimension = 29;
34 #endif
35
36 const SkColor kHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
37
38 // Menu offset relative to the bottom-right corner of the menu button.
39 const int kMenuYOffsetFromButton = -4;
40 const int kMenuXOffsetFromButton = -7;
41
42 }  // namespace
43
44 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
45                              AppListViewDelegate* view_delegate)
46     : delegate_(delegate),
47       view_delegate_(view_delegate),
48       model_(NULL),
49       icon_view_(new views::ImageView),
50       speech_button_(NULL),
51       search_box_(new views::Textfield),
52       contents_view_(NULL) {
53   AddChildView(icon_view_);
54
55   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
56
57 #if !defined(OS_CHROMEOS)
58   menu_button_ = new views::MenuButton(NULL, base::string16(), this, false);
59   menu_button_->SetBorder(views::Border::NullBorder());
60   menu_button_->SetImage(views::Button::STATE_NORMAL,
61                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL));
62   menu_button_->SetImage(views::Button::STATE_HOVERED,
63                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER));
64   menu_button_->SetImage(views::Button::STATE_PRESSED,
65                          *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED));
66   AddChildView(menu_button_);
67 #endif
68
69   search_box_->SetBorder(views::Border::NullBorder());
70   search_box_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
71   search_box_->set_placeholder_text_color(kHintTextColor);
72   search_box_->set_controller(this);
73   AddChildView(search_box_);
74
75   view_delegate_->GetSpeechUI()->AddObserver(this);
76   ModelChanged();
77 }
78
79 SearchBoxView::~SearchBoxView() {
80   view_delegate_->GetSpeechUI()->RemoveObserver(this);
81   model_->search_box()->RemoveObserver(this);
82 }
83
84 void SearchBoxView::ModelChanged() {
85   if (model_)
86     model_->search_box()->RemoveObserver(this);
87
88   model_ = view_delegate_->GetModel();
89   DCHECK(model_);
90   model_->search_box()->AddObserver(this);
91   IconChanged();
92   SpeechRecognitionButtonPropChanged();
93   HintTextChanged();
94 }
95
96 bool SearchBoxView::HasSearch() const {
97   return !search_box_->text().empty();
98 }
99
100 void SearchBoxView::ClearSearch() {
101   search_box_->SetText(base::string16());
102   view_delegate_->AutoLaunchCanceled();
103   // Updates model and fires query changed manually because SetText() above
104   // does not generate ContentsChanged() notification.
105   UpdateModel();
106   NotifyQueryChanged();
107 }
108
109 void SearchBoxView::InvalidateMenu() {
110   menu_.reset();
111 }
112
113 gfx::Size SearchBoxView::GetPreferredSize() const {
114   return gfx::Size(kPreferredWidth, kPreferredHeight);
115 }
116
117 void SearchBoxView::Layout() {
118   gfx::Rect rect(GetContentsBounds());
119   if (rect.IsEmpty())
120     return;
121
122   gfx::Rect icon_frame(rect);
123   icon_frame.set_width(kIconDimension + 2 * kPadding);
124   icon_view_->SetBoundsRect(icon_frame);
125
126   // Places |speech_button_| if exists. |speech_button_frame| holds its bounds
127   // to calculate the search box bounds.
128   gfx::Rect speech_button_frame;
129   if (speech_button_) {
130     speech_button_frame = icon_frame;
131     speech_button_frame.set_x(rect.right() - icon_frame.width());
132     gfx::Size button_size = speech_button_->GetPreferredSize();
133     gfx::Point button_origin = speech_button_frame.CenterPoint();
134     button_origin.Offset(-button_size.width() / 2, -button_size.height() / 2);
135     speech_button_->SetBoundsRect(gfx::Rect(button_origin, button_size));
136   }
137
138   gfx::Rect menu_button_frame(rect);
139 #if !defined(OS_CHROMEOS)
140   menu_button_frame.set_width(kMenuButtonDimension);
141   menu_button_frame.set_x(rect.right() - menu_button_frame.width() - kPadding);
142   menu_button_frame.ClampToCenteredSize(gfx::Size(menu_button_frame.width(),
143                                                   kMenuButtonDimension));
144   menu_button_->SetBoundsRect(menu_button_frame);
145 #else
146   menu_button_frame.set_width(0);
147 #endif
148
149   gfx::Rect edit_frame(rect);
150   edit_frame.set_x(icon_frame.right());
151   int edit_frame_width =
152       rect.width() - icon_frame.width() - kPadding - menu_button_frame.width();
153   if (!speech_button_frame.IsEmpty())
154     edit_frame_width -= speech_button_frame.width() + kPadding;
155   edit_frame.set_width(edit_frame_width);
156   edit_frame.ClampToCenteredSize(
157       gfx::Size(edit_frame.width(), search_box_->GetPreferredSize().height()));
158   search_box_->SetBoundsRect(edit_frame);
159 }
160
161 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent& event) {
162   if (contents_view_)
163     return contents_view_->OnMouseWheel(event);
164
165   return false;
166 }
167
168 void SearchBoxView::UpdateModel() {
169   // Temporarily remove from observer to ignore notifications caused by us.
170   model_->search_box()->RemoveObserver(this);
171   model_->search_box()->SetText(search_box_->text());
172   model_->search_box()->SetSelectionModel(search_box_->GetSelectionModel());
173   model_->search_box()->AddObserver(this);
174 }
175
176 void SearchBoxView::NotifyQueryChanged() {
177   DCHECK(delegate_);
178   delegate_->QueryChanged(this);
179 }
180
181 void SearchBoxView::ContentsChanged(views::Textfield* sender,
182                                     const base::string16& new_contents) {
183   UpdateModel();
184   view_delegate_->AutoLaunchCanceled();
185   NotifyQueryChanged();
186 }
187
188 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
189                                    const ui::KeyEvent& key_event) {
190   bool handled = false;
191   if (contents_view_ && contents_view_->visible())
192     handled = contents_view_->OnKeyPressed(key_event);
193
194   return handled;
195 }
196
197 void SearchBoxView::ButtonPressed(views::Button* sender,
198                                   const ui::Event& event) {
199   DCHECK(speech_button_ && sender == speech_button_);
200   view_delegate_->ToggleSpeechRecognition();
201 }
202
203 void SearchBoxView::OnMenuButtonClicked(View* source, const gfx::Point& point) {
204   if (!menu_)
205     menu_.reset(new AppListMenuViews(view_delegate_));
206
207   const gfx::Point menu_location =
208       menu_button_->GetBoundsInScreen().bottom_right() +
209       gfx::Vector2d(kMenuXOffsetFromButton, kMenuYOffsetFromButton);
210   menu_->RunMenuAt(menu_button_, menu_location);
211 }
212
213 void SearchBoxView::IconChanged() {
214   icon_view_->SetImage(model_->search_box()->icon());
215 }
216
217 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
218   const SearchBoxModel::SpeechButtonProperty* speech_button_prop =
219       model_->search_box()->speech_button();
220   if (speech_button_prop) {
221     if (!speech_button_) {
222       speech_button_ = new views::ImageButton(this);
223       AddChildView(speech_button_);
224     }
225
226     if (view_delegate_->GetSpeechUI()->state() ==
227         SPEECH_RECOGNITION_HOTWORD_LISTENING) {
228       speech_button_->SetImage(
229           views::Button::STATE_NORMAL, &speech_button_prop->on_icon);
230       speech_button_->SetTooltipText(speech_button_prop->on_tooltip);
231     } else {
232       speech_button_->SetImage(
233           views::Button::STATE_NORMAL, &speech_button_prop->off_icon);
234       speech_button_->SetTooltipText(speech_button_prop->off_tooltip);
235     }
236   } else {
237     if (speech_button_) {
238       // Deleting a view will detach it from its parent.
239       delete speech_button_;
240       speech_button_ = NULL;
241     }
242   }
243 }
244
245 void SearchBoxView::HintTextChanged() {
246   search_box_->set_placeholder_text(model_->search_box()->hint_text());
247 }
248
249 void SearchBoxView::SelectionModelChanged() {
250   search_box_->SelectSelectionModel(model_->search_box()->selection_model());
251 }
252
253 void SearchBoxView::TextChanged() {
254   search_box_->SetText(model_->search_box()->text());
255   NotifyQueryChanged();
256 }
257
258 void SearchBoxView::OnSpeechRecognitionStateChanged(
259     SpeechRecognitionState new_state) {
260   SpeechRecognitionButtonPropChanged();
261   SchedulePaint();
262 }
263
264 }  // namespace app_list