| // 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. |
| |
| #ifndef ASH_WM_MODE_PIE_MENU_VIEW_H_ |
| #define ASH_WM_MODE_PIE_MENU_VIEW_H_ |
| |
| #include <stack> |
| #include <string> |
| #include <vector> |
| |
| #include "ash/ash_export.h" |
| #include "base/containers/flat_map.h" |
| #include "base/memory/raw_ptr.h" |
| #include "ui/base/metadata/metadata_header_macros.h" |
| #include "ui/views/view.h" |
| |
| namespace gfx { |
| struct VectorIcon; |
| } // namespace gfx |
| |
| namespace views { |
| class ImageButton; |
| } // namespace views |
| |
| namespace ash { |
| |
| class PieMenuButton; |
| class PieMenuView; |
| |
| // ----------------------------------------------------------------------------- |
| // PieSubMenuContainerView: |
| |
| // Defines a container for buttons representing menu items in a pie menu. |
| class ASH_EXPORT PieSubMenuContainerView : public views::View { |
| public: |
| METADATA_HEADER(PieSubMenuContainerView); |
| |
| PieSubMenuContainerView(const PieSubMenuContainerView&) = delete; |
| PieSubMenuContainerView& operator=(const PieSubMenuContainerView&) = delete; |
| ~PieSubMenuContainerView() override; |
| |
| size_t button_count() const { return buttons_.size(); } |
| |
| // Adds a new menu item button with the given `button_id`, |
| // `button_label_text`, and an optional `icon` (if non-null). The `button_id` |
| // must be unique among all the IDs of the current existing buttons hosted by |
| // parent `PieMenuView` and all its sub menus. |
| // Returns a pointer to the newly added button. |
| views::View* AddMenuButton(int button_id, |
| const std::u16string& button_label_text, |
| const gfx::VectorIcon* icon); |
| |
| // Removes all the currently added buttons in this container. This can be used |
| // to rebuild the contents of this sub menu from scratch. |
| void RemoveAllButtons(); |
| |
| private: |
| friend class PieMenuView; |
| |
| explicit PieSubMenuContainerView(PieMenuView* owner_menu_view); |
| |
| // The parent owner pie menu that hosts this container. Not null. |
| const raw_ptr<PieMenuView> owner_menu_view_; |
| |
| // The buttons on this container, which will be painted as slices of a circle |
| // in their same order in this vector. |
| std::vector<raw_ptr<PieMenuButton, VectorExperimental>> buttons_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // PieMenuView: |
| |
| // Defines a pie menu that lists its menu items as radial slices of a circle. |
| // Each menu item button can open its sub menu with its own buttons, replacing |
| // the current content of this pie view. A back button is then shown to go back |
| // to the previous sub menu in the stack. There is always a main menu container |
| // at all times. |
| class ASH_EXPORT PieMenuView : public views::View { |
| public: |
| METADATA_HEADER(PieMenuView); |
| |
| // Defines an interface for the delegate of this class which will be informed |
| // when a button on this pie view is pressed. |
| class Delegate { |
| public: |
| // Called when the button with the given `button_id` is pressed. |
| virtual void OnPieMenuButtonPressed(int button_id) = 0; |
| |
| protected: |
| virtual ~Delegate() = default; |
| }; |
| |
| explicit PieMenuView(Delegate* delegate); |
| PieMenuView(const PieMenuView&) = delete; |
| PieMenuView& operator=(const PieMenuView&) = delete; |
| ~PieMenuView() override; |
| |
| PieSubMenuContainerView* main_menu_container() { |
| return main_menu_container_; |
| } |
| |
| // Returns the `PieSubMenuContainerView` that the button whose ID is |
| // `button_id` opens when pressed if any, or creates one for it and associates |
| // it with that button. A button with `button_id` must exist in this pie menu. |
| PieSubMenuContainerView* GetOrAddSubMenuForButton(int button_id); |
| |
| // Sets the label of the button whose ID is `button_id` to the given `text`. |
| // A button with `button_id` must exist in this pie menu. |
| void SetButtonLabelText(int button_id, const std::u16string& text); |
| |
| // Pops all the sub menus (if any) and shows the main menu. |
| void ReturnToMainMenu(); |
| |
| // Similar to `GetButtonById()` below but returns the button as a |
| // `views::View`. This prevents the need for exposing the actual type of |
| // `PieMenuButton`. |
| views::View* GetButtonByIdAsView(int button_id) const; |
| |
| // Gets the center point in screen coordinates of the contents of the button |
| // whose ID is `button_id`. If no such button is found, an empty point is |
| // returned. |
| gfx::Point GetButtonContentsCenterInScreen(int button_id) const; |
| |
| // views::View: |
| void Layout(PassKey) override; |
| void AddedToWidget() override; |
| void OnThemeChanged() override; |
| |
| private: |
| friend class PieSubMenuContainerView; |
| |
| // Called when the given `button` is added on any of the sub menu containers |
| // hosted by this view. |
| void OnPieMenuButtonAdded(PieMenuButton* button); |
| |
| // Called when the given `button` is removed from any of the sub menu |
| // containers hosted by this view. |
| void OnPieMenuButtonRemoved(PieMenuButton* button); |
| |
| // Called when the given `button` is pressed. This will inform the `delegate_` |
| // via the `OnPieMenuButtonPressed()` API. |
| void OnPieMenuButtonPressed(PieMenuButton* button); |
| |
| // Opens the given `sub_menu` by making it the only visible sub menu container |
| // which visually replaces the contents of this pie view with the buttons |
| // hosted by the given `sub_menu`. The `back_button_` will be shown to allow |
| // the user to go back to the previous sub menu in `active_sub_menus_stack_` |
| // (if any) or to the `main_menu_container_`. |
| void OpenSubMenu(PieSubMenuContainerView* sub_menu); |
| |
| // If there are active sub menus in `active_sub_menus_stack_`, this function |
| // pops the top-most one such that the previous sub menu shows. Finally when |
| // there are no more sub menus, the `main_menu_container_` will show, and the |
| // `back_button_` will hide. |
| void MaybePopSubMenu(); |
| |
| // Returns the button whose ID is `button_id` or nullptr if it doesn't exist. |
| PieMenuButton* GetButtonById(int button_id) const; |
| |
| // The delegate of this view which takes care of handling button presses. Not |
| // null. |
| const raw_ptr<Delegate, DanglingUntriaged> delegate_; |
| |
| // The container hosting the buttons on the main menu of this view. When this |
| // is visible, `active_sub_menus_stack_` should be empty, and `back_button_` |
| // should be hidden. |
| const raw_ptr<PieSubMenuContainerView> main_menu_container_; |
| |
| // Since a button on a sub menu can open another sub menu and so on, we keep |
| // a stack of currently active sub menus (other than the main menu), so that |
| // pressing on `back_button_` pops the top-most sub menu to show the previous |
| // one, until there are no more active sub menus, at which point |
| // `main_menu_container_` shows up and `back_button_` hides. |
| std::stack<PieSubMenuContainerView*> active_sub_menus_stack_; |
| |
| const raw_ptr<views::ImageButton> back_button_; |
| |
| // Maps all the buttons on all sub menu containers of this view by their IDs. |
| base::flat_map</*button_id=*/int, PieMenuButton*> buttons_by_id_; |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_WM_MODE_PIE_MENU_VIEW_H_ |