0

picker: show recent items in zero state view

Bug: b:339527335
Change-Id: Ib8cd028511f19482c6369e64a9b295f9ca864a01
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5528931
Reviewed-by: Darren Shen <shend@chromium.org>
Commit-Queue: Jing Wang <jiwan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1301058}
This commit is contained in:
Jing Wang
2024-05-15 02:40:59 +00:00
committed by Chromium LUCI CQ
parent 02ae13d457
commit 32b1e2da9d
13 changed files with 94 additions and 128 deletions

@@ -67,6 +67,18 @@ std::vector<PickerCategory> PickerModel::GetAvailableCategories() const {
return categories;
}
std::vector<PickerCategory> PickerModel::GetRecentResultsCategories() const {
if (HasSelectedText()) {
return std::vector<PickerCategory>{};
}
return {
PickerCategory::kDriveFiles,
PickerCategory::kLocalFiles,
PickerCategory::kLinks,
};
}
bool PickerModel::HasSelectedText() const {
return !selected_text_.empty();
}

@@ -33,6 +33,8 @@ class ASH_EXPORT PickerModel {
std::vector<PickerCategory> GetAvailableCategories() const;
std::vector<PickerCategory> GetRecentResultsCategories() const;
std::u16string_view selected_text() const;
bool HasSelectedText() const;

@@ -361,8 +361,9 @@ std::vector<PickerCategory> PickerController::GetAvailableCategories() {
: model_->GetAvailableCategories();
}
bool PickerController::ShouldShowRecentResults() {
return model_ && !model_->HasSelectedText();
std::vector<PickerCategory> PickerController::GetRecentResultsCategories() {
return model_ == nullptr ? std::vector<PickerCategory>{}
: model_->GetRecentResultsCategories();
}
void PickerController::GetResultsForCategory(PickerCategory category,

@@ -75,7 +75,7 @@ class ASH_EXPORT PickerController
// PickerViewDelegate:
std::vector<PickerCategory> GetAvailableCategories() override;
bool ShouldShowRecentResults() override;
std::vector<PickerCategory> GetRecentResultsCategories() override;
void GetResultsForCategory(PickerCategory category,
SearchResultsCallback callback) override;
void TransformSelectedText(PickerCategory category) override;

@@ -146,6 +146,19 @@ PickerCategory GetCategoryForMoreResults(PickerSectionType type) {
}
}
std::vector<PickerSearchResult> GetMostRecentResult(
std::vector<PickerSearchResultsSection> results) {
if (results.empty() ||
results[0].type() != PickerSectionType::kRecentlyUsed) {
return {};
}
base::span<const PickerSearchResult> search_results = results[0].results();
if (search_results.empty()) {
return {};
}
return {search_results[0]};
}
} // namespace
PickerView::PickerView(PickerViewDelegate* delegate,
@@ -209,6 +222,13 @@ void PickerView::SelectZeroStateResult(const PickerSearchResult& result) {
SelectSearchResult(result);
}
void PickerView::GetZeroStateRecentResults(PickerCategory category,
SearchResultsCallback callback) {
delegate_->GetResultsForCategory(
category,
base::BindRepeating(&GetMostRecentResult).Then(std::move(callback)));
}
void PickerView::GetSuggestedZeroStateEditorResults(
SuggestedEditorResultsCallback callback) {
delegate_->GetSuggestedEditorResults(std::move(callback));
@@ -397,7 +417,7 @@ void PickerView::AddContentsViewWithSeparator(PickerLayoutType layout_type) {
zero_state_view_ =
contents_view_->AddPage(std::make_unique<PickerZeroStateView>(
this, delegate_->GetAvailableCategories(),
delegate_->ShouldShowRecentResults(), kMaxSize.width(),
delegate_->GetRecentResultsCategories(), kMaxSize.width(),
delegate_->GetAssetFetcher()));
category_view_ = contents_view_->AddPage(std::make_unique<PickerCategoryView>(

@@ -73,6 +73,8 @@ class ASH_EXPORT PickerView : public views::WidgetDelegateView,
// PickerZeroStateViewDelegate:
void SelectZeroStateCategory(PickerCategory category) override;
void SelectZeroStateResult(const PickerSearchResult& result) override;
void GetZeroStateRecentResults(PickerCategory category,
SearchResultsCallback callback) override;
void GetSuggestedZeroStateEditorResults(
SuggestedEditorResultsCallback callback) override;
void NotifyPseudoFocusChanged(views::View* view) override;

@@ -32,8 +32,9 @@ class ASH_EXPORT PickerViewDelegate {
virtual std::vector<PickerCategory> GetAvailableCategories() = 0;
// Returns whether we should show suggested results in zero state view.
virtual bool ShouldShowRecentResults() = 0;
// Returns categories for which we should show recent results in zero state
// view.
virtual std::vector<PickerCategory> GetRecentResultsCategories() = 0;
// Gets initially suggested results for category. Results will be returned via
// `callback`, which may be called multiples times to update the results.

@@ -115,7 +115,9 @@ class FakePickerViewDelegate : public PickerViewDelegate {
requested_case_transformation_category_ = category;
}
bool ShouldShowRecentResults() override { return true; }
std::vector<PickerCategory> GetRecentResultsCategories() override {
return {PickerCategory::kDriveFiles};
}
void GetResultsForCategory(PickerCategory category,
SearchResultsCallback callback) override {

@@ -32,7 +32,9 @@ class FakePickerViewDelegate : public PickerViewDelegate {
public:
// PickerViewDelegate:
std::vector<PickerCategory> GetAvailableCategories() override { return {}; }
bool ShouldShowRecentResults() override { return false; }
std::vector<PickerCategory> GetRecentResultsCategories() override {
return {};
}
void GetResultsForCategory(PickerCategory category,
SearchResultsCallback callback) override {}
void TransformSelectedText(PickerCategory category) override {}

@@ -48,54 +48,11 @@
#include "ui/views/view_utils.h"
namespace ash {
namespace {
constexpr base::TimeDelta kClipboardRecency = base::Seconds(60);
std::unique_ptr<PickerListItemView> CreateListItemViewForClipboardResult(
const PickerSearchResult::ClipboardData& data,
PickerListItemView::SelectItemCallback callback) {
auto item_view = std::make_unique<PickerListItemView>(std::move(callback));
item_view->SetLeadingIcon(ui::ImageModel::FromVectorIcon(
kClipboardIcon, cros_tokens::kCrosSysOnSurface));
item_view->SetSecondaryText(
l10n_util::GetStringUTF16(IDS_PICKER_FROM_CLIPBOARD_TEXT));
switch (data.display_format) {
case PickerSearchResult::ClipboardData::DisplayFormat::kFile:
case PickerSearchResult::ClipboardData::DisplayFormat::kText:
item_view->SetPrimaryText(data.display_text);
break;
case PickerSearchResult::ClipboardData::DisplayFormat::kImage:
if (!data.display_image.has_value()) {
return nullptr;
}
item_view->SetPrimaryImage(
std::make_unique<views::ImageView>(*data.display_image));
break;
case PickerSearchResult::ClipboardData::DisplayFormat::kHtml:
item_view->SetPrimaryText(
l10n_util::GetStringUTF16(IDS_PICKER_HTML_CONTENT));
break;
}
return item_view;
}
std::unique_ptr<PickerListItemView> CreateListItemViewForSearchResult(
const PickerSearchResult& result,
PickerListItemView::SelectItemCallback callback) {
// Only supports Clipboard results right now.
if (auto* data =
std::get_if<PickerSearchResult::ClipboardData>(&result.data())) {
return CreateListItemViewForClipboardResult(*data, std::move(callback));
}
return nullptr;
}
} // namespace
PickerZeroStateView::PickerZeroStateView(
PickerZeroStateViewDelegate* delegate,
base::span<const PickerCategory> available_categories,
bool show_recent_results,
base::span<const PickerCategory> recent_results_categories,
int picker_view_width,
PickerAssetFetcher* asset_fetcher)
: delegate_(delegate) {
@@ -105,12 +62,11 @@ PickerZeroStateView::PickerZeroStateView(
section_list_view_ = AddChildView(std::make_unique<PickerSectionListView>(
picker_view_width, asset_fetcher));
if (show_recent_results) {
clipboard_provider_ = std::make_unique<PickerClipboardProvider>();
clipboard_provider_->FetchResults(
for (PickerCategory category : recent_results_categories) {
delegate_->GetZeroStateRecentResults(
category,
base::BindRepeating(&PickerZeroStateView::OnFetchRecentResults,
weak_ptr_factory_.GetWeakPtr()),
u"", kClipboardRecency);
weak_ptr_factory_.GetWeakPtr()));
}
if (base::Contains(available_categories, PickerCategory::kEditorRewrite)) {
@@ -324,13 +280,9 @@ void PickerZeroStateView::OnFetchRecentResults(
GetSectionTitleForPickerSectionType(PickerSectionType::kRecentlyUsed));
}
for (const auto& result : results) {
if (std::unique_ptr<PickerListItemView> item_view =
CreateListItemViewForSearchResult(
result,
base::BindRepeating(&PickerZeroStateView::OnResultSelected,
weak_ptr_factory_.GetWeakPtr(), result))) {
recent_section_view_->AddListItem(std::move(item_view));
}
recent_section_view_->AddResult(
result, base::BindRepeating(&PickerZeroStateView::OnResultSelected,
weak_ptr_factory_.GetWeakPtr(), result));
}
SetPseudoFocusedView(section_list_view_->GetTopItem());
}

@@ -39,7 +39,7 @@ class ASH_EXPORT PickerZeroStateView : public PickerPageView {
explicit PickerZeroStateView(
PickerZeroStateViewDelegate* delegate,
base::span<const PickerCategory> available_categories,
bool show_suggested_results,
base::span<const PickerCategory> recent_results_categories,
int picker_view_width,
PickerAssetFetcher* asset_fetcher);
PickerZeroStateView(const PickerZeroStateView&) = delete;

@@ -22,10 +22,16 @@ class ASH_EXPORT PickerZeroStateViewDelegate {
using SuggestedEditorResultsCallback =
base::OnceCallback<void(std::vector<PickerSearchResult>)>;
using SearchResultsCallback =
base::RepeatingCallback<void(std::vector<PickerSearchResult>)>;
virtual void SelectZeroStateCategory(PickerCategory category) = 0;
virtual void SelectZeroStateResult(const PickerSearchResult& result) = 0;
virtual void GetZeroStateRecentResults(PickerCategory category,
SearchResultsCallback callback) = 0;
virtual void GetSuggestedZeroStateEditorResults(
SuggestedEditorResultsCallback callback) = 0;

@@ -34,6 +34,7 @@
namespace ash {
namespace {
using ::testing::_;
using ::testing::AllOf;
using ::testing::Contains;
using ::testing::ElementsAre;
@@ -79,6 +80,10 @@ class MockZeroStateViewDelegate : public PickerZeroStateViewDelegate {
GetSuggestedZeroStateEditorResults,
(SuggestedEditorResultsCallback),
(override));
MOCK_METHOD(void,
GetZeroStateRecentResults,
(PickerCategory, SearchResultsCallback),
(override));
MOCK_METHOD(void, NotifyPseudoFocusChanged, (views::View*), (override));
};
@@ -92,7 +97,7 @@ class PickerZeroStateViewTest : public views::ViewsTestBase {
TEST_F(PickerZeroStateViewTest, CreatesCategorySections) {
MockZeroStateViewDelegate mock_delegate;
PickerZeroStateView view(&mock_delegate, kAllCategories, true, kPickerWidth,
PickerZeroStateView view(&mock_delegate, kAllCategories, {}, kPickerWidth,
&asset_fetcher_);
EXPECT_THAT(view.section_views_for_testing(),
@@ -109,7 +114,7 @@ TEST_F(PickerZeroStateViewTest, LeftClickSelectsCategory) {
MockZeroStateViewDelegate mock_delegate;
auto* view = widget->SetContentsView(std::make_unique<PickerZeroStateView>(
&mock_delegate, std::vector<PickerCategory>{PickerCategory::kExpressions},
false, kPickerWidth, &asset_fetcher_));
std::vector<PickerCategory>{}, kPickerWidth, &asset_fetcher_));
widget->Show();
ASSERT_THAT(view->section_views_for_testing(),
Contains(Key(PickerCategoryType::kGeneral)));
@@ -129,43 +134,33 @@ TEST_F(PickerZeroStateViewTest, LeftClickSelectsCategory) {
LeftClickOn(*category_view);
}
TEST_F(PickerZeroStateViewTest, ShowsClipboardItems) {
base::UnguessableToken item_id;
testing::StrictMock<MockClipboardHistoryController> mock_clipboard;
EXPECT_CALL(mock_clipboard, GetHistoryValues)
.WillOnce(
[&item_id](
ClipboardHistoryController::GetHistoryValuesCallback callback) {
ClipboardHistoryItemBuilder builder;
ClipboardHistoryItem item =
builder.SetFormat(ui::ClipboardInternalFormat::kText)
.SetText("test")
.Build();
item_id = item.id();
std::move(callback).Run({std::move(item)});
});
TEST_F(PickerZeroStateViewTest, ShowsRecentItems) {
MockZeroStateViewDelegate mock_delegate;
EXPECT_CALL(mock_delegate,
GetZeroStateRecentResults(PickerCategory::kDriveFiles, _))
.WillOnce([](PickerCategory category,
MockZeroStateViewDelegate::SearchResultsCallback callback) {
std::move(callback).Run({PickerSearchResult::DriveFile(
/*title=*/u"test drive file",
/*url=*/GURL(),
/*icon=*/{})});
});
std::unique_ptr<views::Widget> widget = CreateTestWidget();
widget->SetFullscreen(true);
base::test::TestFuture<const PickerSearchResult&> future;
MockZeroStateViewDelegate mock_delegate;
auto* view = widget->SetContentsView(std::make_unique<PickerZeroStateView>(
&mock_delegate, kAllCategories, true, kPickerWidth, &asset_fetcher_));
&mock_delegate, kAllCategories,
std::vector<PickerCategory>{PickerCategory::kDriveFiles}, kPickerWidth,
&asset_fetcher_));
widget->Show();
EXPECT_CALL(
mock_delegate,
SelectZeroStateResult(Property(
"data", &ash::PickerSearchResult::data,
VariantWith<ash::PickerSearchResult::ClipboardData>(AllOf(
Field("item_id", &ash::PickerSearchResult::ClipboardData::item_id,
item_id),
Field("display_format",
&ash::PickerSearchResult::ClipboardData::display_format,
PickerSearchResult::ClipboardData::DisplayFormat::kText),
Field("display_text",
&ash::PickerSearchResult::ClipboardData::display_text,
u"test"))))))
EXPECT_CALL(mock_delegate,
SelectZeroStateResult(Property(
"data", &ash::PickerSearchResult::data,
VariantWith<ash::PickerSearchResult::DriveFileData>(Field(
"title", &ash::PickerSearchResult::DriveFileData::title,
u"test drive file")))))
.Times(1);
ASSERT_THAT(view->RecentSectionForTesting(), Not(IsNull()));
@@ -175,35 +170,6 @@ TEST_F(PickerZeroStateViewTest, ShowsClipboardItems) {
LeftClickOn(*item_view);
}
TEST_F(PickerZeroStateViewTest, HidesRecentSectionWhenNoItemsToDisplay) {
testing::StrictMock<MockClipboardHistoryController> mock_clipboard;
EXPECT_CALL(mock_clipboard, GetHistoryValues)
.WillOnce(
[](ClipboardHistoryController::GetHistoryValuesCallback callback) {
std::move(callback).Run({});
});
std::unique_ptr<views::Widget> widget = CreateTestWidget();
widget->SetFullscreen(true);
MockZeroStateViewDelegate mock_delegate;
auto* view = widget->SetContentsView(std::make_unique<PickerZeroStateView>(
&mock_delegate, kAllCategories, true, kPickerWidth, &asset_fetcher_));
widget->Show();
EXPECT_THAT(view->RecentSectionForTesting(), IsNull());
}
TEST_F(PickerZeroStateViewTest, DoesntShowClipboardItems) {
std::unique_ptr<views::Widget> widget = CreateTestWidget();
widget->SetFullscreen(true);
MockZeroStateViewDelegate mock_delegate;
auto* view = widget->SetContentsView(std::make_unique<PickerZeroStateView>(
&mock_delegate, kAllCategories, false, kPickerWidth, &asset_fetcher_));
widget->Show();
EXPECT_THAT(view->RecentSectionForTesting(), IsNull());
}
TEST_F(PickerZeroStateViewTest,
DoesntShowEditorRewriteCategoryForEmptySuggestions) {
MockZeroStateViewDelegate mock_delegate;
@@ -211,7 +177,7 @@ TEST_F(PickerZeroStateViewTest,
.WillOnce([](MockZeroStateViewDelegate::SuggestedEditorResultsCallback
callback) { std::move(callback).Run({}); });
PickerZeroStateView view(&mock_delegate, {{PickerCategory::kEditorRewrite}},
false, kPickerWidth, &asset_fetcher_);
{}, kPickerWidth, &asset_fetcher_);
EXPECT_THAT(
view.section_views_for_testing(),
@@ -239,7 +205,7 @@ TEST_F(PickerZeroStateViewTest, ShowsEditorSuggestionsAsItems) {
});
});
PickerZeroStateView view(&mock_delegate, {{PickerCategory::kEditorRewrite}},
false, kPickerWidth, &asset_fetcher_);
{}, kPickerWidth, &asset_fetcher_);
EXPECT_THAT(
view.section_views_for_testing(),