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
|
// Copyright 2023 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_QUICK_INSERT_VIEWS_QUICK_INSERT_VIEW_H_
#define ASH_QUICK_INSERT_VIEWS_QUICK_INSERT_VIEW_H_
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include "ash/ash_export.h"
#include "ash/quick_insert/metrics/quick_insert_performance_metrics.h"
#include "ash/quick_insert/model/quick_insert_search_results_section.h"
#include "ash/quick_insert/quick_insert_category.h"
#include "ash/quick_insert/quick_insert_controller.h"
#include "ash/quick_insert/quick_insert_search_result.h"
#include "ash/quick_insert/views/quick_insert_emoji_bar_view_delegate.h"
#include "ash/quick_insert/views/quick_insert_key_event_handler.h"
#include "ash/quick_insert/views/quick_insert_preview_bubble_controller.h"
#include "ash/quick_insert/views/quick_insert_pseudo_focus_handler.h"
#include "ash/quick_insert/views/quick_insert_search_results_view_delegate.h"
#include "ash/quick_insert/views/quick_insert_submenu_controller.h"
#include "ash/quick_insert/views/quick_insert_zero_state_view_delegate.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/view.h"
#include "ui/views/view_tracker.h"
#include "ui/views/widget/widget_delegate.h"
namespace views {
class Widget;
class NonClientFrameView;
} // namespace views
namespace ash {
enum class QuickInsertCapsLockPosition;
enum class QuickInsertLayoutType;
enum class QuickInsertPositionType;
enum class QuickInsertPseudoFocusDirection;
class QuickInsertEmojiBarView;
class QuickInsertMainContainerView;
class QuickInsertSearchFieldView;
class QuickInsertPageView;
class QuickInsertSearchResultsSection;
class QuickInsertSearchResultsView;
class QuickInsertTraversableItemContainer;
class QuickInsertViewDelegate;
class QuickInsertZeroStateView;
// View for the Quick Insert widget.
class ASH_EXPORT QuickInsertView
: public views::WidgetDelegateView,
public QuickInsertZeroStateViewDelegate,
public QuickInsertSearchResultsViewDelegate,
public QuickInsertEmojiBarViewDelegate,
public QuickInsertPseudoFocusHandler,
public QuickInsertPreviewBubbleController::Observer {
METADATA_HEADER(QuickInsertView, views::WidgetDelegateView)
public:
// `delegate` must remain valid for the lifetime of this class.
explicit QuickInsertView(QuickInsertViewDelegate* delegate,
const gfx::Rect& anchor_bounds,
QuickInsertLayoutType layout_type,
QuickInsertPositionType position_type,
base::TimeTicks trigger_event_timestamp);
QuickInsertView(const QuickInsertView&) = delete;
QuickInsertView& operator=(const QuickInsertView&) = delete;
~QuickInsertView() override;
// Time from when a search starts to when the previous set of results are
// cleared.
// Slightly longer than the real burn in period to ensure empty results do not
// flash on the screen before showing burn-in results.
static constexpr base::TimeDelta kClearResultsTimeout =
QuickInsertController::kBurnInPeriod + base::Milliseconds(50);
// views::WidgetDelegateView:
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
views::Widget* widget) override;
void AddedToWidget() override;
void RemovedFromWidget() override;
void Layout(PassKey) override;
// QuickInsertZeroStateViewDelegate:
void SelectZeroStateCategory(QuickInsertCategory category) override;
void SelectZeroStateResult(const QuickInsertSearchResult& result) override;
void GetZeroStateSuggestedResults(SuggestedResultsCallback callback) override;
void RequestPseudoFocus(views::View* view) override;
void OnZeroStateViewHeightChanged() override;
void SetCapsLockDisplayed(bool displayed) override;
QuickInsertCapsLockPosition GetCapsLockPosition() override;
// QuickInsertSearchResultsViewDelegate:
void SelectSearchResult(const QuickInsertSearchResult& result) override;
void SelectMoreResults(QuickInsertSectionType type) override;
QuickInsertActionType GetActionForResult(
const QuickInsertSearchResult& result) override;
void OnSearchResultsViewHeightChanged() override;
// QuickInsertEmojiBarViewDelegate:
void ToggleGifs(bool is_checked) override;
void ShowEmojiPicker(ui::EmojiPickerCategory category) override;
// QuickInsertPseudoFocusHandler:
bool DoPseudoFocusedAction() override;
bool MovePseudoFocusUp() override;
bool MovePseudoFocusDown() override;
bool MovePseudoFocusLeft() override;
bool MovePseudoFocusRight() override;
bool AdvancePseudoFocus(QuickInsertPseudoFocusDirection direction) override;
// QuickInsertPreviewBubbleController::Observer:
void OnPreviewBubbleVisibilityChanged(bool visible) override;
// Returns the target bounds for this Quick Insert view. The target bounds try
// to vertically align `search_field_view_` with `anchor_bounds`.
// `anchor_bounds` and returned bounds should be in screen coordinates.
gfx::Rect GetTargetBounds(const gfx::Rect& anchor_bounds,
QuickInsertLayoutType layout_type);
QuickInsertSubmenuController& submenu_controller_for_testing() {
return submenu_controller_;
}
QuickInsertPreviewBubbleController& preview_controller_for_testing() {
return preview_controller_;
}
QuickInsertSearchFieldView& search_field_view_for_testing() {
return *search_field_view_;
}
QuickInsertSearchResultsView& search_results_view_for_testing() {
return *search_results_view_;
}
QuickInsertSearchResultsView& category_results_view_for_testing() {
return *category_results_view_;
}
QuickInsertZeroStateView& zero_state_view_for_testing() {
return *zero_state_view_;
}
QuickInsertEmojiBarView* emoji_bar_view_for_testing() {
return emoji_bar_view_;
}
private:
// Sets the search text field's query text to the query, focuses it, then
// updates the active page - starting / ending a search if necessary.
void UpdateSearchQueryAndActivePage(std::u16string query);
// Updates the active page based on the search text field's query text, as
// well as the active category.
// If the search text field's query text is non-empty, this starts a search
// and sets the active page to the search view after a delay via
// `OnClearResultsTimerFired` and `PublishSearchResults`.
// Otherwise, stops any previous searches and immediately sets the active page
// to the zero state / category results view, fetching category results if
// necessary.
void UpdateActivePage();
// Displays `results` in the emoji bar.
void PublishEmojiResults(std::vector<QuickInsertEmojiResult> results);
// Clears the search results and sets the active page to the search view.
void OnClearResultsTimerFired();
// Displays `results` in the search view and sets it as the active page.
// If `results` is empty and no results were previously published, then a "no
// results found" view is shown instead of a blank view.
void PublishSearchResults(
std::vector<QuickInsertSearchResultsSection> results);
// Selects a category. This shows the category view and fetches zero-state
// results for the category, which are returned to `PublishCategoryResults`.
void SelectCategory(QuickInsertCategory category);
// Selects a category. This shows the category view and fetches search
// results for the category based on `query`, which are returned to
// `PublishSearchResults`.
void SelectCategoryWithQuery(QuickInsertCategory category,
std::u16string_view query);
// Displays `results` in the category view.
void PublishCategoryResults(
QuickInsertCategory category,
std::vector<QuickInsertSearchResultsSection> results);
// Adds the main container, which includes the search field and contents
// pages.
void AddMainContainerView(QuickInsertLayoutType layout_type);
// Adds the emoji bar, which contains emoji and other expression results and
// is shown above the main container.
void AddEmojiBarView();
// Sets `page_view` as the active page in `main_container_view_`.
void SetActivePage(QuickInsertPageView* page_view);
// Sets emoji bar visibility, or does nothing if the emoji bar is not enabled.
void SetEmojiBarVisibleIfEnabled(bool visible);
// Moves pseudo focus between different parts of the QuickInsertView, i.e.
// between the emoji bar and the main container.
void AdvanceActiveItemContainer(QuickInsertPseudoFocusDirection direction);
// Sets `view` as the pseudo focused view, i.e. the view which responds to
// user actions that trigger `DoPseudoFocusedAction`. If `view` is null,
// pseudo focus instead moves back to the search field.
void SetPseudoFocusedView(views::View* view);
views::View* GetPseudoFocusedView();
// Removes the currently selected category filter, with the option to clear
// the search field.
void ResetSelectedCategory(bool reset_query);
// Called when the search field back button is pressed.
void OnSearchBackButtonPressed();
// Clears the current results in the emoji bar and shows recent and
// placeholder emojis instead.
void ResetEmojiBarToZeroState();
// Returns true if `view` is contained in a submenu of this QuickInsertView.
bool IsContainedInSubmenu(views::View* view);
// Called to indicate that the Quick Insert widget bounds need to be be
// updated (e.g. to re-align the Quick Insert search field after results have
// changed).
void SetWidgetBoundsNeedsUpdate();
// The currently selected category.
// Should only be set to `std::nullopt` through `OnSearchBackButtonPressed`.
// Should only be set to a value through `SelectCategory` and
// `SelectCategoryWithQuery`.
std::optional<QuickInsertCategory> selected_category_;
// Whether the GIF toggle is checked (i.e. should only show GIF results).
bool is_gif_toggle_checked_ = false;
// The category which `category_results_view_` has results for.
// Used for caching results if the user did not change their selected
// category.
// For example:
// - When a user starts a filtered search from a category's suggested results,
// then clears the search query, the old suggested results are not cleared
// as `last_suggested_results_category_ == selected_category_`.
// - When a user starts a non-filtered search from zero state, then filters
// results to a category, then clears the search query, new results will be
// fetched as the `last_suggested_results_category_ != selected_category_`.
std::optional<QuickInsertCategory> last_suggested_results_category_;
// The whitespace-trimmed query and category when `UpdateActivePage()` was
// last called.
// Used for avoid unnecessary searches if `UpdateActivePage()` is called again
// with the same {query, selected_category, is_gif_toggle_checked}.
std::u16string last_query_;
std::optional<QuickInsertCategory> last_selected_category_;
bool last_is_gif_toggle_checked_ = false;
QuickInsertKeyEventHandler key_event_handler_;
QuickInsertSubmenuController submenu_controller_;
QuickInsertPreviewBubbleController preview_controller_;
QuickInsertPerformanceMetrics performance_metrics_;
raw_ptr<QuickInsertViewDelegate> delegate_ = nullptr;
// The main container contains the search field and contents pages.
raw_ptr<QuickInsertMainContainerView> main_container_view_ = nullptr;
raw_ptr<QuickInsertSearchFieldView> search_field_view_ = nullptr;
raw_ptr<QuickInsertZeroStateView> zero_state_view_ = nullptr;
raw_ptr<QuickInsertSearchResultsView> category_results_view_ = nullptr;
raw_ptr<QuickInsertSearchResultsView> search_results_view_ = nullptr;
raw_ptr<QuickInsertEmojiBarView> emoji_bar_view_ = nullptr;
// The item container which contains `pseudo_focused_view_` and will respond
// to keyboard navigation events.
raw_ptr<QuickInsertTraversableItemContainer> active_item_container_ = nullptr;
// Tracks the currently pseudo focused view, which responds to user actions
// that trigger `DoPseudoFocusedAction`.
views::ViewTracker pseudo_focused_view_tracker_;
// If true, the Widget bounds should be adjusted on the next layout.
bool widget_bounds_needs_update_ = true;
// Clears `search_results_view_`'s old search results when a new search is
// started - after `kClearResultsTimeout`, or when the first search results
// come in (whatever is earliest).
// This timer is running iff the first set of results for the current search
// have not been published yet.
base::OneShotTimer clear_results_timer_;
base::ScopedObservation<QuickInsertPreviewBubbleController,
QuickInsertPreviewBubbleController::Observer>
preview_bubble_observation_{this};
base::WeakPtrFactory<QuickInsertView> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_QUICK_INSERT_VIEWS_QUICK_INSERT_VIEW_H_
|