1// Aseprite UI Library
2// Copyright (C) 2020-2022 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifndef UI_MENU_H_INCLUDED
9#define UI_MENU_H_INCLUDED
10#pragma once
11
12#include "obs/signal.h"
13#include "ui/register_message.h"
14#include "ui/separator.h"
15#include "ui/widget.h"
16#include "ui/window.h"
17
18#include <memory>
19
20namespace ui {
21
22 class MenuBoxWindow;
23 class MenuItem;
24 class Timer;
25 struct MenuBaseData;
26
27 class Menu : public Widget {
28 public:
29 Menu();
30 ~Menu();
31
32 void showPopup(const gfx::Point& pos,
33 Display* parentDisplay);
34 Widget* findItemById(const char* id) const;
35
36 // Returns the MenuItem that has as submenu this menu.
37 MenuItem* getOwnerMenuItem() {
38 return m_menuitem;
39 }
40
41 obs::signal<void()> OpenPopup;
42
43 protected:
44 virtual void onPaint(PaintEvent& ev) override;
45 virtual void onResize(ResizeEvent& ev) override;
46 virtual void onSizeHint(SizeHintEvent& ev) override;
47 virtual void onOpenPopup();
48
49 private:
50 void setOwnerMenuItem(MenuItem* ownerMenuItem) {
51 m_menuitem = ownerMenuItem;
52 }
53
54 void closeAll();
55
56 MenuItem* getHighlightedItem();
57 void highlightItem(MenuItem* menuitem, bool click, bool open_submenu, bool select_first_child);
58 void unhighlightItem();
59
60 MenuItem* m_menuitem; // From where the menu was open
61
62 friend class MenuBox;
63 friend class MenuItem;
64 };
65
66 class MenuBox : public Widget {
67 public:
68 MenuBox(WidgetType type = kMenuBoxWidget);
69 ~MenuBox();
70
71 Menu* getMenu();
72 void setMenu(Menu* menu);
73
74 MenuBaseData* getBase() {
75 return m_base.get();
76 }
77
78 // Closes all menu-boxes and goes back to the normal state of the
79 // menu-bar.
80 void cancelMenuLoop();
81
82 protected:
83 virtual bool onProcessMessage(Message* msg) override;
84 virtual void onResize(ResizeEvent& ev) override;
85 virtual void onSizeHint(SizeHintEvent& ev) override;
86 MenuBaseData* createBase();
87
88 private:
89 void closePopup();
90 void startFilteringMouseDown();
91 void stopFilteringMouseDown();
92
93 std::unique_ptr<MenuBaseData> m_base;
94
95 friend class Menu;
96 friend class MenuItem;
97 };
98
99 class MenuBar : public MenuBox {
100 public:
101 enum class ProcessTopLevelShortcuts { kNo, kYes };
102
103 MenuBar(ProcessTopLevelShortcuts processShortcuts);
104
105 bool processTopLevelShortcuts() const {
106 return m_processTopLevelShortcuts;
107 }
108
109 static bool expandOnMouseover();
110 static void setExpandOnMouseover(bool state);
111
112 private:
113 // True if we should open top-level menus with Alt+mnemonic (this
114 // flag is not used by Aseprite), top-level menus are opened with
115 // the ShowMenu command now.
116 bool m_processTopLevelShortcuts;
117 static bool m_expandOnMouseover;
118 };
119
120 class MenuItem : public Widget {
121 public:
122 MenuItem(const std::string& text);
123 ~MenuItem();
124
125 Menu* getSubmenu();
126 void setSubmenu(Menu* submenu);
127
128 // Open the submenu of this menu item (the menu item should be
129 // positioned in a correct position on the screen).
130 void openSubmenu();
131
132 bool isHighlighted() const;
133 void setHighlighted(bool state);
134
135 // Returns true if the MenuItem has a submenu.
136 bool hasSubmenu() const;
137
138 // Returns true if the submenu is opened.
139 bool hasSubmenuOpened() const {
140 return (m_submenu_menubox != nullptr);
141 }
142
143 // Returns the menu-box where the sub-menu has been opened, or
144 // just nullptr if the sub-menu is closed.
145 MenuBox* getSubmenuContainer() const {
146 return m_submenu_menubox;
147 }
148
149 void executeClick();
150 void validateItem();
151
152 // Fired when the menu item is clicked.
153 obs::signal<void()> Click;
154
155 protected:
156 bool onProcessMessage(Message* msg) override;
157 void onInitTheme(InitThemeEvent& ev) override;
158 void onPaint(PaintEvent& ev) override;
159 void onSizeHint(SizeHintEvent& ev) override;
160 virtual void onClick();
161 virtual void onValidate();
162
163 bool inBar() const;
164
165 private:
166 void openSubmenu(bool select_first);
167 void closeSubmenu(bool last_of_close_chain);
168 void startTimer();
169 void stopTimer();
170
171 bool m_highlighted; // Is it highlighted?
172 Menu* m_submenu; // The sub-menu
173 MenuBox* m_submenu_menubox; // The opened menubox for this menu-item
174 std::unique_ptr<Timer> m_submenu_timer; // Timer to open the submenu
175
176 friend class Menu;
177 friend class MenuBox;
178 friend class MenuBoxWindow;
179 };
180
181 class MenuSeparator : public Separator {
182 public:
183 MenuSeparator() : Separator("", HORIZONTAL) {
184 }
185 };
186
187 class MenuBoxWindow : public Window {
188 public:
189 MenuBoxWindow(MenuItem* menuitem = nullptr);
190 ~MenuBoxWindow();
191 MenuBox* menubox() { return &m_menubox; }
192 protected:
193 bool onProcessMessage(Message* msg) override;
194 private:
195 MenuBox m_menubox;
196 MenuItem* m_menuitem;
197 };
198
199 extern RegisterMessage kOpenMenuItemMessage;
200 extern RegisterMessage kCloseMenuItemMessage;
201 extern RegisterMessage kClosePopupMessage;
202 extern RegisterMessage kExecuteMenuItemMessage;
203
204} // namespace ui
205
206#endif
207