1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310

ash / search_box / search_box_view_base.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SEARCH_BOX_SEARCH_BOX_VIEW_BASE_H_
#define ASH_SEARCH_BOX_SEARCH_BOX_VIEW_BASE_H_

#include <optional>
#include <string>
#include <vector>

#include "ash/search_box/search_box_constants.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/view.h"
#include "ui/views/view_tracker.h"

namespace gfx {
class ImageSkia;
}  // namespace gfx

namespace views {
class BoxLayoutView;
class ImageView;
class Label;
class Textfield;
}  // namespace views

namespace ash {

class LauncherSearchIphView;
class SearchBoxImageButton;
class SearchIconImageView;

// SearchBoxViewBase consists of icons and a Textfield. The Textfiled is for
// inputting queries and triggering callbacks. The icons include a search icon,
// a close icon and a back icon for different functionalities. This class
// provides common functions for the search box view across Chrome OS.
class SearchBoxViewBase : public views::View,
                          public views::TextfieldController {
  METADATA_HEADER(SearchBoxViewBase, views::View)

 public:
  SearchBoxViewBase();

  SearchBoxViewBase(const SearchBoxViewBase&) = delete;
  SearchBoxViewBase& operator=(const SearchBoxViewBase&) = delete;

  ~SearchBoxViewBase() override;

  // Creates the search box close button at the right edge of the search box.
  // The close button will initially be hidden. The visibility will be updated
  // appropriately when `UpdateButtonsVisibility()` gets called.
  views::ImageButton* CreateCloseButton(
      const base::RepeatingClosure& button_callback);

  // Creates `end_button_container_`, a container view that hosts one or both of
  // the `assistant_button_` and `sunfish_button_`.
  void CreateEndButtonContainer();

  // Creates the sunfish launcher button at the right edge of the search box,
  // next to the assistant button. Note that it will only be shown if the close
  // button is hidden, as the buttons have the same expected position within the
  // search box.
  views::ImageButton* CreateSunfishButton(
      const base::RepeatingClosure& button_callback);

  // Creates the search box assistant button at the right edge of the search
  // box. Note that the assistant button will only be shown if close button is
  // hidden, as the buttons have the same expected position within the search
  // box.
  // The assistant button will initially be hidden. The visibility will be
  // updated appropriately when `UpdateButtonsVisibility()` gets called.
  views::ImageButton* CreateAssistantButton(
      const base::RepeatingClosure& button_callback);

  // Creates the search box assistant new entry point button at the right edge
  // of the search box in LTR layout. The assistant new entry point button will
  // initially be hidden. The visibility will be updated appropriately when
  // `UpdateButtonsVisibility()` gets called.
  views::ImageButton* CreateAssistantNewEntryPointButton(
      const base::RepeatingClosure& button_callback);

  // Creates the search box category filter button at the right edge of the
  // search box, where clicking on it shows a bubble for the users to select
  // search categories to show.
  // The filter button will initially be hidden and will be shown along with the
  // close button. The visibility will be updated appropriately when
  // `UpdateButtonsVisibility()` gets called.
  views::ImageButton* CreateFilterButton(
      const base::RepeatingClosure& button_callback);

  bool HasSearch() const;

  // Returns the bounds to use for the view (including the shadow) given the
  // desired bounds of the search box contents.
  gfx::Rect GetViewBoundsForSearchBoxContentsBounds(
      const gfx::Rect& rect) const;

  views::ImageButton* sunfish_button();
  views::ImageButton* assistant_button();
  views::ImageButton* assistant_new_entry_point_button();
  views::View* edge_button_container();
  views::ImageButton* close_button();
  views::ImageButton* filter_button();
  views::View* filter_and_close_button_container();
  views::ImageView* search_icon();
  views::Textfield* search_box() { return search_box_; }

  void SetIphView(std::unique_ptr<LauncherSearchIphView> iph_view);
  LauncherSearchIphView* GetIphView();
  void DeleteIphView();

  // Called when the query in the search box textfield changes. The search box
  // implementation is expected to handle the new query.
  // `query` the new search box query.
  // `initiated_by_user` whether the query changes was a result of the user
  // typing.
  virtual void HandleQueryChange(const std::u16string& query,
                                 bool initiated_by_user) = 0;

  // Explicitly triggers the search while keeping the same query.
  void TriggerSearch();

  // Sets contents for the title and category labels used for ghost text
  // autocomplete.
  void MaybeSetAutocompleteGhostText(const std::u16string& title,
                                     const std::u16string& category);

  std::string GetSearchBoxGhostTextForTest();

  // Setting the search box active left aligns the placeholder text, changes
  // the color of the placeholder text, and enables cursor blink. Setting the
  // search box inactive center aligns the placeholder text, sets the color, and
  // disables cursor blink.
  void SetSearchBoxActive(bool active, ui::EventType event_type);

  // Handles Gesture and Mouse Events sent from |search_box_|.
  bool OnTextfieldEvent(ui::EventType type);

  // Overridden from views::View:
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;
  void OnGestureEvent(ui::GestureEvent* event) override;
  void OnMouseEvent(ui::MouseEvent* event) override;
  void OnThemeChanged() override;

  // Allows for search box to be notified of gestures occurring outside, without
  // deactivating the searchbox.
  void NotifyGestureEvent();

  // Whether the search box is active.
  bool is_search_box_active() const { return is_search_box_active_; }

  void OnSearchBoxFocusedChanged();

  // Whether the trimmed query in the search box is empty.
  bool IsSearchBoxTrimmedQueryEmpty() const;

  virtual void UpdateSearchTextfieldAccessibleActiveDescendantId();

  void ClearSearch();

  // Called when the search box active state changes.
  virtual void OnSearchBoxActiveChanged(bool active);

  // Updates the painting if the focus moves to or from the search box.
  virtual void UpdateSearchBoxFocusPaint();

 protected:
  struct InitParams {
    InitParams();
    ~InitParams();
    InitParams(const InitParams&) = delete;
    InitParams& operator=(const InitParams&) = delete;

    // Whether to show close button if the search box is active and empty.
    bool show_close_button_when_active = false;

    // Whether to create a rounded-rect background.
    bool create_background = true;

    // Whether to animate the transition when the search icon is changed.
    bool animate_changing_search_icon = false;

    // Whether we should increase spacing between `search_icon_', 'search_box_',
    // and the 'search_box_button_container_'.
    bool increase_child_view_padding = false;

    // If set, the margins that should be used for the search box text field.
    std::optional<gfx::Insets> textfield_margins;
  };

  void Init(const InitParams& params);

  // TODO(http://b/362364735): Fix close button positioning when Sunfish is
  // enabled.
  // Updates the visibility of the close and assistant buttons.
  void UpdateButtonsVisibility();

  // When necessary, starts the fade in animation for the button container.
  void MaybeFadeContainerIn(views::View* container);

  // When necessary, starts the fade out animation for the button container.
  void MaybeFadeContainerOut(views::View* container);

  // Used as a callback to set the button container's visibility to false.
  void SetContainerVisibilityHidden(views::View* container);

  // Overridden from views::TextfieldController:
  void ContentsChanged(views::Textfield* sender,
                       const std::u16string& new_contents) override;
  bool HandleMouseEvent(views::Textfield* sender,
                        const ui::MouseEvent& mouse_event) override;
  bool HandleGestureEvent(views::Textfield* sender,
                          const ui::GestureEvent& gesture_event) override;

  views::BoxLayoutView* box_layout_view() { return content_container_; }

  void SetSearchBoxBackgroundCornerRadius(int corner_radius);

  void SetSearchIconImage(gfx::ImageSkia image);

  void SetShowAssistantButton(bool show);
  void SetShowAssistantNewEntryPointButton(bool show);
  void SetShowSunfishButton(bool show);

  // Detects |kMousePressed| and |EventType::kGestureTap| events on the white
  // background of the search box.
  virtual void HandleSearchBoxEvent(ui::LocatedEvent* located_event);

  // Updates the search box's background color.
  void UpdateBackgroundColor(SkColor color);

  // Shows/hides the virtual keyboard if the search box is active.
  virtual void UpdateKeyboardVisibility() {}

  // Updates the color and alignment of the placeholder text.
  virtual void UpdatePlaceholderTextStyle() {}

  // Update search box border based on whether the search box is activated.
  virtual void UpdateSearchBoxBorder() {}

  // Updates the style of the searchbox labels and textfield.
  void SetPreferredStyleForAutocompleteText(const gfx::FontList& font_list,
                                            ui::ColorId text_color_id);
  void SetPreferredStyleForSearchboxText(const gfx::FontList& font_list,
                                         ui::ColorId text_color_id);

  // Initializes `filter_and_close_button_container_` if it has not already been
  // done.
  void MaybeCreateFilterAndCloseButtonContainer();

 private:
  void OnEnabledChanged();

  // Owned by views hierarchy.
  raw_ptr<views::BoxLayoutView> main_container_;
  raw_ptr<views::BoxLayoutView> content_container_;
  raw_ptr<SearchIconImageView> search_icon_ = nullptr;
  raw_ptr<SearchBoxImageButton> assistant_button_ = nullptr;
  raw_ptr<SearchBoxImageButton> assistant_new_entry_point_button_ = nullptr;
  raw_ptr<SearchBoxImageButton> sunfish_button_ = nullptr;
  raw_ptr<SearchBoxImageButton> close_button_ = nullptr;
  raw_ptr<SearchBoxImageButton> filter_button_ = nullptr;
  raw_ptr<views::BoxLayoutView> end_button_container_ = nullptr;
  raw_ptr<views::BoxLayoutView> filter_and_close_button_container_ = nullptr;
  raw_ptr<views::BoxLayoutView> text_container_ = nullptr;

  raw_ptr<views::Textfield> search_box_;
  raw_ptr<views::BoxLayoutView> ghost_text_container_ = nullptr;
  raw_ptr<views::Label> separator_label_ = nullptr;
  raw_ptr<views::Label> autocomplete_ghost_text_ = nullptr;
  raw_ptr<views::Label> category_separator_label_ = nullptr;
  raw_ptr<views::Label> category_ghost_text_ = nullptr;

  raw_ptr<views::View> search_box_button_container_ = nullptr;

  views::ViewTracker iph_view_tracker_;

  // Whether the search box is active.
  bool is_search_box_active_ = false;
  // Whether to show close button if the search box is active and empty.
  bool show_close_button_when_active_ = false;
  // Whether to show assistant button.
  bool show_assistant_button_ = false;
  // Whether to show assistant new entry point button.
  bool show_assistant_new_entry_point_button_ = false;
  // Whether to show sunfish button.
  bool show_sunfish_button_ = false;

  base::CallbackListSubscription enabled_changed_subscription_ =
      AddEnabledChangedCallback(
          base::BindRepeating(&SearchBoxViewBase::OnEnabledChanged,
                              base::Unretained(this)));

  base::WeakPtrFactory<SearchBoxViewBase> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_SEARCH_BOX_SEARCH_BOX_VIEW_BASE_H_