mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
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
173 lines
4.3 KiB
JavaScript
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;
|