Workaround for xkbcommon dead keys.
[chromium-blink-merge.git] / ui / app_list / views / search_box_view.cc
blob4126f0d79b8216f5b65ff801792a85bd460202bf
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"
7 #include <algorithm>
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"
28 namespace app_list {
30 namespace {
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 {
50 public:
51 ExperimentalSearchBoxBackground() {}
52 ~ExperimentalSearchBoxBackground() override {}
54 private:
55 // views::Background overrides:
56 void Paint(gfx::Canvas* canvas, views::View* view) const override {
57 gfx::Rect bounds = view->GetContentsBounds();
59 SkPaint paint;
60 paint.setFlags(SkPaint::kAntiAlias_Flag);
61 paint.setColor(kBackgroundBorderColor);
62 canvas->DrawRoundRect(bounds, kBackgroundBorderCornerRadius, paint);
63 bounds.Inset(kBackgroundBorderWidth,
64 kBackgroundBorderWidth,
65 kBackgroundBorderWidth,
66 0);
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);
77 } // namespace
79 SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
80 AppListViewDelegate* view_delegate)
81 : delegate_(delegate),
82 view_delegate_(view_delegate),
83 model_(NULL),
84 icon_view_(NULL),
85 back_button_(NULL),
86 speech_button_(NULL),
87 menu_button_(NULL),
88 search_box_(new views::Textfield),
89 contents_view_(NULL) {
90 if (switches::IsExperimentalAppListEnabled()) {
91 back_button_ = new views::ImageButton(this);
92 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
93 back_button_->SetImage(
94 views::ImageButton::STATE_NORMAL,
95 rb.GetImageSkiaNamed(IDR_APP_LIST_FOLDER_BACK_NORMAL));
96 back_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
97 views::ImageButton::ALIGN_MIDDLE);
98 AddChildView(back_button_);
100 set_background(new ExperimentalSearchBoxBackground());
101 } else {
102 set_background(
103 views::Background::CreateSolidBackground(kSearchBoxBackground));
104 SetBorder(
105 views::Border::CreateSolidSidedBorder(0, 0, 1, 0, kTopSeparatorColor));
106 icon_view_ = new views::ImageView;
107 AddChildView(icon_view_);
110 views::BoxLayout* layout =
111 new views::BoxLayout(views::BoxLayout::kHorizontal,
112 kPadding,
114 kPadding - views::Textfield::kTextPadding);
115 SetLayoutManager(layout);
116 layout->set_cross_axis_alignment(
117 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
118 layout->set_minimum_cross_axis_size(kPreferredHeight);
120 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
122 search_box_->SetBorder(views::Border::NullBorder());
123 search_box_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
124 search_box_->set_placeholder_text_color(kHintTextColor);
125 search_box_->set_controller(this);
126 AddChildView(search_box_);
127 layout->SetFlexForView(search_box_, 1);
129 #if !defined(OS_CHROMEOS)
130 menu_button_ = new views::MenuButton(NULL, base::string16(), this, false);
131 menu_button_->SetBorder(views::Border::NullBorder());
132 menu_button_->SetImage(views::Button::STATE_NORMAL,
133 *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_NORMAL));
134 menu_button_->SetImage(views::Button::STATE_HOVERED,
135 *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_HOVER));
136 menu_button_->SetImage(views::Button::STATE_PRESSED,
137 *rb.GetImageSkiaNamed(IDR_APP_LIST_TOOLS_PRESSED));
138 AddChildView(menu_button_);
139 #endif
141 view_delegate_->GetSpeechUI()->AddObserver(this);
142 ModelChanged();
145 SearchBoxView::~SearchBoxView() {
146 view_delegate_->GetSpeechUI()->RemoveObserver(this);
147 model_->search_box()->RemoveObserver(this);
150 void SearchBoxView::ModelChanged() {
151 if (model_)
152 model_->search_box()->RemoveObserver(this);
154 model_ = view_delegate_->GetModel();
155 DCHECK(model_);
156 model_->search_box()->AddObserver(this);
157 IconChanged();
158 SpeechRecognitionButtonPropChanged();
159 HintTextChanged();
162 bool SearchBoxView::HasSearch() const {
163 return !search_box_->text().empty();
166 void SearchBoxView::ClearSearch() {
167 search_box_->SetText(base::string16());
168 view_delegate_->AutoLaunchCanceled();
169 // Updates model and fires query changed manually because SetText() above
170 // does not generate ContentsChanged() notification.
171 UpdateModel();
172 NotifyQueryChanged();
175 void SearchBoxView::InvalidateMenu() {
176 menu_.reset();
179 gfx::Size SearchBoxView::GetPreferredSize() const {
180 return gfx::Size(kPreferredWidth, kPreferredHeight);
183 bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent& event) {
184 if (contents_view_)
185 return contents_view_->OnMouseWheel(event);
187 return false;
190 void SearchBoxView::OnEnabledChanged() {
191 search_box_->SetEnabled(enabled());
192 if (menu_button_)
193 menu_button_->SetEnabled(enabled());
194 if (speech_button_)
195 speech_button_->SetEnabled(enabled());
198 void SearchBoxView::UpdateModel() {
199 // Temporarily remove from observer to ignore notifications caused by us.
200 model_->search_box()->RemoveObserver(this);
201 model_->search_box()->SetText(search_box_->text());
202 model_->search_box()->SetSelectionModel(search_box_->GetSelectionModel());
203 model_->search_box()->AddObserver(this);
206 void SearchBoxView::NotifyQueryChanged() {
207 DCHECK(delegate_);
208 delegate_->QueryChanged(this);
211 void SearchBoxView::ContentsChanged(views::Textfield* sender,
212 const base::string16& new_contents) {
213 UpdateModel();
214 view_delegate_->AutoLaunchCanceled();
215 NotifyQueryChanged();
218 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
219 const ui::KeyEvent& key_event) {
220 bool handled = false;
221 if (contents_view_ && contents_view_->visible())
222 handled = contents_view_->OnKeyPressed(key_event);
224 // Prevent Shift+Tab from locking up the whole chrome browser process.
225 // Explicitly capturing the Shift+Tab event here compensates for a focus
226 // search issue. We get away with this because there are no other focus
227 // targets. See http://crbug.com/438425 for details.
228 if (key_event.key_code() == ui::VKEY_TAB && key_event.IsShiftDown())
229 handled = true;
231 return handled;
234 void SearchBoxView::ButtonPressed(views::Button* sender,
235 const ui::Event& event) {
236 if (back_button_ && sender == back_button_)
237 delegate_->BackButtonPressed();
238 else if (speech_button_ && sender == speech_button_)
239 view_delegate_->ToggleSpeechRecognition();
240 else
241 NOTREACHED();
244 void SearchBoxView::OnMenuButtonClicked(View* source, const gfx::Point& point) {
245 if (!menu_)
246 menu_.reset(new AppListMenuViews(view_delegate_));
248 const gfx::Point menu_location =
249 menu_button_->GetBoundsInScreen().bottom_right() +
250 gfx::Vector2d(kMenuXOffsetFromButton, kMenuYOffsetFromButton);
251 menu_->RunMenuAt(menu_button_, menu_location);
254 void SearchBoxView::IconChanged() {
255 if (icon_view_)
256 icon_view_->SetImage(model_->search_box()->icon());
259 void SearchBoxView::SpeechRecognitionButtonPropChanged() {
260 const SearchBoxModel::SpeechButtonProperty* speech_button_prop =
261 model_->search_box()->speech_button();
262 if (speech_button_prop) {
263 if (!speech_button_) {
264 speech_button_ = new views::ImageButton(this);
265 AddChildView(speech_button_);
268 if (view_delegate_->GetSpeechUI()->state() ==
269 SPEECH_RECOGNITION_HOTWORD_LISTENING) {
270 speech_button_->SetImage(
271 views::Button::STATE_NORMAL, &speech_button_prop->on_icon);
272 speech_button_->SetTooltipText(speech_button_prop->on_tooltip);
273 } else {
274 speech_button_->SetImage(
275 views::Button::STATE_NORMAL, &speech_button_prop->off_icon);
276 speech_button_->SetTooltipText(speech_button_prop->off_tooltip);
278 } else {
279 if (speech_button_) {
280 // Deleting a view will detach it from its parent.
281 delete speech_button_;
282 speech_button_ = NULL;
285 Layout();
288 void SearchBoxView::HintTextChanged() {
289 search_box_->set_placeholder_text(model_->search_box()->hint_text());
292 void SearchBoxView::SelectionModelChanged() {
293 search_box_->SelectSelectionModel(model_->search_box()->selection_model());
296 void SearchBoxView::TextChanged() {
297 search_box_->SetText(model_->search_box()->text());
298 NotifyQueryChanged();
301 void SearchBoxView::OnSpeechRecognitionStateChanged(
302 SpeechRecognitionState new_state) {
303 SpeechRecognitionButtonPropChanged();
304 SchedulePaint();
307 } // namespace app_list