Index: binaries/data/mods/public/gui/hotkeys/HotkeyPicker.js =================================================================== --- /dev/null +++ binaries/data/mods/public/gui/hotkeys/HotkeyPicker.js @@ -0,0 +1,61 @@ +/** + * Handle the interface to pick a hotkey combination. + * The player must keep a key combination for 2s in the input field for it to be registered. + */ +class HotkeyPicker +{ + constructor(onClose, name, mapping) + { + this.name = name; + + this.window = Engine.GetGUIObjectByName("hotkeyPicker"); + this.window.hidden = false; + + Engine.GetGUIObjectByName("hotkeyPickerDesc").caption = '' + + `Entering hotkey for ${name}.\nCurrent hotkey mapping is ${mapping}`; + + this.text = Engine.GetGUIObjectByName("hotkeyPickerText"); + this.text.caption = translate("Start pressing keys to pick a hotkey."); + this.input = Engine.GetGUIObjectByName("hotkeyPickerInput"); + this.input.focus(); + + this.combination = null; + + this.input.onKeyChange = keys => this.text.caption = keys.join("+"); + this.input.onCombination = keys => { + this.text.caption = translate(`Mapping: ${keys.join("+")}`); + this.input.blur(); + this.combination = keys; + + this.renderButtons(); + }; + + this.renderButtons(); + + Engine.GetGUIObjectByName("hotkeyPickerCancel").onPress = () => { + this.combination = null; + onClose(this); + }; + Engine.GetGUIObjectByName("hotkeyPickerAccept").onPress = () => onClose(this); + Engine.GetGUIObjectByName("hotkeyPickerRepick").onPress = () => { + this.input.focus(); + this.combination = null; + this.renderButtons(); + }; + + this.combination = null; + this.input.focus(); + } + + close() + { + this.window.hidden = true; + this.input.blur(); + } + + renderButtons() + { + Engine.GetGUIObjectByName("hotkeyPickerAccept").enabled = !!this.combination; + Engine.GetGUIObjectByName("hotkeyPickerRepick").enabled = !!this.combination; + } +} Index: binaries/data/mods/public/gui/hotkeys/HotkeysPage.js =================================================================== --- /dev/null +++ binaries/data/mods/public/gui/hotkeys/HotkeysPage.js @@ -0,0 +1,157 @@ +class HotkeysPage +{ + constructor() + { + this.categories = this.getHotkeyCategories(); + placeTabButtons(this.categories, 30, 4, index => this.selectPanel(index), ()=>{}); + Engine.GetGUIObjectByName("hotkeyList").onMouseLeftDoubleClickItem = () => { + let idx = Engine.GetGUIObjectByName("hotkeyList").selected; + new HotkeyPicker( + this.onHotkeyPicked.bind(this), + Engine.GetGUIObjectByName("hotkeyList").list_name[idx], + Engine.GetGUIObjectByName("hotkeyList").list_mapping[idx] + ); + }; + + Engine.GetGUIObjectByName("hotkeyCancel").onPress = () => Engine.PopGuiPage(); + Engine.GetGUIObjectByName("hotkeyReset").onPress = () => this.resetUserHotkeys(); + Engine.GetGUIObjectByName("hotkeySave").onPress = () => { + this.saveUserHotkeys(); + }; + this.selectPanel(0); + } + + hotkeySort(a, b) + { + const specialKeys = ["Shift", "Alt", "Ctrl", "Super", "Super"]; + // Quick hack to put those first. + if (specialKeys.indexOf(a) !== -1) + a = ' ' + a; + if (specialKeys.indexOf(b) !== -1) + b = ' ' + b; + return a.localeCompare(b, Engine.GetCurrentLocale().substr(0,2), { "numeric": true }); + } + + onHotkeyPicked(picker) + { + picker.close(); + if (!picker.combination) + return; + let panel = Engine.GetGUIObjectByName("hotkeyList"); + let idx = panel.list_name.indexOf(picker.name); + if (idx === -1 || this.selectedPanel === -1) + return; // Shouldn't happen. + + this.categories[this.selectedPanel].hotkeys[idx][1] = picker.combination; + this.setupPanel(this.categories[this.selectedPanel]); + } + + selectPanel(index) + { + this.selectedPanel = index; + selectPanel(this.selectedPanel); + this.setupPanel(this.categories[this.selectedPanel]); + } + + // Variant for tabulation + onPanelSetup(index) + { + this.selectedPanel = index; + this.setupPanel(this.categories[this.selectedPanel]); + } + + setupPanel(category) + { + let panel = Engine.GetGUIObjectByName("hotkeyList"); + + panel.list_name = category.hotkeys.map(x => x[0]); + panel.list_mapping = category.hotkeys.map(x => x[1].sort(this.hotkeySort).join("+")); + panel.list = category.hotkeys.map(() => 0); + panel.list_data = category.hotkeys.map(() => 0); + } + + getHotkeyCategories() + { + let hotkeys = Engine.GetHotkeyMap(); + let categories = { + "other": { + "label": translate("General hotkeys"), + "tooltip": translate("Hotkeys that don't fit in another namespace."), + "hotkeys": [] + } + }; + let n_categories = 1; + for (let hotkeyName in hotkeys) + { + let category = "other"; + let firstdot = hotkeyName.indexOf('.'); + if (firstdot !== -1) + category = hotkeyName.substr(0, firstdot); + if (!(category in categories)) + { + if (n_categories > 18) + category = "other"; + categories[category] = { + "label": category, + "tooltip": sprintf(translate("Hotkeys under the %s namespace"), category), + "hotkeys": [] + }; + } + categories[category].hotkeys.push([hotkeyName, hotkeys[hotkeyName]]); + } + // Remove categories that are too small to deserve a tab. + for (let cat of Object.keys(categories)) + if (categories[cat].hotkeys.length < 6) + { + categories.other.hotkeys = categories.other.hotkeys.concat(categories[cat].hotkeys); + delete categories[cat]; + } + for (let cat in categories) + categories[cat].hotkeys = categories[cat].hotkeys.sort(); + + // TODO SM52: Object.values() + let ret = []; + for (let cat in categories) + ret.push(categories[cat]); + return ret; + } + + resetUserHotkeys() + { + messageBox( + 400, 200, + translate("Reset all hotkeys to default values?\nThis will only take place if you click save."), + translate("Confirmation"), + [translate("No"), translate("Yes")], + [ + () => {}, + () => { + this.categories.forEach(cat => cat.hotkeys.forEach(([name, _]) => { + Engine.ConfigDB_RemoveValue("user", "hotkey." + name); + })); + this.categories = this.getHotkeyCategories(); + this.selectPanel(this.selectedPanel); + } + ]); + } + + saveUserHotkeys() + { + this.categories.forEach(cat => cat.hotkeys.forEach(([name, mapping]) => { + let keymap = mapping.sort(this.hotkeySort).join("+"); + Engine.ConfigDB_RemoveValue("user", "hotkey." + name); + let val = Engine.ConfigDB_GetValue("user", "hotkey." + name); + if (keymap !== val) + Engine.ConfigDB_CreateValue("user", "hotkey." + name, keymap); + })); + Engine.ConfigDB_WriteFile("user", "config/user.cfg"); + Engine.ReloadHotkeys(); + } +} + + +function init(data) +{ + let hotkeyPage = new HotkeysPage(data); + g_OnSelectTab = hotkeyPage.onPanelSetup.bind(hotkeyPage); +} Index: binaries/data/mods/public/gui/hotkeys/hotkeys.xml =================================================================== --- /dev/null +++ binaries/data/mods/public/gui/hotkeys/hotkeys.xml @@ -0,0 +1,92 @@ + + + + +