blob: 78c468bc02f269b44e0ed41d279aa5747c342422 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/app_list/views/search_box_view.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "ash/app_list/model/search/search_box_model.h"
#include "ash/app_list/model/speech/speech_ui_model.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "components/wallpaper/wallpaper_color_profile.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/app_list/app_list_constants.h"
#include "ui/app_list/app_list_switches.h"
#include "ui/app_list/app_list_util.h"
#include "ui/app_list/app_list_view_delegate.h"
#include "ui/app_list/resources/grit/app_list_resources.h"
#include "ui/app_list/vector_icons/vector_icons.h"
#include "ui/app_list/views/app_list_view.h"
#include "ui/app_list/views/contents_view.h"
#include "ui/app_list/views/search_box_view_delegate.h"
#include "ui/app_list/views/search_result_base_view.h"
#include "ui/app_list/views/search_result_page_view.h"
#include "ui/base/ime/text_input_flags.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/shadow_value.h"
#include "ui/keyboard/keyboard_controller.h"
#include "ui/keyboard/keyboard_util.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/shadow_border.h"
#include "ui/views/widget/widget.h"
using wallpaper::ColorProfileType;
namespace app_list {
namespace {
constexpr int kPadding = 12;
constexpr int kPaddingSearchResult = 16;
constexpr int kInnerPadding = 16;
constexpr int kPreferredWidth = 544;
constexpr int kSearchBoxBorderWidth = 4;
constexpr SkColor kSearchBoxBorderColor =
SkColorSetARGBMacro(0x3D, 0xFF, 0xFF, 0xFF);
constexpr int kSearchBoxBorderCornerRadiusSearchResult = 4;
constexpr int kMicIconSize = 24;
constexpr int kCloseIconSize = 24;
constexpr int kSearchBoxFocusBorderCornerRadius = 28;
constexpr int kLightVibrantBlendAlpha = 0xE6;
// Range of the fraction of app list from collapsed to peeking that search box
// should change opacity.
constexpr float kOpacityStartFraction = 0.1f;
constexpr float kOpacityEndFraction = 0.6f;
// Color of placeholder text in zero query state.
constexpr SkColor kZeroQuerySearchboxColor =
SkColorSetARGBMacro(0x8A, 0x00, 0x00, 0x00);
// Gets the box layout inset horizontal padding for the state of AppListModel.
int GetBoxLayoutPaddingForState(AppListModel::State state) {
if (state == AppListModel::STATE_SEARCH_RESULTS)
return kPaddingSearchResult;
return kPadding;
}
} // namespace
// A background that paints a solid white rounded rect with a thin grey border.
class SearchBoxBackground : public views::Background {
public:
explicit SearchBoxBackground(SkColor color)
: corner_radius_(kSearchBoxBorderCornerRadius), color_(color) {}
~SearchBoxBackground() override {}
void set_corner_radius(int corner_radius) { corner_radius_ = corner_radius; }
void set_color(SkColor color) { color_ = color; }
private:
// views::Background overrides:
void Paint(gfx::Canvas* canvas, views::View* view) const override {
gfx::Rect bounds = view->GetContentsBounds();
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setColor(color_);
canvas->DrawRoundRect(bounds, corner_radius_, flags);
}
int corner_radius_;
SkColor color_;
DISALLOW_COPY_AND_ASSIGN(SearchBoxBackground);
};
// To paint grey background on mic and back buttons, and close buttons for
// fullscreen launcher.
class SearchBoxImageButton : public views::ImageButton {
public:
explicit SearchBoxImageButton(views::ButtonListener* listener)
: ImageButton(listener) {
SetFocusBehavior(FocusBehavior::ALWAYS);
}
~SearchBoxImageButton() override {}
bool OnKeyPressed(const ui::KeyEvent& event) override {
// Disable space key to press the button. The keyboard events received
// by this view are forwarded from a Textfield (SearchBoxView) and key
// released events are not forwarded. This leaves the button in pressed
// state.
if (event.key_code() == ui::VKEY_SPACE)
return false;
return Button::OnKeyPressed(event);
}
private:
// views::View overrides:
void OnPaintBackground(gfx::Canvas* canvas) override {
if (state() == STATE_PRESSED) {
canvas->FillRect(gfx::Rect(size()), kSelectedColor);
}
}
const char* GetClassName() const override { return "SearchBoxImageButton"; }
DISALLOW_COPY_AND_ASSIGN(SearchBoxImageButton);
};
// To show context menu of selected view instead of that of focused view which
// is always this view when the user uses keyboard shortcut to open context
// menu.
class SearchBoxTextfield : public views::Textfield {
public:
explicit SearchBoxTextfield(SearchBoxView* search_box_view)
: search_box_view_(search_box_view) {}
~SearchBoxTextfield() override = default;
// Overridden from views::View:
void ShowContextMenu(const gfx::Point& p,
ui::MenuSourceType source_type) override {
views::View* selected_view =
search_box_view_->GetSelectedViewInContentsView();
if (source_type != ui::MENU_SOURCE_KEYBOARD || !selected_view) {
views::Textfield::ShowContextMenu(p, source_type);
return;
}
selected_view->ShowContextMenu(
selected_view->GetKeyboardContextMenuLocation(),
ui::MENU_SOURCE_KEYBOARD);
}
void OnFocus() override {
search_box_view_->SetSelected(true);
Textfield::OnFocus();
}
void OnBlur() override {
search_box_view_->SetSelected(false);
// Clear selection and set the caret to the end of the text.
ClearSelection();
Textfield::OnBlur();
}
private:
SearchBoxView* const search_box_view_;
DISALLOW_COPY_AND_ASSIGN(SearchBoxTextfield);
};
SearchBoxView::SearchBoxView(SearchBoxViewDelegate* delegate,
AppListViewDelegate* view_delegate,
AppListView* app_list_view)
: delegate_(delegate),
view_delegate_(view_delegate),
content_container_(new views::View),
search_box_(new SearchBoxTextfield(this)),
app_list_view_(app_list_view) {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
SetLayoutManager(std::make_unique<views::FillLayout>());
SetPreferredSize(gfx::Size(kPreferredWidth, kSearchBoxPreferredHeight));
UpdateSearchBoxBorder();
AddChildView(content_container_);
content_container_->SetBackground(
std::make_unique<SearchBoxBackground>(background_color_));
box_layout_ =
content_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::kHorizontal, gfx::Insets(0, kPadding),
kInnerPadding -
views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_TEXTFIELD_HORIZONTAL_TEXT_PADDING)));
box_layout_->set_cross_axis_alignment(
views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
box_layout_->set_minimum_cross_axis_size(kSearchBoxPreferredHeight);
search_box_->SetBorder(views::NullBorder());
search_box_->SetTextColor(kSearchTextColor);
search_box_->SetBackgroundColor(background_color_);
search_box_->set_controller(this);
search_box_->SetTextInputType(ui::TEXT_INPUT_TYPE_SEARCH);
search_box_->SetTextInputFlags(ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF);
back_button_ = new SearchBoxImageButton(this);
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
back_button_->SetImage(views::ImageButton::STATE_NORMAL,
rb.GetImageSkiaNamed(IDR_APP_LIST_FOLDER_BACK_NORMAL));
back_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
views::ImageButton::ALIGN_MIDDLE);
SetBackButtonLabel(false);
content_container_->AddChildView(back_button_);
is_tablet_mode_ = app_list_view->is_tablet_mode();
search_icon_ = new views::ImageView();
content_container_->AddChildView(search_icon_);
search_box_->set_placeholder_text_color(search_box_color_);
search_box_->set_placeholder_text_draw_flags(gfx::Canvas::TEXT_ALIGN_CENTER);
search_box_->SetFontList(search_box_->GetFontList().DeriveWithSizeDelta(2));
back_button_->SetVisible(false);
search_box_->SetCursorEnabled(is_search_box_active_);
content_container_->AddChildView(search_box_);
box_layout_->SetFlexForView(search_box_, 1);
// An invisible space view to align |search_box_| to center.
search_box_right_space_ = new views::View();
search_box_right_space_->SetPreferredSize(gfx::Size(kSearchIconSize, 0));
content_container_->AddChildView(search_box_right_space_);
close_button_ = new SearchBoxImageButton(this);
close_button_->SetImage(
views::ImageButton::STATE_NORMAL,
gfx::CreateVectorIcon(kIcCloseIcon, kCloseIconSize, search_box_color_));
close_button_->SetVisible(false);
close_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_APP_LIST_CLEAR_SEARCHBOX));
content_container_->AddChildView(close_button_);
view_delegate_->GetSpeechUI()->AddObserver(this);
view_delegate_->AddObserver(this);
ModelChanged();
}
SearchBoxView::~SearchBoxView() {
view_delegate_->GetSpeechUI()->RemoveObserver(this);
search_model_->search_box()->RemoveObserver(this);
view_delegate_->RemoveObserver(this);
}
void SearchBoxView::ModelChanged() {
if (search_model_)
search_model_->search_box()->RemoveObserver(this);
search_model_ = view_delegate_->GetSearchModel();
DCHECK(search_model_);
UpdateSearchIcon();
search_model_->search_box()->AddObserver(this);
SpeechRecognitionButtonPropChanged();
HintTextChanged();
OnWallpaperColorsChanged();
}
bool SearchBoxView::HasSearch() const {
return !search_box_->text().empty();
}
void SearchBoxView::ClearSearch() {
search_box_->SetText(base::string16());
UpdateCloseButtonVisisbility();
// Updates model and fires query changed manually because SetText() above
// does not generate ContentsChanged() notification.
UpdateModel(false);
NotifyQueryChanged();
app_list_view_->SetStateFromSearchBoxView(search_box_->text().empty());
}
gfx::Rect SearchBoxView::GetViewBoundsForSearchBoxContentsBounds(
const gfx::Rect& rect) const {
gfx::Rect view_bounds = rect;
view_bounds.Inset(-GetInsets());
return view_bounds;
}
views::ImageButton* SearchBoxView::back_button() {
return static_cast<views::ImageButton*>(back_button_);
}
views::ImageButton* SearchBoxView::close_button() {
return static_cast<views::ImageButton*>(close_button_);
}
void SearchBoxView::SetBackButtonLabel(bool folder) {
if (!back_button_)
return;
base::string16 back_button_label(l10n_util::GetStringUTF16(
folder ? IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME
: IDS_APP_LIST_BACK));
back_button_->SetAccessibleName(back_button_label);
back_button_->SetTooltipText(back_button_label);
}
void SearchBoxView::ShowBackOrGoogleIcon(bool show_back_button) {
search_icon_->SetVisible(!show_back_button);
back_button_->SetVisible(show_back_button);
content_container_->Layout();
}
void SearchBoxView::SetSearchBoxActive(bool active) {
if (active == is_search_box_active_)
return;
is_search_box_active_ = active;
UpdateSearchIcon();
UpdateBackgroundColor(background_color_);
search_box_->set_placeholder_text_draw_flags(
active ? (base::i18n::IsRTL() ? gfx::Canvas::TEXT_ALIGN_RIGHT
: gfx::Canvas::TEXT_ALIGN_LEFT)
: gfx::Canvas::TEXT_ALIGN_CENTER);
search_box_->set_placeholder_text_color(active ? kZeroQuerySearchboxColor
: search_box_color_);
search_box_->SetCursorEnabled(active);
if (active)
search_box_->RequestFocus();
search_box_right_space_->SetVisible(!active);
UpdateSearchBoxBorder();
UpdateKeyboardVisibility();
content_container_->Layout();
SchedulePaint();
}
void SearchBoxView::UpdateKeyboardVisibility() {
if (!is_tablet_mode_)
return;
keyboard::KeyboardController* const keyboard_controller =
keyboard::KeyboardController::GetInstance();
if (!keyboard_controller ||
is_search_box_active_ == keyboard::IsKeyboardVisible()) {
return;
}
if (is_search_box_active_) {
keyboard_controller->ShowKeyboard(false);
return;
}
keyboard_controller->HideKeyboard(
keyboard::KeyboardController::HIDE_REASON_MANUAL);
}
void SearchBoxView::HandleSearchBoxEvent(ui::LocatedEvent* located_event) {
if (located_event->type() == ui::ET_MOUSEWHEEL) {
if (!app_list_view_->HandleScroll(
located_event->AsMouseWheelEvent()->offset().y(),
ui::ET_MOUSEWHEEL)) {
return;
}
} else if (located_event->type() == ui::ET_MOUSE_PRESSED ||
located_event->type() == ui::ET_GESTURE_TAP) {
bool event_is_in_searchbox_bounds =
GetWidget()->GetWindowBoundsInScreen().Contains(
located_event->root_location());
if (is_search_box_active_ || !event_is_in_searchbox_bounds ||
!search_box_->text().empty())
return;
// If the event was within the searchbox bounds and in an inactive empty
// search box, enable the search box.
SetSearchBoxActive(true);
}
located_event->SetHandled();
}
bool SearchBoxView::OnTextfieldEvent() {
if (is_search_box_active_)
return false;
SetSearchBoxActive(true);
return true;
}
bool SearchBoxView::OnMouseWheel(const ui::MouseWheelEvent& event) {
if (contents_view_)
return contents_view_->OnMouseWheel(event);
return false;
}
void SearchBoxView::OnEnabledChanged() {
search_box_->SetEnabled(enabled());
if (speech_button_)
speech_button_->SetEnabled(enabled());
if (close_button_)
close_button_->SetEnabled(enabled());
}
const char* SearchBoxView::GetClassName() const {
return "SearchBoxView";
}
void SearchBoxView::OnGestureEvent(ui::GestureEvent* event) {
HandleSearchBoxEvent(event);
}
void SearchBoxView::OnMouseEvent(ui::MouseEvent* event) {
HandleSearchBoxEvent(event);
}
void SearchBoxView::OnKeyEvent(ui::KeyEvent* event) {
app_list_view_->RedirectKeyEventToSearchBox(event);
if (!CanProcessUpDownKeyTraversal(*event))
return;
// If focus is in search box view, up key moves focus to the last element of
// contents view, while down key moves focus to the first element of contents
// view.
views::View* v = GetFocusManager()->GetNextFocusableView(
contents_view_, GetWidget(), event->key_code() == ui::VKEY_UP, false);
if (v)
v->RequestFocus();
event->SetHandled();
}
ui::AXRole SearchBoxView::GetAccessibleWindowRole() const {
// Default role of root view is AX_ROLE_WINDOW which traps ChromeVox focus
// within the root view. Assign AX_ROLE_GROUP here to allow the focus to move
// from elements in search box to app list view.
return ui::AX_ROLE_GROUP;
}
bool SearchBoxView::ShouldAdvanceFocusToTopLevelWidget() const {
// Focus should be able to move from search box to items in app list view.
return true;
}
void SearchBoxView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
if (back_button_ && sender == back_button_) {
delegate_->BackButtonPressed();
} else if (speech_button_ && sender == speech_button_) {
view_delegate_->StartSpeechRecognition();
} else if (close_button_ && sender == close_button_) {
ClearSearch();
app_list_view_->SetStateFromSearchBoxView(true);
} else {
NOTREACHED();
}
}
void SearchBoxView::UpdateBackground(double progress,
AppListModel::State current_state,
AppListModel::State target_state) {
GetSearchBoxBackground()->set_corner_radius(gfx::Tween::LinearIntValueBetween(
progress, GetSearchBoxBorderCornerRadiusForState(current_state),
GetSearchBoxBorderCornerRadiusForState(target_state)));
const SkColor color = gfx::Tween::ColorValueBetween(
progress, GetBackgroundColorForState(current_state),
GetBackgroundColorForState(target_state));
UpdateBackgroundColor(color);
}
void SearchBoxView::UpdateLayout(double progress,
AppListModel::State current_state,
AppListModel::State target_state) {
box_layout_->set_inside_border_insets(
gfx::Insets(0, gfx::Tween::LinearIntValueBetween(
progress, GetBoxLayoutPaddingForState(current_state),
GetBoxLayoutPaddingForState(target_state))));
InvalidateLayout();
}
void SearchBoxView::OnTabletModeChanged(bool started) {
is_tablet_mode_ = started;
UpdateKeyboardVisibility();
}
int SearchBoxView::GetSearchBoxBorderCornerRadiusForState(
AppListModel::State state) const {
if (state == AppListModel::STATE_SEARCH_RESULTS &&
!app_list_view_->is_in_drag()) {
return kSearchBoxBorderCornerRadiusSearchResult;
}
return kSearchBoxBorderCornerRadius;
}
SkColor SearchBoxView::GetBackgroundColorForState(
AppListModel::State state) const {
if (state == AppListModel::STATE_SEARCH_RESULTS)
return kCardBackgroundColor;
return background_color_;
}
void SearchBoxView::UpdateOpacity() {
// The opacity of searchbox is a function of the fractional displacement of
// the app list from collapsed(0) to peeking(1) state. When the fraction
// changes from |kOpacityStartFraction| to |kOpaticyEndFraction|, the opacity
// of searchbox changes from 0.f to 1.0f.
ContentsView* contents_view = static_cast<ContentsView*>(contents_view_);
int app_list_y_position_in_screen =
contents_view->app_list_view()->app_list_y_position_in_screen();
float fraction =
std::max<float>(0, contents_view->app_list_view()->GetScreenBottom() -
kShelfSize - app_list_y_position_in_screen) /
(kPeekingAppListHeight - kShelfSize);
float opacity =
std::min(std::max((fraction - kOpacityStartFraction) /
(kOpacityEndFraction - kOpacityStartFraction),
0.f),
1.0f);
AppListView* app_list_view = contents_view->app_list_view();
bool should_restore_opacity =
!app_list_view->is_in_drag() &&
(app_list_view->app_list_state() != AppListViewState::CLOSED);
// Restores the opacity of searchbox if the gesture dragging ends.
this->layer()->SetOpacity(should_restore_opacity ? 1.0f : opacity);
contents_view->search_results_page_view()->layer()->SetOpacity(
should_restore_opacity ? 1.0f : opacity);
}
views::View* SearchBoxView::GetSelectedViewInContentsView() const {
if (!contents_view_)
return nullptr;
return static_cast<ContentsView*>(contents_view_)->GetSelectedView();
}
void SearchBoxView::SetSelected(bool selected) {
if (selected_ == selected)
return;
selected_ = selected;
if (selected) {
// Set the ChromeVox focus to the search box.
search_box_->NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
}
UpdateSearchBoxBorder();
Layout();
SchedulePaint();
}
void SearchBoxView::UpdateModel(bool initiated_by_user) {
// Temporarily remove from observer to ignore notifications caused by us.
search_model_->search_box()->RemoveObserver(this);
search_model_->search_box()->Update(search_box_->text(), initiated_by_user);
search_model_->search_box()->SetSelectionModel(
search_box_->GetSelectionModel());
search_model_->search_box()->AddObserver(this);
}
void SearchBoxView::NotifyQueryChanged() {
DCHECK(delegate_);
delegate_->QueryChanged(this);
}
void SearchBoxView::ContentsChanged(views::Textfield* sender,
const base::string16& new_contents) {
// Set search box focused when query changes.
search_box_->RequestFocus();
UpdateModel(true);
NotifyQueryChanged();
SetSearchBoxActive(true);
UpdateCloseButtonVisisbility();
const bool is_trimmed_query_empty = IsSearchBoxTrimmedQueryEmpty();
// If the query is only whitespace, don't transition the AppListView state.
app_list_view_->SetStateFromSearchBoxView(is_trimmed_query_empty);
}
bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
const ui::KeyEvent& key_event) {
if (key_event.type() == ui::ET_KEY_PRESSED &&
key_event.key_code() == ui::VKEY_RETURN) {
if (!IsSearchBoxTrimmedQueryEmpty()) {
// Hitting Enter when focus is on search box opens the first result.
ui::KeyEvent event(key_event);
views::View* first_result_view =
static_cast<ContentsView*>(contents_view_)
->search_results_page_view()
->first_result_view();
if (first_result_view)
first_result_view->OnKeyEvent(&event);
return true;
}
if (!is_search_box_active()) {
SetSearchBoxActive(true);
return true;
}
return false;
}
if (CanProcessLeftRightKeyTraversal(key_event))
return ProcessLeftRightKeyTraversalForTextfield(search_box_, key_event);
return false;
}
bool SearchBoxView::HandleMouseEvent(views::Textfield* sender,
const ui::MouseEvent& mouse_event) {
if (mouse_event.type() == ui::ET_MOUSEWHEEL) {
return app_list_view_->HandleScroll(
(&mouse_event)->AsMouseWheelEvent()->offset().y(), ui::ET_MOUSEWHEEL);
} else {
return OnTextfieldEvent();
}
}
bool SearchBoxView::HandleGestureEvent(views::Textfield* sender,
const ui::GestureEvent& gesture_event) {
return OnTextfieldEvent();
}
void SearchBoxView::SpeechRecognitionButtonPropChanged() {
const SearchBoxModel::SpeechButtonProperty* speech_button_prop =
search_model_->search_box()->speech_button();
if (speech_button_prop) {
if (!speech_button_) {
speech_button_ = new SearchBoxImageButton(this);
content_container_->AddChildView(speech_button_);
}
speech_button_->SetAccessibleName(speech_button_prop->accessible_name);
speech_button_->SetImage(
views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(kIcMicBlackIcon, kMicIconSize,
kDefaultSearchboxColor));
speech_button_->SetTooltipText(speech_button_prop->tooltip);
} else {
if (speech_button_) {
// Deleting a view will detach it from its parent.
delete speech_button_;
speech_button_ = nullptr;
}
}
Layout();
}
void SearchBoxView::HintTextChanged() {
const app_list::SearchBoxModel* search_box = search_model_->search_box();
search_box_->set_placeholder_text(search_box->hint_text());
search_box_->SetAccessibleName(search_box->accessible_name());
}
void SearchBoxView::SelectionModelChanged() {
search_box_->SelectSelectionModel(
search_model_->search_box()->selection_model());
}
void SearchBoxView::Update() {
search_box_->SetText(search_model_->search_box()->text());
UpdateCloseButtonVisisbility();
NotifyQueryChanged();
}
void SearchBoxView::OnWallpaperColorsChanged() {
std::vector<SkColor> prominent_colors;
GetWallpaperProminentColors(&prominent_colors);
if (prominent_colors.empty())
return;
DCHECK_EQ(static_cast<size_t>(ColorProfileType::NUM_OF_COLOR_PROFILES),
prominent_colors.size());
SetSearchBoxColor(
prominent_colors[static_cast<int>(ColorProfileType::DARK_MUTED)]);
SetBackgroundColor(
prominent_colors[static_cast<int>(ColorProfileType::LIGHT_VIBRANT)]);
UpdateSearchIcon();
if (speech_button_) {
speech_button_->SetImage(
views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(kIcMicBlackIcon, kMicIconSize,
search_box_color_));
}
close_button_->SetImage(
views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(kIcCloseIcon, kCloseIconSize, search_box_color_));
search_box_->set_placeholder_text_color(search_box_color_);
UpdateBackgroundColor(background_color_);
SchedulePaint();
}
void SearchBoxView::OnSpeechRecognitionStateChanged(
SpeechRecognitionState new_state) {
SpeechRecognitionButtonPropChanged();
SchedulePaint();
}
void SearchBoxView::UpdateSearchIcon() {
const gfx::VectorIcon& google_icon =
is_search_box_active() ? kIcGoogleColorIcon : kIcGoogleBlackIcon;
const gfx::VectorIcon& icon = search_model_->search_engine_is_google()
? google_icon
: kIcSearchEngineNotGoogleIcon;
search_icon_->SetImage(
gfx::CreateVectorIcon(icon, kSearchIconSize, search_box_color_));
}
void SearchBoxView::GetWallpaperProminentColors(std::vector<SkColor>* colors) {
view_delegate_->GetWallpaperProminentColors(colors);
}
// TODO(crbug.com/755219): Unify this with UpdateBackgroundColor.
void SearchBoxView::SetBackgroundColor(SkColor light_vibrant) {
const SkColor light_vibrant_mixed = color_utils::AlphaBlend(
SK_ColorWHITE, light_vibrant, kLightVibrantBlendAlpha);
background_color_ = SK_ColorTRANSPARENT == light_vibrant
? kSearchBoxBackgroundDefault
: light_vibrant_mixed;
}
void SearchBoxView::SetSearchBoxColor(SkColor color) {
search_box_color_ =
SK_ColorTRANSPARENT == color ? kDefaultSearchboxColor : color;
}
// TODO(crbug.com/755219): Unify this with SetBackgroundColor.
void SearchBoxView::UpdateBackgroundColor(SkColor color) {
if (is_search_box_active_)
color = kSearchBoxBackgroundDefault;
GetSearchBoxBackground()->set_color(color);
search_box_->SetBackgroundColor(color);
}
void SearchBoxView::UpdateCloseButtonVisisbility() {
if (!close_button_)
return;
bool should_show_close_button_ = !search_box_->text().empty();
if (close_button_->visible() == should_show_close_button_)
return;
close_button_->SetVisible(should_show_close_button_);
content_container_->Layout();
}
SearchBoxBackground* SearchBoxView::GetSearchBoxBackground() const {
return static_cast<SearchBoxBackground*>(content_container_->background());
}
bool SearchBoxView::IsSearchBoxTrimmedQueryEmpty() const {
base::string16 trimmed_query;
base::TrimWhitespace(search_box_->text(), base::TrimPositions::TRIM_ALL,
&trimmed_query);
return trimmed_query.empty();
}
void SearchBoxView::UpdateSearchBoxBorder() {
if (selected() && !is_search_box_active()) {
// Show a gray ring around search box to indicate that the search box is
// selected. Do not show it when search box is active, because blinking
// cursor already indicates that.
SetBorder(views::CreateRoundedRectBorder(kSearchBoxBorderWidth,
kSearchBoxFocusBorderCornerRadius,
kSearchBoxBorderColor));
return;
}
// Creates an empty border as a placeholder for colored border so that
// re-layout won't move views below the search box.
SetBorder(
views::CreateEmptyBorder(kSearchBoxBorderWidth, kSearchBoxBorderWidth,
kSearchBoxBorderWidth, kSearchBoxBorderWidth));
}
} // namespace app_list