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:

committed by
Chromium LUCI CQ

parent
02ae13d457
commit
32b1e2da9d
ash/picker
@@ -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(),
|
||||
|
Reference in New Issue
Block a user