123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/system/holding_space/holding_space_item_view.h"
- #include "ash/public/cpp/holding_space/holding_space_client.h"
- #include "ash/public/cpp/holding_space/holding_space_constants.h"
- #include "ash/public/cpp/holding_space/holding_space_controller.h"
- #include "ash/public/cpp/holding_space/holding_space_item.h"
- #include "ash/public/cpp/holding_space/holding_space_progress.h"
- #include "ash/public/cpp/holding_space/holding_space_util.h"
- #include "ash/public/cpp/shelf_config.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/style/ash_color_provider.h"
- #include "ash/style/dark_light_mode_controller_impl.h"
- #include "ash/system/holding_space/holding_space_util.h"
- #include "ash/system/holding_space/holding_space_view_delegate.h"
- #include "base/bind.h"
- #include "ui/base/class_property.h"
- #include "ui/base/dragdrop/drag_drop_types.h"
- #include "ui/base/metadata/metadata_impl_macros.h"
- #include "ui/compositor/layer.h"
- #include "ui/gfx/canvas.h"
- #include "ui/gfx/color_palette.h"
- #include "ui/gfx/paint_vector_icon.h"
- #include "ui/views/accessibility/view_accessibility.h"
- #include "ui/views/background.h"
- #include "ui/views/controls/button/image_button.h"
- #include "ui/views/controls/focus_ring.h"
- #include "ui/views/controls/image_view.h"
- #include "ui/views/layout/fill_layout.h"
- #include "ui/views/painter.h"
- #include "ui/views/vector_icons.h"
- #include "ui/views/widget/widget.h"
- namespace ash {
- namespace {
- // A UI class property used to identify if a view is an instance of
- // `HoldingSpaceItemView`. Class name is not an adequate identifier as it may be
- // overridden by subclasses.
- DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsHoldingSpaceItemViewProperty, false)
- // Appearance.
- constexpr size_t kCheckmarkBackgroundSize = 18;
- // Helpers ---------------------------------------------------------------------
- // Schedules repaint of `layer`.
- void InvalidateLayer(ui::Layer* layer) {
- layer->SchedulePaint(gfx::Rect(layer->size()));
- }
- // CallbackPainter -------------------------------------------------------------
- // A painter which delegates painting to a callback.
- class CallbackPainter : public views::Painter {
- public:
- using Callback = base::RepeatingCallback<void(gfx::Canvas*, gfx::Size)>;
- CallbackPainter(const CallbackPainter&) = delete;
- CallbackPainter& operator=(const CallbackPainter&) = delete;
- ~CallbackPainter() override = default;
- // Creates a painted layer which delegates painting to `callback`.
- static std::unique_ptr<ui::LayerOwner> CreatePaintedLayer(Callback callback) {
- auto owner = views::Painter::CreatePaintedLayer(
- base::WrapUnique(new CallbackPainter(callback)));
- owner->layer()->SetFillsBoundsOpaquely(false);
- return owner;
- }
- private:
- explicit CallbackPainter(Callback callback) : callback_(callback) {}
- // views::Painter:
- gfx::Size GetMinimumSize() const override { return gfx::Size(); }
- void Paint(gfx::Canvas* canvas, const gfx::Size& size) override {
- callback_.Run(canvas, size);
- }
- Callback callback_;
- };
- // MinimumSizableView ---------------------------------------------------------
- // A view which respects a minimum size restriction.
- class MinimumSizableView : public views::View {
- public:
- explicit MinimumSizableView(const gfx::Size& min_size)
- : min_size_(min_size) {}
- MinimumSizableView(const MinimumSizableView&) = delete;
- MinimumSizableView& operator=(const MinimumSizableView&) = delete;
- ~MinimumSizableView() override = default;
- private:
- // views::View:
- gfx::Size CalculatePreferredSize() const override {
- gfx::Size preferred_size(views::View::CalculatePreferredSize());
- preferred_size.SetToMax(min_size_);
- return preferred_size;
- }
- int GetHeightForWidth(int width) const override {
- return std::max(views::View::GetHeightForWidth(width), min_size_.height());
- }
- const gfx::Size min_size_;
- };
- } // namespace
- // HoldingSpaceItemView --------------------------------------------------------
- HoldingSpaceItemView::HoldingSpaceItemView(HoldingSpaceViewDelegate* delegate,
- const HoldingSpaceItem* item)
- : delegate_(delegate), item_(item), item_id_(item->id()) {
- // Subscribe to be notified of `item_` deletion. Note that it is safe to use a
- // raw pointer here since `this` owns the callback.
- item_deletion_subscription_ = item_->AddDeletionCallback(base::BindRepeating(
- [](HoldingSpaceItemView* view) { view->item_ = nullptr; },
- base::Unretained(this)));
- model_observer_.Observe(HoldingSpaceController::Get()->model());
- SetProperty(kIsHoldingSpaceItemViewProperty, true);
- set_context_menu_controller(delegate_);
- set_drag_controller(delegate_);
- SetNotifyEnterExitOnChild(true);
- // Accessibility.
- GetViewAccessibility().OverrideRole(ax::mojom::Role::kListItem);
- GetViewAccessibility().OverrideName(item->GetAccessibleName());
- // When the description is not specified, tooltip text will be used.
- // That text is redundant to the name, but different enough that it is
- // still exposed to assistive technologies which may then present both.
- // To avoid that redundant presentation, set the description explicitly
- // to the empty string. See crrev.com/c/3218112.
- GetViewAccessibility().OverrideDescription(
- std::u16string(), ax::mojom::DescriptionFrom::kAttributeExplicitlyEmpty);
- // Layer.
- SetPaintToLayer();
- layer()->SetFillsBoundsOpaquely(false);
- // Focus.
- SetFocusBehavior(FocusBehavior::ALWAYS);
- set_suppress_default_focus_handling();
- focused_layer_owner_ =
- CallbackPainter::CreatePaintedLayer(base::BindRepeating(
- &HoldingSpaceItemView::OnPaintFocus, base::Unretained(this)));
- layer()->Add(focused_layer_owner_->layer());
- // Selection.
- selected_layer_owner_ =
- CallbackPainter::CreatePaintedLayer(base::BindRepeating(
- &HoldingSpaceItemView::OnPaintSelect, base::Unretained(this)));
- layer()->Add(selected_layer_owner_->layer());
- // This view's `selected_` state is represented differently depending on
- // `delegate_`'s selection UI. Register to be notified of changes.
- selection_ui_changed_subscription_ =
- delegate_->AddSelectionUiChangedCallback(base::BindRepeating(
- &HoldingSpaceItemView::OnSelectionUiChanged, base::Unretained(this)));
- delegate_->OnHoldingSpaceItemViewCreated(this);
- }
- HoldingSpaceItemView::~HoldingSpaceItemView() {
- if (delegate_)
- delegate_->OnHoldingSpaceItemViewDestroying(this);
- }
- // static
- HoldingSpaceItemView* HoldingSpaceItemView::Cast(views::View* view) {
- return const_cast<HoldingSpaceItemView*>(
- Cast(const_cast<const views::View*>(view)));
- }
- // static
- const HoldingSpaceItemView* HoldingSpaceItemView::Cast(
- const views::View* view) {
- DCHECK(HoldingSpaceItemView::IsInstance(view));
- return static_cast<const HoldingSpaceItemView*>(view);
- }
- // static
- bool HoldingSpaceItemView::IsInstance(const views::View* view) {
- return view->GetProperty(kIsHoldingSpaceItemViewProperty);
- }
- void HoldingSpaceItemView::Reset() {
- delegate_ = nullptr;
- }
- bool HoldingSpaceItemView::HandleAccessibleAction(
- const ui::AXActionData& action_data) {
- return (delegate_ && delegate_->OnHoldingSpaceItemViewAccessibleAction(
- this, action_data)) ||
- views::View::HandleAccessibleAction(action_data);
- }
- void HoldingSpaceItemView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
- gfx::Rect bounds = GetLocalBounds();
- // Selection ring.
- selected_layer_owner_->layer()->SetBounds(bounds);
- InvalidateLayer(selected_layer_owner_->layer());
- // Focus ring.
- // NOTE: The focus ring is painted just outside the bounds for this view.
- bounds.Inset(gfx::Insets(kHoldingSpaceFocusInsets));
- focused_layer_owner_->layer()->SetBounds(bounds);
- InvalidateLayer(focused_layer_owner_->layer());
- }
- void HoldingSpaceItemView::OnFocus() {
- InvalidateLayer(focused_layer_owner_->layer());
- }
- void HoldingSpaceItemView::OnBlur() {
- InvalidateLayer(focused_layer_owner_->layer());
- }
- void HoldingSpaceItemView::OnGestureEvent(ui::GestureEvent* event) {
- if (delegate_ && delegate_->OnHoldingSpaceItemViewGestureEvent(this, *event))
- event->SetHandled();
- }
- bool HoldingSpaceItemView::OnKeyPressed(const ui::KeyEvent& event) {
- return delegate_ && delegate_->OnHoldingSpaceItemViewKeyPressed(this, event);
- }
- void HoldingSpaceItemView::OnMouseEvent(ui::MouseEvent* event) {
- switch (event->type()) {
- case ui::ET_MOUSE_ENTERED:
- case ui::ET_MOUSE_EXITED:
- UpdatePrimaryAction();
- break;
- default:
- break;
- }
- views::View::OnMouseEvent(event);
- }
- bool HoldingSpaceItemView::OnMousePressed(const ui::MouseEvent& event) {
- return delegate_ &&
- delegate_->OnHoldingSpaceItemViewMousePressed(this, event);
- }
- void HoldingSpaceItemView::OnMouseReleased(const ui::MouseEvent& event) {
- if (delegate_)
- delegate_->OnHoldingSpaceItemViewMouseReleased(this, event);
- }
- void HoldingSpaceItemView::OnThemeChanged() {
- views::View::OnThemeChanged();
- AshColorProvider* const ash_color_provider = AshColorProvider::Get();
- // Background.
- SetBackground(views::CreateRoundedRectBackground(
- ash_color_provider->GetControlsLayerColor(
- AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive),
- kHoldingSpaceCornerRadius));
- // Checkmark.
- checkmark_->SetBackground(holding_space_util::CreateCircleBackground(
- ash_color_provider->GetControlsLayerColor(
- AshColorProvider::ControlsLayerType::kFocusRingColor),
- kCheckmarkBackgroundSize));
- checkmark_->SetImage(gfx::CreateVectorIcon(
- kCheckIcon, kHoldingSpaceIconSize,
- DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
- ? gfx::kGoogleGrey900
- : SK_ColorWHITE));
- // Focused/selected layers.
- InvalidateLayer(focused_layer_owner_->layer());
- InvalidateLayer(selected_layer_owner_->layer());
- if (!primary_action_container_)
- return;
- // Cancel.
- const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kButtonIconColor);
- primary_action_cancel_->SetImage(
- views::Button::STATE_NORMAL,
- gfx::CreateVectorIcon(kCancelIcon, kHoldingSpaceIconSize, icon_color));
- // Pin.
- const gfx::ImageSkia unpinned_icon = gfx::CreateVectorIcon(
- views::kUnpinIcon, kHoldingSpaceIconSize, icon_color);
- const gfx::ImageSkia pinned_icon =
- gfx::CreateVectorIcon(views::kPinIcon, kHoldingSpaceIconSize, icon_color);
- primary_action_pin_->SetImage(views::Button::STATE_NORMAL, unpinned_icon);
- primary_action_pin_->SetToggledImage(views::Button::STATE_NORMAL,
- &pinned_icon);
- }
- void HoldingSpaceItemView::OnHoldingSpaceItemUpdated(
- const HoldingSpaceItem* item,
- uint32_t updated_fields) {
- if (item_ != item)
- return;
- // Accessibility.
- if (updated_fields & UpdatedField::kAccessibleName) {
- GetViewAccessibility().OverrideName(item_->GetAccessibleName());
- NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
- }
- // Primary action.
- UpdatePrimaryAction();
- }
- void HoldingSpaceItemView::StartDrag(const ui::LocatedEvent& event,
- ui::mojom::DragEventSource source) {
- int drag_operations = GetDragOperations(event.location());
- if (drag_operations == ui::DragDropTypes::DRAG_NONE)
- return;
- views::Widget* widget = GetWidget();
- DCHECK(widget);
- if (widget->dragged_view())
- return;
- auto data = std::make_unique<ui::OSExchangeData>();
- WriteDragData(event.location(), data.get());
- gfx::Point widget_location(event.location());
- views::View::ConvertPointToWidget(this, &widget_location);
- widget->RunShellDrag(this, std::move(data), widget_location, drag_operations,
- source);
- }
- void HoldingSpaceItemView::SetSelected(bool selected) {
- if (selected_ == selected)
- return;
- selected_ = selected;
- InvalidateLayer(selected_layer_owner_->layer());
- if (delegate_)
- delegate_->OnHoldingSpaceItemViewSelectedChanged(this);
- OnSelectionUiChanged();
- }
- views::Builder<views::ImageView>
- HoldingSpaceItemView::CreateCheckmarkBuilder() {
- DCHECK(!checkmark_);
- auto checkmark = views::Builder<views::ImageView>();
- checkmark.CopyAddressTo(&checkmark_)
- .SetID(kHoldingSpaceItemCheckmarkId)
- .SetVisible(selected());
- return checkmark;
- }
- views::Builder<views::View> HoldingSpaceItemView::CreatePrimaryActionBuilder(
- const gfx::Size& min_size) {
- DCHECK(!primary_action_container_);
- DCHECK(!primary_action_cancel_);
- DCHECK(!primary_action_pin_);
- using HorizontalAlignment = views::ImageButton::HorizontalAlignment;
- using VerticalAlignment = views::ImageButton::VerticalAlignment;
- gfx::Size preferred_size(kHoldingSpaceIconSize, kHoldingSpaceIconSize);
- preferred_size.SetToMax(min_size);
- auto primary_action = views::Builder<views::View>();
- primary_action.CopyAddressTo(&primary_action_container_)
- .SetID(kHoldingSpaceItemPrimaryActionContainerId)
- .SetUseDefaultFillLayout(true)
- .SetVisible(false)
- .AddChild(
- views::Builder<views::ImageButton>()
- .CopyAddressTo(&primary_action_cancel_)
- .SetID(kHoldingSpaceItemCancelButtonId)
- .SetCallback(base::BindRepeating(
- &HoldingSpaceItemView::OnPrimaryActionPressed,
- base::Unretained(this)))
- .SetFocusBehavior(views::View::FocusBehavior::NEVER)
- .SetImageHorizontalAlignment(HorizontalAlignment::ALIGN_CENTER)
- .SetImageVerticalAlignment(VerticalAlignment::ALIGN_MIDDLE)
- .SetPreferredSize(preferred_size)
- .SetVisible(false))
- .AddChild(
- views::Builder<views::ToggleImageButton>()
- .CopyAddressTo(&primary_action_pin_)
- .SetID(kHoldingSpaceItemPinButtonId)
- .SetCallback(base::BindRepeating(
- &HoldingSpaceItemView::OnPrimaryActionPressed,
- base::Unretained(this)))
- .SetFocusBehavior(views::View::FocusBehavior::NEVER)
- .SetImageHorizontalAlignment(HorizontalAlignment::ALIGN_CENTER)
- .SetImageVerticalAlignment(VerticalAlignment::ALIGN_MIDDLE)
- .SetPreferredSize(preferred_size)
- .SetVisible(false));
- return primary_action;
- }
- void HoldingSpaceItemView::OnSelectionUiChanged() {
- const bool multiselect =
- delegate_ && delegate_->selection_ui() ==
- HoldingSpaceViewDelegate::SelectionUi::kMultiSelect;
- checkmark_->SetVisible(selected() && multiselect);
- }
- void HoldingSpaceItemView::OnPaintFocus(gfx::Canvas* canvas, gfx::Size size) {
- if (!HasFocus())
- return;
- cc::PaintFlags flags;
- flags.setAntiAlias(true);
- flags.setColor(AshColorProvider::Get()->GetControlsLayerColor(
- AshColorProvider::ControlsLayerType::kFocusRingColor));
- flags.setStrokeWidth(views::FocusRing::kDefaultHaloThickness);
- flags.setStyle(cc::PaintFlags::kStroke_Style);
- gfx::Rect bounds = gfx::Rect(size);
- bounds.Inset(gfx::Insets(flags.getStrokeWidth() / 2));
- canvas->DrawRoundRect(bounds, kHoldingSpaceFocusCornerRadius, flags);
- }
- void HoldingSpaceItemView::OnPaintSelect(gfx::Canvas* canvas, gfx::Size size) {
- if (!selected_)
- return;
- const SkColor color =
- SkColorSetA(AshColorProvider::Get()->GetControlsLayerColor(
- AshColorProvider::ControlsLayerType::kFocusRingColor),
- kHoldingSpaceSelectedOverlayOpacity * 0xFF);
- cc::PaintFlags flags;
- flags.setAntiAlias(true);
- flags.setColor(color);
- canvas->DrawRoundRect(gfx::Rect(size), kHoldingSpaceCornerRadius, flags);
- }
- void HoldingSpaceItemView::OnPrimaryActionPressed() {
- // If the associated `item()` has been deleted then `this` is in the process
- // of being destroyed and no action needs to be taken.
- if (!item())
- return;
- DCHECK_NE(primary_action_cancel_->GetVisible(),
- primary_action_pin_->GetVisible());
- if (delegate())
- delegate()->OnHoldingSpaceItemViewPrimaryActionPressed(this);
- // Cancel.
- if (primary_action_cancel_->GetVisible()) {
- if (!holding_space_util::ExecuteInProgressCommand(
- item(), HoldingSpaceCommandId::kCancelItem)) {
- NOTREACHED();
- }
- return;
- }
- // Pin.
- const bool is_item_pinned =
- HoldingSpaceController::Get()->model()->ContainsItem(
- HoldingSpaceItem::Type::kPinnedFile, item()->file_path());
- // Unpinning `item()` may result in the destruction of this view.
- auto weak_ptr = weak_factory_.GetWeakPtr();
- if (is_item_pinned)
- HoldingSpaceController::Get()->client()->UnpinItems({item()});
- else
- HoldingSpaceController::Get()->client()->PinItems({item()});
- if (weak_ptr)
- UpdatePrimaryAction();
- }
- void HoldingSpaceItemView::UpdatePrimaryAction() {
- // If the associated `item()` has been deleted then `this` is in the process
- // of being destroyed and no action needs to be taken.
- if (!item())
- return;
- if (!IsMouseHovered()) {
- primary_action_container_->SetVisible(false);
- OnPrimaryActionVisibilityChanged(false);
- return;
- }
- // Cancel.
- // NOTE: Only in-progress items currently support cancellation.
- const bool is_item_in_progress = !item()->progress().IsComplete();
- primary_action_cancel_->SetVisible(
- is_item_in_progress && holding_space_util::SupportsInProgressCommand(
- item(), HoldingSpaceCommandId::kCancelItem));
- // Pin.
- const bool is_item_pinned =
- HoldingSpaceController::Get()->model()->ContainsItem(
- HoldingSpaceItem::Type::kPinnedFile, item()->file_path());
- primary_action_pin_->SetToggled(!is_item_pinned);
- primary_action_pin_->SetVisible(!is_item_in_progress);
- // Container.
- primary_action_container_->SetVisible(primary_action_cancel_->GetVisible() ||
- primary_action_pin_->GetVisible());
- OnPrimaryActionVisibilityChanged(primary_action_container_->GetVisible());
- }
- BEGIN_METADATA(HoldingSpaceItemView, views::View)
- END_METADATA
- } // namespace ash
|