Toggle mainmenu submenus instead of opening the same submenu on repeated clicks.
[0ad.git] / binaries / data / mods / public / gui / pregame / MainMenuItemHandler.js
blob94fba1e298462bd872a013cfed81c83d1e0cbed4
1 /**
2  * This class sets up the main menu buttons, animates submenu that opens when
3  * clicking on category buttons, assigns the defined actions and hotkeys to every button.
4  */
5 class MainMenuItemHandler
7         constructor(menuItems)
8         {
9                 this.menuItems = menuItems;
10                 this.lastTickTime = Date.now();
12                 this.lastOpenItem = undefined;
14                 this.mainMenu = Engine.GetGUIObjectByName("mainMenu");
15                 this.mainMenuButtons = Engine.GetGUIObjectByName("mainMenuButtons");
16                 this.submenu = Engine.GetGUIObjectByName("submenu");
17                 this.submenuButtons = Engine.GetGUIObjectByName("submenuButtons");
18                 this.MainMenuPanelRightBorderTop = Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop");
19                 this.MainMenuPanelRightBorderBottom = Engine.GetGUIObjectByName("MainMenuPanelRightBorderBottom");
21                 this.setupMenuButtons(this.mainMenuButtons.children, this.menuItems);
22                 this.setupHotkeys(this.menuItems);
24                 Engine.GetGUIObjectByName("closeMenuButton").onPress = this.closeSubmenu.bind(this);
25         }
27         setupMenuButtons(buttons, menuItems)
28         {
29                 buttons.forEach((button, i) => {
30                         let item = menuItems[i];
31                         button.hidden = !item;
32                         if (button.hidden)
33                                 return;
35                         button.size = new GUISize(
36                                 0, (this.ButtonHeight + this.Margin) * i,
37                                 0, (this.ButtonHeight + this.Margin) * i + this.ButtonHeight,
38                                 0, 0, 100, 0);
39                         button.caption = item.caption;
40                         button.tooltip = item.tooltip;
41                         button.enabled = item.enabled === undefined || item.enabled;
42                         button.onPress = this.pressButton.bind(this, item, i);
43                         button.hidden = false;
44                 });
46                 if (buttons.length < menuItems.length)
47                         error("GUI page has space for " + buttons.length + " menu buttons, but " + menuItems.length + " items are provided!");
48         }
50         /**
51          * Expand selected submenu, or collapse if it already is expanded.
52          */
53         pressButton(item, i)
54         {
55                 if (this.submenu.hidden)
56                 {
57                         this.performButtonAction(item, i);
58                 }
59                 else
60                 {
61                         this.closeSubmenu();
62                         if (this.lastOpenItem && this.lastOpenItem != item)
63                                 this.performButtonAction(item, i);
64                         else
65                                 this.lastOpenItem = undefined;
66                 }
67         }
69         /**
70          * Expand submenu or perform action specified by the button object.
71          */
72         performButtonAction(item, i)
73         {
74                 this.lastOpenItem = item;
76                 if (item.onPress)
77                         item.onPress();
78                 else
79                         this.openSubmenu(i);
80         }
82         setupHotkeys(menuItems)
83         {
84                 for (let i in menuItems)
85                 {
86                         let item = menuItems[i];
87                         if (item.onPress && item.hotkey)
88                                 Engine.SetGlobalHotkey(item.hotkey, () => {
89                                         this.closeSubmenu();
90                                         item.onPress();
91                                 });
93                         if (item.submenu)
94                                 this.setupHotkeys(item.submenu);
95                 }
96         }
98         openSubmenu(i)
99         {
100                 this.setupMenuButtons(this.submenuButtons.children, this.menuItems[i].submenu);
102                 let top = this.mainMenuButtons.size.top + this.mainMenuButtons.children[i].size.top;
104                 this.submenu.size = new GUISize(
105                         this.submenu.size.left, top - this.Margin,
106                         this.submenu.size.right, top + (this.ButtonHeight + this.Margin) * this.menuItems[i].submenu.length);
108                 this.submenu.hidden = false;
110                 {
111                         let size = this.MainMenuPanelRightBorderTop.size;
112                         size.bottom = this.submenu.size.top + this.Margin;
113                         size.rbottom = 0;
114                         this.MainMenuPanelRightBorderTop.size = size;
115                 }
117                 {
118                         let size = this.MainMenuPanelRightBorderBottom.size;
119                         size.top = this.submenu.size.bottom;
120                         this.MainMenuPanelRightBorderBottom.size = size;
121                 }
123                 // Start animation
124                 this.lastTickTime = Date.now();
125                 this.mainMenu.onTick = this.onTick.bind(this);
126         }
128         closeSubmenu()
129         {
130                 this.submenu.hidden = true;
131                 this.submenu.size = this.mainMenu.size;
133                 let size = this.MainMenuPanelRightBorderTop.size;
134                 size.top = 0;
135                 size.bottom = 0;
136                 size.rbottom = 100;
137                 this.MainMenuPanelRightBorderTop.size = size;
138         }
140         onTick()
141         {
142                 let now = Date.now();
143                 if (now == this.lastTickTime)
144                         return;
146                 let maxOffset = this.mainMenu.size.right - this.submenu.size.left;
147                 let offset = Math.min(this.MenuSpeed * (now - this.lastTickTime), maxOffset);
149                 this.lastTickTime = now;
151                 if (this.submenu.hidden || !offset)
152                 {
153                         delete this.mainMenu.onTick;
154                         return;
155                 }
157                 let size = this.submenu.size;
158                 size.left += offset;
159                 size.right += offset;
160                 this.submenu.size = size;
161         }
165  * Vertical size per button.
166  */
167 MainMenuItemHandler.prototype.ButtonHeight = 28;
170  * Distance between consecutive buttons.
171  */
172 MainMenuItemHandler.prototype.Margin = 4;
175  * Collapse / expansion speed in pixels per milliseconds used when animating the button menu size.
176  */
177 MainMenuItemHandler.prototype.MenuSpeed = 1.2;