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; 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 { bool PickerModel::HasSelectedText() const {
return !selected_text_.empty(); return !selected_text_.empty();
} }

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

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

@@ -75,7 +75,7 @@ class ASH_EXPORT PickerController
// PickerViewDelegate: // PickerViewDelegate:
std::vector<PickerCategory> GetAvailableCategories() override; std::vector<PickerCategory> GetAvailableCategories() override;
bool ShouldShowRecentResults() override; std::vector<PickerCategory> GetRecentResultsCategories() override;
void GetResultsForCategory(PickerCategory category, void GetResultsForCategory(PickerCategory category,
SearchResultsCallback callback) override; SearchResultsCallback callback) override;
void TransformSelectedText(PickerCategory category) 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 } // namespace
PickerView::PickerView(PickerViewDelegate* delegate, PickerView::PickerView(PickerViewDelegate* delegate,
@@ -209,6 +222,13 @@ void PickerView::SelectZeroStateResult(const PickerSearchResult& result) {
SelectSearchResult(result); SelectSearchResult(result);
} }
void PickerView::GetZeroStateRecentResults(PickerCategory category,
SearchResultsCallback callback) {
delegate_->GetResultsForCategory(
category,
base::BindRepeating(&GetMostRecentResult).Then(std::move(callback)));
}
void PickerView::GetSuggestedZeroStateEditorResults( void PickerView::GetSuggestedZeroStateEditorResults(
SuggestedEditorResultsCallback callback) { SuggestedEditorResultsCallback callback) {
delegate_->GetSuggestedEditorResults(std::move(callback)); delegate_->GetSuggestedEditorResults(std::move(callback));
@@ -397,7 +417,7 @@ void PickerView::AddContentsViewWithSeparator(PickerLayoutType layout_type) {
zero_state_view_ = zero_state_view_ =
contents_view_->AddPage(std::make_unique<PickerZeroStateView>( contents_view_->AddPage(std::make_unique<PickerZeroStateView>(
this, delegate_->GetAvailableCategories(), this, delegate_->GetAvailableCategories(),
delegate_->ShouldShowRecentResults(), kMaxSize.width(), delegate_->GetRecentResultsCategories(), kMaxSize.width(),
delegate_->GetAssetFetcher())); delegate_->GetAssetFetcher()));
category_view_ = contents_view_->AddPage(std::make_unique<PickerCategoryView>( category_view_ = contents_view_->AddPage(std::make_unique<PickerCategoryView>(

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

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

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

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

@@ -48,54 +48,11 @@
#include "ui/views/view_utils.h" #include "ui/views/view_utils.h"
namespace ash { 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( PickerZeroStateView::PickerZeroStateView(
PickerZeroStateViewDelegate* delegate, PickerZeroStateViewDelegate* delegate,
base::span<const PickerCategory> available_categories, base::span<const PickerCategory> available_categories,
bool show_recent_results, base::span<const PickerCategory> recent_results_categories,
int picker_view_width, int picker_view_width,
PickerAssetFetcher* asset_fetcher) PickerAssetFetcher* asset_fetcher)
: delegate_(delegate) { : delegate_(delegate) {
@@ -105,12 +62,11 @@ PickerZeroStateView::PickerZeroStateView(
section_list_view_ = AddChildView(std::make_unique<PickerSectionListView>( section_list_view_ = AddChildView(std::make_unique<PickerSectionListView>(
picker_view_width, asset_fetcher)); picker_view_width, asset_fetcher));
if (show_recent_results) { for (PickerCategory category : recent_results_categories) {
clipboard_provider_ = std::make_unique<PickerClipboardProvider>(); delegate_->GetZeroStateRecentResults(
clipboard_provider_->FetchResults( category,
base::BindRepeating(&PickerZeroStateView::OnFetchRecentResults, base::BindRepeating(&PickerZeroStateView::OnFetchRecentResults,
weak_ptr_factory_.GetWeakPtr()), weak_ptr_factory_.GetWeakPtr()));
u"", kClipboardRecency);
} }
if (base::Contains(available_categories, PickerCategory::kEditorRewrite)) { if (base::Contains(available_categories, PickerCategory::kEditorRewrite)) {
@@ -324,13 +280,9 @@ void PickerZeroStateView::OnFetchRecentResults(
GetSectionTitleForPickerSectionType(PickerSectionType::kRecentlyUsed)); GetSectionTitleForPickerSectionType(PickerSectionType::kRecentlyUsed));
} }
for (const auto& result : results) { for (const auto& result : results) {
if (std::unique_ptr<PickerListItemView> item_view = recent_section_view_->AddResult(
CreateListItemViewForSearchResult( result, base::BindRepeating(&PickerZeroStateView::OnResultSelected,
result, weak_ptr_factory_.GetWeakPtr(), result));
base::BindRepeating(&PickerZeroStateView::OnResultSelected,
weak_ptr_factory_.GetWeakPtr(), result))) {
recent_section_view_->AddListItem(std::move(item_view));
}
} }
SetPseudoFocusedView(section_list_view_->GetTopItem()); SetPseudoFocusedView(section_list_view_->GetTopItem());
} }

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

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

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