[go: nahoru, domu]

blob: 767034e1c9d2c0fb80188834218e504f0fc8dbd1 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/app_list/views/search_result_image_list_view.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ash/app_list/views/search_result_image_view.h"
#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/public/cpp/app_list/app_list_notifier.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/typography.h"
#include "base/i18n/time_formatting.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/layer.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/flex_layout_view.h"
namespace ash {
namespace {
// Border spacing for title_label_
constexpr int kPreferredTitleHorizontalMargins = 16;
constexpr int kPreferredTitleTopMargins = 12;
constexpr int kPreferredTitleBottomMargins = 4;
// Layout constants for `image_view_container_`.
constexpr int kImageContainerHorizontalMargins =
kPreferredTitleHorizontalMargins;
constexpr int kImageContainerVerticalMargins = 8;
constexpr int kSpaceBetweenImages = 8;
// Layout constants for `image_info_container_`.
constexpr auto kInfoContainerMargins = gfx::Insets::TLBR(0, 8, 0, 0);
// The number of labels shown in `image_info_container_`, which should also be
// the size of `metadata_content_labels_`.
constexpr size_t kNumOfContentLabels = 3;
// Returns a displayable time for the last modified date in
// `image_info_container_`.
std::u16string GetFormattedTime(base::Time time) {
std::u16string date_time_of_day = base::TimeFormatTimeOfDay(time);
std::u16string relative_date = ui::TimeFormat::RelativeDate(time, nullptr);
std::u16string formatted_time;
if (!relative_date.empty()) {
relative_date = base::ToLowerASCII(relative_date);
formatted_time = l10n_util::GetStringFUTF16(
IDS_ASH_SEARCH_RESULT_IMAGE_LAST_MODIFIED_RELATIVE_DATE_AND_TIME,
relative_date, date_time_of_day);
} else {
formatted_time = l10n_util::GetStringFUTF16(
IDS_ASH_SEARCH_RESULT_IMAGE_LAST_MODIFIED_DATE_AND_TIME,
base::TimeFormatShortDate(time), date_time_of_day);
}
return l10n_util::GetStringFUTF16(
IDS_ASH_SEARCH_RESULT_IMAGE_LAST_MODIFIED_STRING, formatted_time);
}
} // namespace
using views::LayoutAlignment;
SearchResultImageListView::SearchResultImageListView(
AppListViewDelegate* view_delegate)
: SearchResultContainerView(view_delegate) {
auto* layout = SetLayoutManager(std::make_unique<views::FlexLayout>());
layout->SetOrientation(views::LayoutOrientation::kVertical);
title_label_ = AddChildView(std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_IMAGES)));
title_label_->SetBackgroundColor(SK_ColorTRANSPARENT);
title_label_->SetAutoColorReadabilityEnabled(false);
title_label_->SetEnabledColorId(kColorAshTextColorSecondary);
title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label_->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
kPreferredTitleTopMargins, kPreferredTitleHorizontalMargins,
kPreferredTitleBottomMargins, kPreferredTitleHorizontalMargins)));
title_label_->SetPaintToLayer();
title_label_->layer()->SetFillsBoundsOpaquely(false);
SetAccessibleRole(ax::mojom::Role::kListBox);
SetAccessibleName(l10n_util::GetStringFUTF16(
IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_ACCESSIBLE_NAME,
title_label_->GetText()));
image_view_container_ =
AddChildView(std::make_unique<views::FlexLayoutView>());
image_view_container_->SetPaintToLayer();
image_view_container_->layer()->SetFillsBoundsOpaquely(false);
image_view_container_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kPreferred));
// `image_view_container_` flex layout settings.
image_view_container_->SetInteriorMargin(gfx::Insets::TLBR(
kImageContainerVerticalMargins, kImageContainerHorizontalMargins,
kImageContainerVerticalMargins, kImageContainerHorizontalMargins));
image_view_container_->SetCollapseMargins(true);
image_view_container_->SetDefault(views::kMarginsKey,
gfx::Insets::VH(0, kSpaceBetweenImages));
for (size_t i = 0;
i < SharedAppListConfig::instance().image_search_max_results(); ++i) {
auto* image_view = image_view_container_->AddChildView(
std::make_unique<SearchResultImageView>(/*index=*/1, this, &delegate_));
image_view->SetPaintToLayer();
image_view->layer()->SetFillsBoundsOpaquely(false);
image_views_.push_back(image_view);
}
image_info_container_ = image_view_container_->AddChildView(
std::make_unique<views::FlexLayoutView>());
image_info_container_->SetBorder(
views::CreateEmptyBorder(kInfoContainerMargins));
// Initialize the vertical container in `image_info_container_`.
image_info_container_->SetOrientation(views::LayoutOrientation::kVertical);
image_info_container_->SetCollapseMargins(true);
image_info_container_->SetMainAxisAlignment(LayoutAlignment::kCenter);
image_info_container_->SetCrossAxisAlignment(LayoutAlignment::kStart);
// Set the flex to restrict the sizes of the child labels.
image_info_container_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kScaleToMaximum)
.WithWeight(1));
// Initialize the labels in the info container.
for (size_t i = 0; i < kNumOfContentLabels; ++i) {
auto content_label = std::make_unique<views::Label>();
content_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
if (i == 0) {
// Make the image result file name, which is the first metadata label,
// more prominent.
content_label->SetProperty(views::kMarginsKey,
gfx::Insets::TLBR(8, 0, 12, 0));
content_label->SetMultiLine(true);
content_label->SetAllowCharacterBreak(true);
TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosButton1,
*content_label);
content_label->SetEnabledColorId(cros_tokens::kColorPrimary);
} else {
content_label->SetElideBehavior(gfx::ElideBehavior::ELIDE_MIDDLE);
TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosBody2,
*content_label);
content_label->SetEnabledColorId(cros_tokens::kTextColorSecondary);
}
metadata_content_labels_.push_back(content_label.get());
image_info_container_->AddChildView(std::move(content_label));
}
}
SearchResultImageListView::~SearchResultImageListView() = default;
void SearchResultImageListView::SearchResultActivated(
SearchResultImageView* view,
int event_flags,
bool by_button_press) {
if (!view_delegate() || !view || !view->result()) {
return;
}
view_delegate()->OpenSearchResult(
view->result()->id(), event_flags,
AppListLaunchedFrom::kLaunchedFromSearchBox,
AppListLaunchType::kSearchResult, -1 /* suggestion_index */,
!by_button_press && view->is_default_result() /* launch_as_default */);
}
SearchResultImageView* SearchResultImageListView::GetResultViewAt(
size_t index) {
DCHECK(index < image_views_.size());
return image_views_[index];
}
std::vector<raw_ptr<SearchResultImageView, VectorExperimental>>
SearchResultImageListView::GetSearchResultImageViews() {
return image_views_;
}
void SearchResultImageListView::ConfigureLayoutForAvailableWidth(int width) {
const int image_count = image_views_.size();
const int margin_space = kImageContainerHorizontalMargins * 2 +
kSpaceBetweenImages * (image_count - 1);
const int image_width = std::max(0, (width - margin_space) / image_count);
for (ash::SearchResultImageView* image_view : image_views_) {
image_view->ConfigureLayoutForAvailableWidth(image_width);
}
}
void SearchResultImageListView::OnImageMetadataLoaded(
ash::FileMetadata metadata) {
if (num_results() != 1) {
return;
}
// Check that there are 3 labels in `metadata_content_labels_`.
CHECK_EQ(metadata_content_labels_.size(), kNumOfContentLabels);
metadata_content_labels_[0]->SetText(
base::UTF8ToUTF16(metadata.file_name.value()));
metadata_content_labels_[1]->SetText(
base::UTF8ToUTF16(metadata.displayable_folder_path.value()));
metadata_content_labels_[2]->SetText(
GetFormattedTime(metadata.file_info.last_modified));
}
int SearchResultImageListView::DoUpdate() {
std::vector<SearchResult*> display_results =
SearchModel::FilterSearchResultsByFunction(
results(), base::BindRepeating([](const SearchResult& result) {
return result.display_type() == SearchResultDisplayType::kImage;
}),
SharedAppListConfig::instance().image_search_max_results());
const size_t num_results = display_results.size();
for (size_t i = 0; i < image_views_.size(); ++i) {
SearchResultImageView* result_view = GetResultViewAt(i);
if (i < num_results) {
result_view->SetResult(display_results[i]);
result_view->SizeToPreferredSize();
} else {
result_view->SetResult(nullptr);
}
}
if (num_results == 1) {
CHECK(display_results[0]->file_metadata_loader());
display_results[0]->file_metadata_loader()->RequestFileInfo(
base::BindRepeating(&SearchResultImageListView::OnImageMetadataLoaded,
weak_ptr_factory_.GetWeakPtr()));
}
auto* notifier = view_delegate()->GetNotifier();
if (notifier) {
std::vector<AppListNotifier::Result> notifier_results;
for (const auto* result : display_results) {
notifier_results.emplace_back(result->id(), result->metrics_type());
}
notifier->NotifyResultsUpdated(SearchResultDisplayType::kImage,
notifier_results);
}
NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false);
return num_results;
}
void SearchResultImageListView::UpdateResultsVisibility(bool force_hide) {
SetVisible(!force_hide);
for (size_t i = 0; i < image_views_.size(); ++i) {
SearchResultImageView* result_view = GetResultViewAt(i);
result_view->SetVisible(i < num_results() && !force_hide);
}
image_info_container_->SetVisible(num_results() == 1 && !force_hide);
}
views::View* SearchResultImageListView::GetTitleLabel() {
return title_label_.get();
}
std::vector<views::View*> SearchResultImageListView::GetViewsToAnimate() {
return {image_view_container_};
}
BEGIN_METADATA(SearchResultImageListView, SearchResultContainerView)
END_METADATA
} // namespace ash