blob: 077ea4008f57601f9244d6eb59161f897cdb38be [file] [log] [blame]
// Copyright (c) 2012 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.
#ifndef ASH_APP_LIST_VIEWS_APP_LIST_ITEM_VIEW_H_
#define ASH_APP_LIST_VIEWS_APP_LIST_ITEM_VIEW_H_
#include <memory>
#include <string>
#include <utility>
#include "ash/app_list/model/app_icon_load_helper.h"
#include "ash/app_list/model/app_list_item_observer.h"
#include "ash/ash_export.h"
#include "base/memory/ref_counted.h"
#include "base/timer/timer.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/button/button.h"
namespace gfx {
class Point;
class Rect;
} // namespace gfx
namespace ui {
class LocatedEvent;
class SimpleMenuModel;
} // namespace ui
namespace views {
class Label;
} // namespace views
namespace ash {
class AppsGridContextMenu;
class AppListConfig;
class AppListItem;
class AppListMenuModelAdapter;
class AppListViewDelegate;
namespace test {
class AppsGridViewTest;
class AppListMainViewTest;
} // namespace test
// An application icon and title. Commonly part of the AppsGridView, but may be
// used in other contexts. Supports dragging and keyboard selection via the
// GridDelegate interface.
class ASH_EXPORT AppListItemView : public views::Button,
public views::ContextMenuController,
public AppListItemObserver,
public ui::ImplicitAnimationObserver {
public:
METADATA_HEADER(AppListItemView);
// The types of context where the app list item view is shown.
enum class Context {
// The item is shown in an AppsGridView.
kAppsGridView,
// The item is shown in the RecentAppsView.
kRecentAppsView
};
// The parent apps grid (AppsGridView) or a stub. Not named "Delegate" to
// differentiate it from AppListViewDelegate.
class GridDelegate {
public:
virtual ~GridDelegate() = default;
// Whether the parent apps grid (if any) is a folder.
virtual bool IsInFolder() const = 0;
// Methods for keyboard selection.
virtual void SetSelectedView(AppListItemView* view) = 0;
virtual void ClearSelectedView() = 0;
virtual bool IsSelectedView(const AppListItemView* view) const = 0;
// Registers `view` as a dragged item with the apps grid. Called when the
// user presses the mouse, or starts touch interaction with the view (both
// of which may transition into a drag operation).
// `location` - The pointer location in the view's bounds.
// `root_location` - The pointer location in the root window coordinates.
// `drag_start_callback` - Callback that gets called when the mouse/touch
// interaction transitions into a drag (i.e. when the "drag" item starts
// moving.
// `drag_end_callback` - Callback that gets called when drag interaction
// ends.
// Returns whether `view` has been registered as a dragged view. Callbacks
// should be ignored if the method returns false. If the method returns
// true, it's expected to eventually run `drag_end_callback`.
virtual bool InitiateDrag(AppListItemView* view,
const gfx::Point& location,
const gfx::Point& root_location,
base::OnceClosure drag_start_callback,
base::OnceClosure drag_end_callback) = 0;
virtual void StartDragAndDropHostDragAfterLongPress() = 0;
// Called from AppListItemView when it receives a drag event. Returns true
// if the drag is still happening.
virtual bool UpdateDragFromItem(bool is_touch,
const ui::LocatedEvent& event) = 0;
virtual void EndDrag(bool cancel) = 0;
// Provided as a callback for AppListItemView to notify of activation via
// press/click/return key.
virtual void OnAppListItemViewActivated(AppListItemView* pressed_item_view,
const ui::Event& event) = 0;
};
AppListItemView(const AppListConfig* app_list_config,
GridDelegate* grid_delegate,
AppListItem* item,
AppListViewDelegate* view_delegate,
Context context);
AppListItemView(const AppListItemView&) = delete;
AppListItemView& operator=(const AppListItemView&) = delete;
~AppListItemView() override;
// Sets the app list config that should be used to size the app list icon, and
// margins within the app list item view. The owner should ensure the
// `AppListItemView` does not outlive the object referenced by
// `app_list_config_`.
void UpdateAppListConfig(const AppListConfig* app_list_config);
// Sets the icon of this image.
void SetIcon(const gfx::ImageSkia& icon);
void SetItemName(const std::u16string& display_name,
const std::u16string& full_name);
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
void CancelContextMenu();
gfx::Point GetDragImageOffset();
void SetAsAttemptedFolderTarget(bool is_target_folder);
// Sets focus without a11y announcements or focus ring.
void SilentlyRequestFocus();
// Ensures that the item view is selected by `grid_delegate_`.
void EnsureSelected();
AppListItem* item() const { return item_weak_; }
views::Label* title() { return title_; }
// In a synchronous drag the item view isn't informed directly of the drag
// ending, so the runner of the drag should call this.
void OnSyncDragEnd();
// Returns the icon bounds relative to AppListItemView.
gfx::Rect GetIconBounds() const;
// Returns the icon bounds in screen.
gfx::Rect GetIconBoundsInScreen() const;
// Returns the image of icon.
gfx::ImageSkia GetIconImage() const;
// Sets the icon's visibility.
void SetIconVisible(bool visible);
// Handles the icon's scaling and animation for a cardified grid.
void EnterCardifyState();
void ExitCardifyState();
// Returns the icon bounds for with |target_bounds| as the bounds of this view
// and given |icon_size| and the |icon_scale| if the icon was scaled from the
// original display size.
static gfx::Rect GetIconBoundsForTargetViewBounds(
const AppListConfig* config,
const gfx::Rect& target_bounds,
const gfx::Size& icon_size,
float icon_scale);
// Returns the title bounds for with |target_bounds| as the bounds of this
// view and given |title_size| and the |icon_scale| if the icon was scaled
// from the original display size.
static gfx::Rect GetTitleBoundsForTargetViewBounds(
const AppListConfig* config,
const gfx::Rect& target_bounds,
const gfx::Size& title_size,
float icon_scale);
// views::Button overrides:
void OnGestureEvent(ui::GestureEvent* event) override;
void OnThemeChanged() override;
// views::View overrides:
std::u16string GetTooltipText(const gfx::Point& p) const override;
// When a dragged view enters this view, a preview circle is shown for
// non-folder item while the icon is enlarged for folder item. When a
// dragged view exits this view, the reverse animation will be performed.
void OnDraggedViewEnter();
void OnDraggedViewExit();
// Enables background blur for folder icon if |enabled| is true.
void SetBackgroundBlurEnabled(bool enabled);
// Ensures this item view has its own layer.
void EnsureLayer();
bool HasNotificationBadge();
void FireMouseDragTimerForTest();
bool FireTouchDragTimerForTest();
// Whether the context menu on a non-folder app item view is showing.
bool IsShowingAppMenu() const;
bool is_folder() const { return is_folder_; }
bool IsNotificationIndicatorShownForTest() const;
GridDelegate* grid_delegate_for_test() { return grid_delegate_; }
AppListMenuModelAdapter* item_menu_model_adapter() const {
return item_menu_model_adapter_.get();
}
AppsGridContextMenu* context_menu_for_folder() const {
return context_menu_for_folder_.get();
}
// Sets the callback which will run after the context menu is shown.
void SetContextMenuShownCallbackForTest(base::RepeatingClosure closure);
private:
friend class AppListItemViewProductivityLauncherTest;
friend class test::AppsGridViewTest;
friend class test::AppListMainViewTest;
class IconImageView;
class AppNotificationIndicatorView;
enum UIState {
UI_STATE_NORMAL, // Normal UI (icon + label)
UI_STATE_DRAGGING, // Dragging UI (scaled icon only)
UI_STATE_DROPPING_IN_FOLDER, // Folder dropping preview UI
};
// Describes the app list item view drag state.
enum class DragState {
// Item is not being dragged.
kNone,
// Drag is initialized for the item (the owning apps grid considers the view
// to be the dragged view), but the item is still not being dragged.
// Depending on mouse/touch drag timers, UI may be in either normal, or
// dragging state.
kInitialized,
// The item drag is in progress. While in this state, the owning apps grid
// view will generally hide the item view, and replace it with a drag icon
// widget. The UI should be in dragging state (scaled up and with title
// hidden).
kStarted,
};
// gfx::AnimationDelegate:
void AnimationProgressed(const gfx::Animation* animation) override;
// Callback used when a menu is closed.
void OnMenuClosed();
// Get icon from |item_| and schedule background processing.
void UpdateIcon();
// Update the tooltip text from |item_|.
void UpdateTooltip();
void SetUIState(UIState state);
// Scales up app icon if |scale_up| is true; otherwise, scale it back to
// normal size.
void ScaleAppIcon(bool scale_up);
// Scale app icon to |scale_factor| without animation.
void ScaleIconImmediatly(float scale_factor);
// Sets |touch_dragging_| flag and updates UI.
void SetTouchDragging(bool touch_dragging);
// Sets |mouse_dragging_| flag and updates UI. Only to be called on
// |mouse_drag_timer_|.
void SetMouseDragging(bool mouse_dragging);
// Invoked when |mouse_drag_timer_| fires to show dragging UI.
void OnMouseDragTimer();
// Invoked when |touch_drag_timer_| fires to show dragging UI.
void OnTouchDragTimer(const gfx::Point& tap_down_location,
const gfx::Point& tap_down_root_location);
// Registers this view as a dragged view with the grid delegate.
bool InitiateDrag(const gfx::Point& location,
const gfx::Point& root_location);
// Called when the drag registered for this view starts moving.
// `drag_start_callback` passed to `GridDelegate::InitiateDrag()`.
void OnDragStarted();
// Called when the drag registered for this view ends.
// `drag_end_callback` passed to `GridDelegate::InitiateDrag()`.
void OnDragEnded();
// Callback invoked when a context menu is received after calling
// |AppListViewDelegate::GetContextMenuModel|.
void OnContextMenuModelReceived(
const gfx::Point& point,
ui::MenuSourceType source_type,
std::unique_ptr<ui::SimpleMenuModel> menu_model);
// views::ContextMenuController overrides:
void ShowContextMenuForViewImpl(views::View* source,
const gfx::Point& point,
ui::MenuSourceType source_type) override;
// views::Button overrides:
bool ShouldEnterPushedState(const ui::Event& event) override;
void PaintButtonContents(gfx::Canvas* canvas) override;
// views::View overrides:
void Layout() override;
gfx::Size CalculatePreferredSize() const override;
bool OnKeyPressed(const ui::KeyEvent& event) override;
bool OnMousePressed(const ui::MouseEvent& event) override;
void OnMouseReleased(const ui::MouseEvent& event) override;
void OnMouseCaptureLost() override;
bool OnMouseDragged(const ui::MouseEvent& event) override;
bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override;
void OnFocus() override;
void OnBlur() override;
// AppListItemObserver overrides:
void ItemIconChanged(AppListConfigType config_type) override;
void ItemNameChanged() override;
void ItemBadgeVisibilityChanged() override;
void ItemBadgeColorChanged() override;
void ItemIsNewInstallChanged() override;
void ItemBeingDestroyed() override;
// ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override;
// Returns the radius of preview circle.
int GetPreviewCircleRadius() const;
// Creates dragged view hover animation if it does not exist.
void CreateDraggedViewHoverAnimation();
// Modifies AppListItemView bounds to match the selected highlight bounds.
void AdaptBoundsForSelectionHighlight(gfx::Rect* rect);
// Calculates the transform between the icon scaled by |icon_scale| and the
// normal size icon.
gfx::Transform GetScaleTransform(float icon_scale);
// The app list config used to layout this view. The initial values is set
// during view construction, but can be changed by calling
// `UpdateAppListConfig()`.
const AppListConfig* app_list_config_;
const bool is_folder_;
// Whether context menu options have been requested. Prevents multiple
// requests.
bool waiting_for_context_menu_options_ = false;
AppListItem* item_weak_; // Owned by AppListModel. Can be nullptr.
// Handles dragging and item selection. Might be a stub for items that are not
// part of an apps grid.
GridDelegate* const grid_delegate_;
// AppListControllerImpl by another name.
AppListViewDelegate* const view_delegate_;
IconImageView* icon_ = nullptr; // Strongly typed child view.
views::Label* title_ = nullptr; // Strongly typed child view.
// Draws a dot next to the title for newly installed apps.
views::View* new_install_dot_ = nullptr;
// The context menu model adapter used for app item view.
std::unique_ptr<AppListMenuModelAdapter> item_menu_model_adapter_;
// The context menu controller used for folder item view.
std::unique_ptr<AppsGridContextMenu> context_menu_for_folder_;
UIState ui_state_ = UI_STATE_NORMAL;
// True if scroll gestures should contribute to dragging.
bool touch_dragging_ = false;
// True if the app is enabled for drag/drop operation by mouse.
bool mouse_dragging_ = false;
// Whether AppsGridView should not be notified of a focus event, triggering
// A11y alerts and a focus ring.
bool focus_silently_ = false;
// Whether AppsGridView is in cardified state.
bool in_cardified_grid_ = false;
// The animation that runs when dragged view enters or exits this view.
std::unique_ptr<gfx::SlideAnimation> dragged_view_hover_animation_;
// The radius of preview circle for non-folder item.
int preview_circle_radius_ = 0;
// Whether `item_menu_model_adapter_` was cancelled as the result of a
// continuous drag gesture.
bool menu_close_initiated_from_drag_ = false;
// Whether `item_menu_model_adapter_` was shown via key event.
bool menu_show_initiated_from_key_ = false;
std::u16string tooltip_text_;
// A timer to defer showing drag UI when mouse is pressed.
base::OneShotTimer mouse_drag_timer_;
// A timer to defer showing drag UI when the app item is touch pressed.
base::OneShotTimer touch_drag_timer_;
// The bitmap image for this app list item.
gfx::ImageSkia icon_image_;
// The current item's drag state.
DragState drag_state_ = DragState::kNone;
// The scaling factor for displaying the app icon.
float icon_scale_ = 1.0f;
// Draws an indicator in the top right corner of the image to represent an
// active notification.
AppNotificationIndicatorView* notification_indicator_ = nullptr;
// Indicates the context in which this view is shown.
const Context context_;
// Helper to trigger icon load.
absl::optional<AppIconLoadHelper> icon_load_helper_;
// Called when the context menu is shown.
base::RepeatingClosure context_menu_shown_callback_;
base::WeakPtrFactory<AppListItemView> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_APP_LIST_VIEWS_APP_LIST_ITEM_VIEW_H_