0ad/binaries/data/mods/public/gui/pregame/MainMenuItemHandler.js
Dunedan 93ce94655d
Use @stylistic/brace-style for eslint
Up to now `eslint-plugin-brace-rules` was used to enforce a common brace
style for JavaScript code. This plugin was however updated the last time
over 9 years ago and will be incompatible with ESLint v10, as that
[removes `context.getSourceCode()`][1], the plugin relies on.

To keep the eslint config working with ESLint v10, this replaces
`eslint-plugin-brace-rules` with the [`@stylistic/brace-style`][2] rule
from `@stylistic/eslint-plugin`, a package we already use.

While `@stylistic/brace-style` doesn't offer an option to format braces
in exactly the same way as before, the "allman" style seems to be the
one closest to the existing code.

[1]: https://eslint.org/blog/2025/11/eslint-v10.0.0-alpha.0-released/#removed-deprecated-rule-context-members
[2]: https://eslint.style/rules/brace-style
2026-01-12 21:33:52 +01:00

173 lines
4.3 KiB
JavaScript

/**
* This class sets up the main menu buttons, animates submenu that opens when
* clicking on category buttons, assigns the defined actions and hotkeys to every button.
*/
export class MainMenuItemHandler
{
constructor(closePageCallback, menuItems)
{
this.closePageCallback = closePageCallback;
this.menuItems = menuItems;
this.lastTickTime = Date.now();
this.lastOpenItem = undefined;
this.mainMenu = Engine.GetGUIObjectByName("mainMenu");
this.mainMenuButtons = Engine.GetGUIObjectByName("mainMenuButtons");
this.submenu = Engine.GetGUIObjectByName("submenu");
this.submenuButtons = Engine.GetGUIObjectByName("submenuButtons");
this.MainMenuPanelRightBorderTop = Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop");
this.MainMenuPanelRightBorderBottom = Engine.GetGUIObjectByName("MainMenuPanelRightBorderBottom");
this.setupMenuButtons(this.mainMenuButtons.children, this.menuItems);
this.setupHotkeys(this.menuItems);
Engine.GetGUIObjectByName("closeMenuButton").onPress = this.closeSubmenu.bind(this);
}
setupMenuButtons(buttons, menuItems)
{
buttons.forEach((button, i) =>
{
const item = menuItems[i];
button.hidden = !item;
if (button.hidden)
return;
button.size = {
"top": (this.ButtonHeight + this.Margin) * i,
"bottom": (this.ButtonHeight + this.Margin) * i + this.ButtonHeight,
"rright": 100
};
button.caption = item.caption;
button.tooltip = item.tooltip;
button.enabled = item.enabled === undefined || item.enabled();
button.onPress = this.pressButton.bind(this, item, i);
button.hidden = false;
});
if (buttons.length < menuItems.length)
error("GUI page has space for " + buttons.length + " menu buttons, but " + menuItems.length + " items are provided!");
}
/**
* Expand selected submenu, or collapse if it already is expanded.
*/
pressButton(item, i)
{
if (this.submenu.hidden)
{
this.performButtonAction(item, i);
}
else
{
this.closeSubmenu();
if (this.lastOpenItem && this.lastOpenItem != item)
this.performButtonAction(item, i);
else
this.lastOpenItem = undefined;
}
}
/**
* Expand submenu or perform action specified by the button object.
*/
performButtonAction(item, i)
{
this.lastOpenItem = item;
if (item.onPress)
item.onPress(this.closePageCallback);
else
this.openSubmenu(i);
}
setupHotkeys(menuItems)
{
for (const i in menuItems)
{
const item = menuItems[i];
if (item.onPress && item.hotkey)
Engine.SetGlobalHotkey(item.hotkey, "Press", () =>
{
this.closeSubmenu();
item.onPress();
});
if (item.submenu)
this.setupHotkeys(item.submenu);
}
}
openSubmenu(i)
{
this.setupMenuButtons(this.submenuButtons.children, this.menuItems[i].submenu);
const top = this.mainMenuButtons.children[i].getComputedSize().top;
this.submenu.size = {
"left": this.submenu.size.left,
"right": this.mainMenu.size.right,
"top": top - this.Margin,
"bottom": top + (this.ButtonHeight + this.Margin) * this.menuItems[i].submenu.length
};
this.submenu.hidden = false;
this.MainMenuPanelRightBorderTop.size.bottom = this.submenu.size.top + this.Margin;
this.MainMenuPanelRightBorderTop.size.rbottom = 0;
this.MainMenuPanelRightBorderBottom.size.top = this.submenu.size.bottom;
// Start animation
this.lastTickTime = Date.now();
this.mainMenu.onTick = this.onTick.bind(this);
}
closeSubmenu()
{
this.submenu.hidden = true;
this.submenu.size = this.mainMenu.size;
Object.assign(this.MainMenuPanelRightBorderTop.size, {
"top": 0,
"bottom": 0,
"rbottom": 100
});
}
onTick()
{
const now = Date.now();
if (now == this.lastTickTime)
return;
const maxOffset = this.mainMenu.size.right - this.submenu.size.left;
const offset = Math.min(this.MenuSpeed * (now - this.lastTickTime), maxOffset);
this.lastTickTime = now;
if (this.submenu.hidden || !offset)
{
delete this.mainMenu.onTick;
return;
}
this.submenu.size.left += offset;
this.submenu.size.right += offset;
}
}
/**
* Vertical size per button.
*/
MainMenuItemHandler.prototype.ButtonHeight = 28;
/**
* Distance between consecutive buttons.
*/
MainMenuItemHandler.prototype.Margin = 4;
/**
* Collapse / expansion speed in pixels per milliseconds used when animating the button menu size.
*/
MainMenuItemHandler.prototype.MenuSpeed = 1.2;