1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255

ash / wm / desks / desk_mini_view.h [blame]

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_WM_DESKS_DESK_MINI_VIEW_H_
#define ASH_WM_DESKS_DESK_MINI_VIEW_H_

#include <memory>

#include "ash/ash_export.h"
#include "ash/wm/desks/desk.h"
#include "ash/wm/desks/desks_controller.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
#include "ui/views/animation/animation_abort_handle.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/view_observer.h"

namespace ash {

class DeskActionContextMenu;
class DeskActionView;
class DeskBarViewBase;
class DeskNameView;
class DeskPreviewView;
class DeskProfilesButton;
class WindowOcclusionCalculator;

// A view that acts as a mini representation (a.k.a. desk thumbnail) of a
// virtual desk in the desk bar view when overview mode is active. This view
// shows a preview of the contents of the associated desk, its title, and
// supports desk activation and removal.
class ASH_EXPORT DeskMiniView : public views::View,
                                public Desk::Observer,
                                public views::TextfieldController,
                                public views::ViewObserver {
  METADATA_HEADER(DeskMiniView, views::View)

 public:
  // Returns the width of the desk preview based on its |preview_height| and the
  // aspect ratio of the root window taken from |root_window_size|.
  static int GetPreviewWidth(const gfx::Size& root_window_size,
                             int preview_height);

  // The desk preview bounds are proportional to the bounds of the display on
  // which it resides.
  static gfx::Rect GetDeskPreviewBounds(aura::Window* root_window);

  DeskMiniView(
      DeskBarViewBase* owner_bar,
      aura::Window* root_window,
      Desk* desk,
      base::WeakPtr<WindowOcclusionCalculator> window_occlusion_calculator);

  DeskMiniView(const DeskMiniView&) = delete;
  DeskMiniView& operator=(const DeskMiniView&) = delete;

  ~DeskMiniView() override;

  aura::Window* root_window() { return root_window_; }

  const Desk* desk() const { return desk_; }
  Desk* desk() { return desk_; }

  DeskNameView* desk_name_view() { return desk_name_view_; }

  const DeskActionView* desk_action_view() const { return desk_action_view_; }
  DeskActionView* desk_action_view() { return desk_action_view_; }
  DeskActionContextMenu* context_menu() { return context_menu_.get(); }

  DeskProfilesButton* desk_profiles_button() { return desk_profile_button_; }

  DeskBarViewBase* owner_bar() { return owner_bar_; }
  const DeskBarViewBase* owner_bar() const { return owner_bar_; }
  const DeskPreviewView* desk_preview() const { return desk_preview_; }
  DeskPreviewView* desk_preview() { return desk_preview_; }

  bool is_animating_to_remove() const { return is_animating_to_remove_; }
  void set_is_animating_to_remove(bool value) {
    is_animating_to_remove_ = value;
  }

  // Sets the animation abort handle. Please note, it will abort the existing
  // animation first (if there is one) when a new one comes.
  void set_animation_abort_handle(
      std::unique_ptr<views::AnimationAbortHandle> animation_abort_handle) {
    animation_abort_handle_ = std::move(animation_abort_handle);
  }

  gfx::Rect GetPreviewBoundsInScreen() const;

  // Returns the associated desk's container window on the display this
  // mini_view resides on.
  aura::Window* GetDeskContainer() const;

  // Returns true if the desk's name is being modified (i.e. the DeskNameView
  // has the focus).
  bool IsDeskNameBeingModified() const;

  // Updates the visibility state of the desk buttons depending on whether this
  // view is mouse hovered, or if switch access is enabled.
  void UpdateDeskButtonVisibility();

  // Gesture tapping may affect the visibility of the desk buttons. There's only
  // one mini_view that shows the desk buttons on long press at any time.
  // This is useful for touch-only UIs.
  void OnWidgetGestureTap(const gfx::Rect& screen_rect, bool is_long_gesture);

  // Returns the expected focus color of `DeskPreviewView` based on the
  // activation state of the corresponding desk and whether the saved desk
  // library is visible.
  std::optional<ui::ColorId> GetFocusColor() const;

  // Updates the focus color of `DeskPreviewView`.
  void UpdateFocusColor();

  // Gets the preview border's insets.
  gfx::Insets GetPreviewBorderInsets() const;

  bool IsPointOnMiniView(const gfx::Point& screen_location) const;

  // Hides the `desk_action_view_` and opens `context_menu_`. Called when
  // `desk_preview_` is right-clicked or long-pressed. `source` is the type of
  // action that caused the context menu to be opened (e.g. long press versus
  // mouse click), and is provided to the context menu runner when the menu is
  // open in `DeskActionContextMenu::ShowContextMenuForViewImpl` so that it can
  // further evaluate menu positioning. This ends up doing nothing in particular
  // in the case of the `DeskActionContextMenu` because we use a
  // `views::MenuRunner::FIXED_ANCHOR` run type parameter, but the
  // `MenuRunner::RunMenuAt` function still requires this parameter, so we pass
  // it down to the function through this parameter.
  void OpenContextMenu(ui::mojom::MenuSourceType source);

  // Closes context menu on this mini view if one exists.
  void MaybeCloseContextMenu();

  // Invoked when the user has clicked a desk close button.
  void OnRemovingDesk(DeskCloseType close_type);

  // Notifies the mini-view that the preview or profile button is about to
  // request focus from a reverse tab traversal so that it can show and focus
  // the desk action view first if it was not already focused.
  void OnPreviewOrProfileAboutToBeFocusedByReverseTab();

  // views::View:
  void Layout(PassKey) override;
  gfx::Size CalculatePreferredSize(
      const views::SizeBounds& available_size) const override;
  void OnThemeChanged() override;

  // Desk::Observer:
  void OnContentChanged() override;
  void OnDeskDestroyed(const Desk* desk) override;
  void OnDeskNameChanged(const std::u16string& new_name) override;

  // views::TextfieldController:
  void ContentsChanged(views::Textfield* sender,
                       const std::u16string& new_contents) override;
  bool HandleKeyEvent(views::Textfield* sender,
                      const ui::KeyEvent& key_event) override;
  bool HandleMouseEvent(views::Textfield* sender,
                        const ui::MouseEvent& mouse_event) override;

  // views::ViewObserver:
  void OnViewFocused(views::View* observed_view) override;
  void OnViewBlurred(views::View* observed_view) override;

 private:
  friend class DesksTestApi;

  // Callback for when `context_menu_` is closed. Makes `desk_action_view_`
  // visible.
  void OnContextMenuClosed();

  // Callback for when a user selects a lacros profile from `context_menu_`.
  void OnSetLacrosProfileId(uint64_t lacros_profile_id);

  void OnDeskPreviewPressed();

  // Callbacks for when a user selects the save desk options in the context
  // menu.
  void OnSaveDeskAsTemplateButtonPressed();
  void OnSaveDeskForLaterButtonPressed();

  // Layout |desk_name_view_| given the current bounds of the desk preview.
  void LayoutDeskNameView(const gfx::Rect& preview_bounds);

  void UpdateAccessibleName();

  const raw_ptr<DeskBarViewBase> owner_bar_;

  // The root window on which this mini_view is created.
  const raw_ptr<aura::Window> root_window_;

  // The associated desk. This can become null if the desk is deleted before the
  // mini view is done. Desk deletion is monitored by `OnDeskDestroyed`.
  raw_ptr<Desk> desk_;  // Not owned.

  // The view that shows a preview of the desk contents.
  raw_ptr<DeskPreviewView> desk_preview_ = nullptr;

  // The view that shows what profile the desk belongs to.
  raw_ptr<DeskProfilesButton> desk_profile_button_ = nullptr;

  // The editable desk name.
  raw_ptr<DeskNameView> desk_name_view_ = nullptr;

  // Stores the hover interface for desk actions.
  raw_ptr<DeskActionView> desk_action_view_ = nullptr;

  // The context menu that appears when `desk_preview_` is right-clicked or
  // long-pressed.
  std::unique_ptr<DeskActionContextMenu> context_menu_;

  // The view containing the desk shortcut icons and labels displaying the
  // shortcut to activate the desk.
  raw_ptr<views::BoxLayoutView> desk_shortcut_view_ = nullptr;

  // The label for the desk shortcut view containing the desk number.
  raw_ptr<views::Label> desk_shortcut_label_ = nullptr;

  // True when this mini view is being animated to be removed from the bar.
  bool is_animating_to_remove_ = false;

  // We force showing desk buttons when the mini_view is long pressed or
  // tapped using touch gestures.
  bool force_show_desk_buttons_ = false;

  // When the DeskNameView is focused, we select all its text. However, if it is
  // focused via a mouse press event, on mouse release will clear the selection.
  // Therefore, we defer selecting all text until we receive that mouse release.
  bool defer_select_all_ = false;

  bool is_desk_name_being_modified_ = false;

  // This is initialized to true and tells the OnViewBlurred function if the
  // user wants to set a new desk name. We set this to false if the
  // HandleKeyEvent function detects that the escape key was pressed so that
  // OnViewBlurred does not change the name of `desk_`.
  bool should_commit_name_changes_ = true;

  // A handle that aborts the active mini view animation when:
  //   1. The mini view is destroyed as the whole bar view is gone.
  //   2. Another new animation is triggered for the same mini view.
  std::unique_ptr<views::AnimationAbortHandle> animation_abort_handle_;
};

}  // namespace ash

#endif  // ASH_WM_DESKS_DESK_MINI_VIEW_H_