diff options
| author | 2021-10-31 12:31:16 +1100 | |
|---|---|---|
| committer | 2021-10-31 12:31:16 +1100 | |
| commit | d64ea5331b2312f81df464fb22dcef0191216d86 (patch) | |
| tree | a3f3784371da401e6a675e5b6b9734b88281a08d /plugins | |
| parent | cfffc54b61cabc5ef9533396bf87324eb5eeb63e (diff) | |
-frontend: Refactored menu event handling and fixed a number of issues. (#8777)
* Moved common code for drawing about box, info viewer, and other text box menus to a base class; removed the last of the info viewer logic and the multi-line item hack from the base menu class.
* Added previous/next group navigation for general inputs and plugin input selection menus.
* Moved message catalog logic to lib/util, allowing osd and emu to use localised messages.
* Made the base menu class use the UI manager’s feature for holding session state rather than a static map and mutex.
* Improved menu event handling model, and fixed many issues, particularly with menus behaving badly when hidden/shown.
* Added better support for menus that don’t participate in the usual menu stack, like the menuless sliders and the save/load state menus.
* Made a number of menus refresh state when being shown after being hidden (fixes MT08121 among other issues).
* Fixed indication of mounted slot option in the slot option details menu.
* Improved appearance of background menus when emulation isn't running - draw all menus in the stack, and darken the background menus to make the edges of the active menu clearer.
* Fixed locale issues in -listxml.
-debugger: Made GUI debuggers more uniform.
* Added new memory view features to Win32 debugger.
* Fixed spelling of hexadecimal in Cocoa debugger and added decimal address option.
* Fixed duplicate keyboard shortcut in Cocoa debugger (Shift-Cmd-D was both new device window and 64-bit float format).
* Made keyboard shortcuts slightly more consistent across debuggers.
-plugins: Moved input selection menu and sequence polling code to a common library. Fixed the issue that prevented keyboard inputs being mapped with -steadykey on.
-docs: Started adding some documentation for MAME's internal UI, and updated the list of example front-ends.
-Regenerated message catalog sources. For translators, the new strings are mostly:
* The names of the inputs provided by the OS-dependent layer for things like fullscreen and video features. These show up in the user interface inputs menu.
* The names for automatically generated views. These show up in the video options menu - test with a system with a lot of screens to see more variants.
* The input macro plugin UI.
* A few format strings for analog input assignments.
* A few strings for the about box header.
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/autofire/autofire_menu.lua | 233 | ||||
| -rw-r--r-- | plugins/autofire/init.lua | 4 | ||||
| -rw-r--r-- | plugins/cheat/init.lua | 64 | ||||
| -rw-r--r-- | plugins/commonui/init.lua | 214 | ||||
| -rw-r--r-- | plugins/commonui/plugin.json | 9 | ||||
| -rw-r--r-- | plugins/data/data_command.lua | 2 | ||||
| -rw-r--r-- | plugins/data/data_gameinit.lua | 2 | ||||
| -rw-r--r-- | plugins/data/data_hiscore.lua | 4 | ||||
| -rw-r--r-- | plugins/data/data_history.lua | 2 | ||||
| -rw-r--r-- | plugins/data/data_mameinfo.lua | 4 | ||||
| -rw-r--r-- | plugins/data/data_marp.lua | 2 | ||||
| -rw-r--r-- | plugins/data/data_messinfo.lua | 4 | ||||
| -rw-r--r-- | plugins/data/data_story.lua | 2 | ||||
| -rw-r--r-- | plugins/data/data_sysinfo.lua | 2 | ||||
| -rw-r--r-- | plugins/inputmacro/init.lua | 2 | ||||
| -rw-r--r-- | plugins/inputmacro/inputmacro_menu.lua | 199 | ||||
| -rw-r--r-- | plugins/timer/init.lua | 10 |
17 files changed, 411 insertions, 348 deletions
diff --git a/plugins/autofire/autofire_menu.lua b/plugins/autofire/autofire_menu.lua index 18000bfbd99..ec2fd6311cf 100644 --- a/plugins/autofire/autofire_menu.lua +++ b/plugins/autofire/autofire_menu.lua @@ -1,5 +1,8 @@ local lib = {} +-- Common UI helper library +local commonui + -- Set of all menus local MENU_TYPES = { MAIN = 0, EDIT = 1, ADD = 2, BUTTON = 3 } @@ -27,14 +30,17 @@ local configure_menu_active = false -- Saved selection on configure menu (to restore after button menu is dismissed) local configure_selection_save +-- Helper for polling for hotkeys +local hotkey_poller + -- Button being created/edited local current_button = {} -- Initial button to select when opening buttons menu local initial_input --- Inputs that can be autofired (to list in BUTTON menu) -local inputs +-- Handler for BUTTON menu +local input_menu -- Returns the section (from MENU_SECTIONS) and the index within that section local function menu_section(index) @@ -59,19 +65,14 @@ local function is_button_complete(button) return button.port and button.field and button.key and button.on_frames and button.off_frames and button.button and button.counter end -local function is_supported_input(ioport_field) - -- IPT_BUTTON1 through IPT_BUTTON16 in ioport_type enum (ioport.h) - return ioport_field.type >= 64 and ioport_field.type <= 79 -end - -- Main menu local function populate_main_menu(buttons) local ioport = manager.machine.ioport local input = manager.machine.input local menu = {} - menu[#menu + 1] = {_('Autofire buttons'), '', 'off'} - menu[#menu + 1] = {string.format(_('Press %s to delete'), input:seq_name(ioport:type_seq(ioport:token_to_input_type('UI_CLEAR')))), '', 'off'} + menu[#menu + 1] = {_p('plugin-autofire', 'Autofire buttons'), '', 'off'} + menu[#menu + 1] = {string.format(_p('plugin-autofire', 'Press %s to delete'), manager.ui:get_general_input_setting(ioport:token_to_input_type('UI_CLEAR'))), '', 'off'} menu[#menu + 1] = {'---', '', ''} header_height = #menu @@ -82,22 +83,26 @@ local function populate_main_menu(buttons) freq = 1 / screen.frame_period end - for index, button in ipairs(buttons) do - -- Round rate to two decimal places - local rate = freq / (button.on_frames + button.off_frames) - rate = math.floor(rate * 100) / 100 - local text = string.format(_('%s [%g Hz]'), _p('input-name', button.button.name), rate) - local subtext = input:seq_name(button.key) - menu[#menu + 1] = {text, subtext, ''} - if index == initial_button then - main_selection_save = #menu + if #buttons > 0 then + for index, button in ipairs(buttons) do + -- Round rate to two decimal places + local rate = freq / (button.on_frames + button.off_frames) + rate = math.floor(rate * 100) / 100 + local text = string.format(_p('plugin-autofire', '%s [%g Hz]'), _p('input-name', button.button.name), rate) + local subtext = input:seq_name(button.key) + menu[#menu + 1] = {text, subtext, ''} + if index == initial_button then + main_selection_save = #menu + end end + else + menu[#menu + 1] = {_p('plugin-autofire', '[no autofire buttons]'), '', 'off'} end initial_button = nil content_height = #menu menu[#menu + 1] = {'---', '', ''} - menu[#menu + 1] = {_('Add autofire button'), '', ''} + menu[#menu + 1] = {_p('plugin-autofire', 'Add autofire button'), '', ''} local selection = main_selection_save main_selection_save = nil @@ -134,67 +139,53 @@ end -- Add/edit menus (mostly identical) local function populate_configure_menu(menu) - local button_name = current_button.button and _p('input-name', current_button.button.name) or _('NOT SET') - local key_name = current_button.key and manager.machine.input:seq_name(current_button.key) or _('NOT SET') - menu[#menu + 1] = {_('Input'), button_name, ''} + local button_name = current_button.button and _p('input-name', current_button.button.name) or _p('plugin-autofire', '[not set]') + local key_name = current_button.key and manager.machine.input:seq_name(current_button.key) or _p('plugin-autofire', '[not set]') + menu[#menu + 1] = {_p('plugin-autofire', 'Input'), button_name, ''} if not (configure_menu_active or configure_selection_save) then configure_selection_save = #menu end - menu[#menu + 1] = {_('Hotkey'), key_name, ''} - menu[#menu + 1] = {_('On frames'), current_button.on_frames, current_button.on_frames > 1 and 'lr' or 'r'} - menu[#menu + 1] = {_('Off frames'), current_button.off_frames, current_button.off_frames > 1 and 'lr' or 'r'} + menu[#menu + 1] = {_p('plugin-autofire', 'Hotkey'), key_name, hotkey_poller and 'lr' or ''} + menu[#menu + 1] = {_p('plugin-autofire', 'On frames'), current_button.on_frames, current_button.on_frames > 1 and 'lr' or 'r'} + menu[#menu + 1] = {_p('plugin-autofire', 'Off frames'), current_button.off_frames, current_button.off_frames > 1 and 'lr' or 'r'} configure_menu_active = true end --- Borrowed from the cheat plugin -local function poll_for_hotkey() - local input = manager.machine.input - local poller = input:switch_sequence_poller() - manager.machine:popmessage(_('Press button for hotkey or wait to leave unchanged')) - manager.machine.video:frame_update() - poller:start() - local time = os.clock() - local clearmsg = true - while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do - if poller.modified then - if not poller.valid then - manager.machine:popmessage(_('Invalid sequence entered')) - clearmsg = false - break +local function handle_configure_menu(index, event) + if hotkey_poller then + -- special handling for polling for hotkey + if hotkey_poller:poll() then + if hotkey_poller.sequence then + current_button.key = hotkey_poller.sequence end - manager.machine:popmessage(input:seq_name(poller.sequence)) - manager.machine.video:frame_update() + hotkey_poller = nil + return true end + return false end - if clearmsg then - manager.machine:popmessage() - end - return poller.valid and poller.sequence or nil -end -local function handle_configure_menu(index, event) if index == 1 then -- Input if event == 'select' then configure_selection_save = header_height + index table.insert(menu_stack, MENU_TYPES.BUTTON) if current_button.port and current_button.field then - initial_input = {port_name = current_button.port, ioport_field = current_button.button} + initial_input = current_button.button end return true end elseif index == 2 then -- Hotkey if event == 'select' then - local keycode = poll_for_hotkey() - if keycode then - current_button.key = keycode - return true + if not commonui then + commonui = require('commonui') end + hotkey_poller = commonui.switch_polling_helper() + return true end elseif index == 3 then -- On frames - manager.machine:popmessage(_('Number of frames button will be pressed')) + manager.machine:popmessage(_p('plugin-autofire', 'Number of frames button will be pressed')) if event == 'left' then current_button.on_frames = current_button.on_frames - 1 return true @@ -207,7 +198,7 @@ local function handle_configure_menu(index, event) end elseif index == 4 then -- Off frames - manager.machine:popmessage(_('Number of frames button will be released')) + manager.machine:popmessage(_p('plugin-autofire', 'Number of frames button will be released')) if event == 'left' then current_button.off_frames = current_button.off_frames - 1 return true @@ -224,7 +215,7 @@ end local function populate_edit_menu() local menu = {} - menu[#menu + 1] = {_('Edit autofire button'), '', 'off'} + menu[#menu + 1] = {_p('plugin-autofire', 'Edit autofire button'), '', 'off'} menu[#menu + 1] = {'---', '', ''} header_height = #menu @@ -232,17 +223,20 @@ local function populate_edit_menu() content_height = #menu menu[#menu + 1] = {'---', '', ''} - menu[#menu + 1] = {_('Done'), '', ''} + menu[#menu + 1] = {_p('plugin-autofire', 'Done'), '', ''} local selection = configure_selection_save configure_selection_save = nil - return menu, selection, 'lrrepeat' + if hotkey_poller then + return hotkey_poller:overlay(menu, selection, 'lrrepeat') + else + return menu, selection, 'lrrepeat' + end end local function handle_edit_menu(index, event, buttons) local section, adjusted_index = menu_section(index) if ((section == MENU_SECTIONS.FOOTER) and (event == 'select')) or (event == 'cancel') then - inputs = nil configure_menu_active = false table.remove(menu_stack) return true @@ -254,7 +248,7 @@ end local function populate_add_menu() local menu = {} - menu[#menu + 1] = {_('Add autofire button'), '', 'off'} + menu[#menu + 1] = {_p('plugin-autofire', 'Add autofire button'), '', 'off'} menu[#menu + 1] = {'---', '', ''} header_height = #menu @@ -263,20 +257,23 @@ local function populate_add_menu() menu[#menu + 1] = {'---', '', ''} if is_button_complete(current_button) then - menu[#menu + 1] = {_('Create'), '', ''} + menu[#menu + 1] = {_p('plugin-autofire', 'Create'), '', ''} else - menu[#menu + 1] = {_('Cancel'), '', ''} + menu[#menu + 1] = {_p('plugin-autofire', 'Cancel'), '', ''} end local selection = configure_selection_save configure_selection_save = nil - return menu, selection, 'lrrepeat' + if hotkey_poller then + return hotkey_poller:overlay(menu, selection, 'lrrepeat') + else + return menu, selection, 'lrrepeat' + end end local function handle_add_menu(index, event, buttons) local section, adjusted_index = menu_section(index) if ((section == MENU_SECTIONS.FOOTER) and (event == 'select')) or (event == 'cancel') then - inputs = nil configure_menu_active = false table.remove(menu_stack) if is_button_complete(current_button) and (event == 'select') then @@ -293,103 +290,31 @@ end -- Button selection menu local function populate_button_menu() - local ioport = manager.machine.ioport - menu = {} - menu[#menu + 1] = {_('Select an input for autofire'), '', 'off'} - menu[#menu + 1] = {'---', '', ''} - header_height = #menu - - if not inputs then - inputs = {} - - for port_key, port in pairs(ioport.ports) do - for field_key, field in pairs(port.fields) do - if is_supported_input(field) then - inputs[#inputs + 1] = { - port_name = port_key, - field_name = field_key, - ioport_field = field - } - end - end - end - - local function compare(x, y) - if x.ioport_field.device.tag < y.ioport_field.device.tag then - return true - elseif x.ioport_field.device.tag > y.ioport_field.device.tag then - return false - end - groupx = ioport:type_group(x.ioport_field.type, x.ioport_field.player) - groupy = ioport:type_group(y.ioport_field.type, y.ioport_field.player) - if groupx < groupy then - return true - elseif groupx > groupy then - return false - elseif x.ioport_field.type < y.ioport_field.type then - return true - elseif x.ioport_field.type > y.ioport_field.type then - return false - else - return x.ioport_field.name < y.ioport_field.name - end - end - table.sort(inputs, compare) - - local i = 1 - local prev - while i <= #inputs do - local current = inputs[i] - if (not prev) or (prev.ioport_field.device.tag ~= current.ioport_field.device.tag) then - table.insert(inputs, i, false) - i = i + 2 - else - i = i + 1 - end - prev = current - end + local function is_supported_input(ioport_field) + -- IPT_BUTTON1 through IPT_BUTTON16 in ioport_type enum (ioport.h) + return ioport_field.type >= 64 and ioport_field.type <= 79 end - local selection = header_height + 1 - for i, input in ipairs(inputs) do - if input then - menu[header_height + i] = { _p('input-name', input.ioport_field.name), '', '' } - if initial_input and (initial_input.port_name == input.port_name) and (initial_input.ioport_field.mask == input.ioport_field.mask) and (initial_input.ioport_field.type == input.ioport_field.type) then - selection = header_height + i - initial_input = nil - end - else - local device = inputs[i + 1].ioport_field.device - if device.owner then - menu[header_height + i] = {string.format(_('%s [root%s]'), device.name, device.tag), '', 'heading'} - else - menu[header_height + i] = {string.format(_('[root%s]'), device.tag), '', 'heading'} - end + local function action(field) + if field then + current_button.port = field.port.tag + current_button.field = field.name + current_button.button = field end + initial_input = nil + input_menu = nil + table.remove(menu_stack) end - content_height = #menu - initial_input = nil - - menu[#menu + 1] = {'---', '', ''} - menu[#menu + 1] = {_('Cancel'), '', ''} - return menu, selection + if not commonui then + commonui = require('commonui') + end + input_menu = commonui.input_selection_menu(action, _p('plugin-autofire', 'Select an input for autofire'), is_supported_input) + return input_menu:populate(initial_input) end local function handle_button_menu(index, event) - local section, adjusted_index = menu_section(index) - if ((section == MENU_SECTIONS.FOOTER) and (event == 'select')) or (event == 'cancel') then - table.remove(menu_stack) - return true - elseif (section == MENU_SECTIONS.CONTENT) and (event == 'select') then - local selected_input = inputs[adjusted_index] - current_button.port = selected_input.port_name - current_button.field = selected_input.field_name - current_button.button = selected_input.ioport_field - table.remove(menu_stack) - return true - end - return false + return input_menu:handle(index, event) end function lib:init_menu(buttons) @@ -397,7 +322,7 @@ function lib:init_menu(buttons) content_height = 0 menu_stack = { MENU_TYPES.MAIN } current_button = {} - inputs = nil + input_menu = nil end function lib:populate_menu(buttons) diff --git a/plugins/autofire/init.lua b/plugins/autofire/init.lua index 57ea3d4bb27..d93bf854168 100644 --- a/plugins/autofire/init.lua +++ b/plugins/autofire/init.lua @@ -100,14 +100,14 @@ function autofire.startplugin() if menu_handler then return menu_handler:populate_menu(buttons) else - return {{_('Failed to load autofire menu'), '', ''}} + return {{_p('plugin-autofire', 'Failed to load autofire menu'), '', 'off'}} end end emu.register_frame_done(process_frame) emu.register_start(load_settings) emu.register_stop(save_settings) - emu.register_menu(menu_callback, menu_populate, _('Autofire')) + emu.register_menu(menu_callback, menu_populate, _p('plugin-autofire', 'Autofire')) end return exports diff --git a/plugins/cheat/init.lua b/plugins/cheat/init.lua index e5fc2245922..e3bf6bac56b 100644 --- a/plugins/cheat/init.lua +++ b/plugins/cheat/init.lua @@ -597,58 +597,56 @@ function cheat.startplugin() local hotkeymenu = false local hotkeylist = {} + local commonui + local poller local function menu_populate() local menu = {} if hotkeymenu then local ioport = manager.machine.ioport local input = manager.machine.input + menu[1] = {_("Select cheat to set hotkey"), "", "off"} - menu[2] = {string.format(_("Press %s to clear hotkey"), input:seq_name(ioport:type_seq(ioport:token_to_input_type("UI_CLEAR")))), "", "off"} + menu[2] = {string.format(_("Press %s to clear hotkey"), manager.ui:get_general_input_setting(ioport:token_to_input_type("UI_CLEAR"))), "", "off"} menu[3] = {"---", "", "off"} hotkeylist = {} local function hkcbfunc(cheat, event) - if event == "clear" then - cheat.hotkeys = nil - return - end - - local poller = input:switch_sequence_poller() - manager.machine:popmessage(_("Press button for hotkey or wait to leave unchanged")) - manager.machine.video:frame_update() - poller:start() - local time = os.clock() - local clearmsg = true - while (not poller:poll()) and (poller.modified or (os.clock() < time + 1)) do - if poller.modified then - if not poller.valid then - manager.machine:popmessage(_("Invalid sequence entered")) - clearmsg = false - break + if poller then + if poller:poll() then + if poller.sequence then + cheat.hotkeys = { pressed = false, keys = poller.sequence } end - manager.machine:popmessage(input:seq_name(poller.sequence)) - manager.machine.video:frame_update() + poller = nil + return true end + elseif event == "clear" then + cheat.hotkeys = nil + return true + elseif event == "select" then + if not commonui then + commonui = require('commonui') + end + poller = commonui.switch_polling_helper() + return true end - if poller.modified and poller.valid then - cheat.hotkeys = { pressed = false, keys = poller.sequence } - end - if clearmsg then - manager.machine:popmessage() - end - manager.machine.video:frame_update() + return false end for num, cheat in ipairs(cheats) do if cheat.script then - menu[#menu + 1] = {cheat.desc, cheat.hotkeys and input:seq_name(cheat.hotkeys.keys) or _("None"), ""} + local setting = cheat.hotkeys and input:seq_name(cheat.hotkeys.keys) or _("None") + menu[#menu + 1] = {cheat.desc, setting, ""} hotkeylist[#hotkeylist + 1] = function(event) return hkcbfunc(cheat, event) end end end menu[#menu + 1] = {"---", "", ""} menu[#menu + 1] = {_("Done"), "", ""} - return menu + if poller then + return poller:overlay(menu) + else + return menu + end end for num, cheat in ipairs(cheats) do menu[num] = {} @@ -703,7 +701,10 @@ function cheat.startplugin() local function menu_callback(index, event) manager.machine:popmessage() if hotkeymenu then - if event == "select" or event == "clear" then + if event == "cancel" then + hotkeymenu = false + return true + else index = index - 3 if index >= 1 and index <= #hotkeylist then hotkeylist[index](event) @@ -712,9 +713,6 @@ function cheat.startplugin() hotkeymenu = false return true end - elseif event == "cancel" then - hotkeymenu = false - return true end return false end diff --git a/plugins/commonui/init.lua b/plugins/commonui/init.lua new file mode 100644 index 00000000000..26b65a5e1b5 --- /dev/null +++ b/plugins/commonui/init.lua @@ -0,0 +1,214 @@ +-- license:BSD-3-Clause +-- copyright-holders:Vas Crabb +local exports = { + name = 'commonui', + version = '0.0.1', + description = 'Common plugin UI helpers', + license = 'BSD-3-Clause', + author = { name = 'Vas Crabb' } } + + +local commonui = exports + + +function commonui.input_selection_menu(action, title, filter) + menu = { } + + local choices + local index_first_choice + local index_cancel + + local function populate_choices() + local ioport = manager.machine.ioport + + local function compare(a, b) + if a.device.tag < b.device.tag then + return true + elseif a.device.tag > b.device.tag then + return false + end + groupa = ioport:type_group(a.type, a.player) + groupb = ioport:type_group(b.type, b.player) + if groupa < groupb then + return true + elseif groupa > groupb then + return false + elseif a.type < b.type then + return true + elseif a.type > b.type then + return false + else + return a.name < b.name + end + end + + choices = { } + for tag, port in pairs(manager.machine.ioport.ports) do + for name, field in pairs(port.fields) do + if (not filter) or filter(field) then + table.insert(choices, field) + end + end + end + table.sort(choices, compare) + + local index = 1 + local prev + while index <= #choices do + local current = choices[index] + if (not prev) or (prev.device.tag ~= current.device.tag) then + table.insert(choices, index, false) + index = index + 2 + else + index = index + 1 + end + prev = current + end + end + + function menu:populate(initial_selection) + if not choices then + populate_choices() + end + + local items = { } + + if title then + table.insert(items, { title, '', 'off' }) + table.insert(items, { '---', '', '' }) + end + + index_first_choice = #items + 1 + local selection = index_first_choice + for index, field in ipairs(choices) do + if field then + table.insert(items, { _p('input-name', field.name), '', '' }) + if initial_selection and (field.port.tag == initial_selection.port.tag) and (field.mask == initial_selection.mask) and (field.type == initial_selection.type) then + selection = #items + initial_selection = nil + end + else + local device = choices[index + 1].device + if device.owner then + table.insert(items, { string.format(_p('plugin-commonui', '%s [root%s]'), device.name, device.tag), '', 'heading' }) + else + table.insert(items, { string.format(_p('plugin-commonui', '[root%s]'), device.tag), '', 'heading' }) + end + end + end + + table.insert(items, { '---', '', '' }) + table.insert(items, { _p('plugin-commonui', 'Cancel'), '', '' }) + index_cancel = #items + + return items, selection + end + + function menu:handle(index, event) + local selection + if (event == 'cancel') or ((index == input_item_cancel) and (event == 'select')) then + action(nil) + return true + elseif event == 'select' then + local field = choices[index - index_first_choice + 1] + if field then + action(field) + return true + end + elseif event == 'prevgroup' then + local found_break = false + while (index > index_first_choice) and (not selection) do + index = index - 1 + if not choices[index - index_first_choice + 1] then + if found_break then + selection = index + 1 + else + found_break = true + end + end + end + elseif event == 'nextgroup' then + while ((index - index_first_choice + 2) < #choices) and (not selection) do + index = index + 1 + if not choices[index - index_first_choice + 1] then + selection = index + 1 + end + end + end + return false, selection + end + + return menu +end + + +function commonui.switch_polling_helper(starting_sequence) + helper = { } + + local machine = manager.machine + local cancel = machine.ioport:token_to_input_type('UI_CANCEL') + local cancel_prompt = manager.ui:get_general_input_setting(cancel) + local input = machine.input + local uiinput = machine.uiinput + local poller = input:switch_sequence_poller() + local modified_ticks = 0 + + if starting_sequence then + poller:start(starting_sequence) + else + poller:start() + end + + function helper:overlay(items, selection, flags) + if flags then + flags = flags .. " nokeys" + else + flags = "nokeys" + end + return items, selection, flags + end + + function helper:poll() + -- prevent race condition between uiinput:pressed() and poll() + if (modified_ticks == 0) and poller.modified then + modified_ticks = emu.osd_ticks() + end + + if uiinput:pressed(cancel) then + -- UI_CANCEL pressed, abort + machine:popmessage() + if (not poller.modified) or (modified_ticks == emu.osd_ticks()) then + -- cancelled immediately + self.sequence = nil + return true -- TODO: communicate this better? + else + -- entered something before cancelling + self.sequence = nil + return true + end + elseif poller:poll() then + if poller.valid then + -- valid sequence entered + machine:popmessage() + self.sequence = poller.sequence + return true + else + -- invalid sequence entered + machine:popmessage(_p('plugin-commonui', 'Invalid sequence entered')) + self.sequence = nil + return true + end + else + machine:popmessage(string.format( + _p('plugin-commonui', 'Enter sequence or press %s to cancel\n%s'), + cancel_prompt, + input:seq_name(poller.sequence))) + return false + end + end + + return helper +end + + +return exports diff --git a/plugins/commonui/plugin.json b/plugins/commonui/plugin.json new file mode 100644 index 00000000000..b826f11f864 --- /dev/null +++ b/plugins/commonui/plugin.json @@ -0,0 +1,9 @@ +{ + "plugin": { + "name": "commonui", + "description": "Common plugin UI helpers", + "version": "0.0.1", + "author": "Vas Crabb", + "type": "library" + } +} diff --git a/plugins/data/data_command.lua b/plugins/data/data_command.lua index de2483bcdc9..cd265d25b79 100644 --- a/plugins/data/data_command.lua +++ b/plugins/data/data_command.lua @@ -19,7 +19,7 @@ function dat.check(set, softlist) if not status or not info then return nil end - return _("Command") + return _p("plugin-data", "Command") end function dat.get() diff --git a/plugins/data/data_gameinit.lua b/plugins/data/data_gameinit.lua index 7b93334e2ce..31e659325c4 100644 --- a/plugins/data/data_gameinit.lua +++ b/plugins/data/data_gameinit.lua @@ -13,7 +13,7 @@ function dat.check(set, softlist) if not status or not info then return nil end - return _("Gameinit") + return _p("plugin-data", "Gameinit") end function dat.get() diff --git a/plugins/data/data_hiscore.lua b/plugins/data/data_hiscore.lua index 0f54bb43384..febfe28d0f9 100644 --- a/plugins/data/data_hiscore.lua +++ b/plugins/data/data_hiscore.lua @@ -1217,7 +1217,7 @@ function dat.check(set, softlist) if curset == set then if output then - return _("High Scores") + return _p("plugin-data", "High Scores") else return nil end @@ -1265,7 +1265,7 @@ function dat.check(set, softlist) end end if output then - return _("High Scores") + return _p("plugin-data", "High Scores") else return nil end diff --git a/plugins/data/data_history.lua b/plugins/data/data_history.lua index 2671d5a065d..e6fd4eb2509 100644 --- a/plugins/data/data_history.lua +++ b/plugins/data/data_history.lua @@ -169,7 +169,7 @@ function dat.check(set, softlist) info = stmt:get_value(0) end stmt:finalize() - return info and _("History") or nil + return info and _p("plugin-data", "History") or nil end function dat.get() diff --git a/plugins/data/data_mameinfo.lua b/plugins/data/data_mameinfo.lua index 4ace7ba3f2a..7a6e6ea2def 100644 --- a/plugins/data/data_mameinfo.lua +++ b/plugins/data/data_mameinfo.lua @@ -15,9 +15,9 @@ function dat.check(set, softlist) local sourcefile = emu.driver_find(set).source_file:match("[^/\\]*$") status, drvinfo = pcall(datread, "drv", "info", sourcefile) if drvinfo then - info = info .. _("\n\n--- DRIVER INFO ---\nDriver: ") .. sourcefile .. "\n\n" .. drvinfo + info = info .. _p("plugin-data", "\n\n--- DRIVER INFO ---\nDriver: ") .. sourcefile .. "\n\n" .. drvinfo end - return _("MAMEinfo") + return _p("plugin-data", "MAMEinfo") end function dat.get() diff --git a/plugins/data/data_marp.lua b/plugins/data/data_marp.lua index 98e6e8160d4..47a45785a81 100644 --- a/plugins/data/data_marp.lua +++ b/plugins/data/data_marp.lua @@ -133,7 +133,7 @@ function dat.check(set, softlist) info = "#j2\n" .. stmt:get_value(0) end stmt:finalize() - return info and _("MARPScore") or nil + return info and _p("plugin-data", "MARPScore") or nil end function dat.get() diff --git a/plugins/data/data_messinfo.lua b/plugins/data/data_messinfo.lua index b679763d940..fe2099890ee 100644 --- a/plugins/data/data_messinfo.lua +++ b/plugins/data/data_messinfo.lua @@ -16,9 +16,9 @@ function dat.check(set, softlist) local sourcefile = emu.driver_find(set).source_file:match("[^/\\]*$") status, drvinfo = pcall(datread, "drv", "info", sourcefile) if drvinfo then - info = info .. _("\n\n--- DRIVER INFO ---\nDriver: ") .. sourcefile .. "\n\n" .. drvinfo + info = info .. _p("plugin-data", "\n\n--- DRIVER INFO ---\nDriver: ") .. sourcefile .. "\n\n" .. drvinfo end - return _("MESSinfo") + return _p("plugin-data", "MESSinfo") end function dat.get() diff --git a/plugins/data/data_story.lua b/plugins/data/data_story.lua index 92b579f9ecb..70bedd02df0 100644 --- a/plugins/data/data_story.lua +++ b/plugins/data/data_story.lua @@ -19,7 +19,7 @@ function dat.check(set, softlist) end end info = "#j2\n" .. table.concat(lines, "\n") - return _("Mamescore") + return _p("plugin-data", "Mamescore") end function dat.get() diff --git a/plugins/data/data_sysinfo.lua b/plugins/data/data_sysinfo.lua index 8010ff854e0..cb2daaa03a0 100644 --- a/plugins/data/data_sysinfo.lua +++ b/plugins/data/data_sysinfo.lua @@ -12,7 +12,7 @@ function dat.check(set, softlist) if not status or not info then return nil end - return _("Sysinfo") + return _p("plugin-data", "Sysinfo") end function dat.get() diff --git a/plugins/inputmacro/init.lua b/plugins/inputmacro/init.lua index 99e3d61f1d4..4897abee1f5 100644 --- a/plugins/inputmacro/init.lua +++ b/plugins/inputmacro/init.lua @@ -127,7 +127,7 @@ function inputmacro.startplugin() emu.register_frame_done(process_frame) emu.register_start(start) emu.register_stop(stop) - emu.register_menu(menu_callback, menu_populate, _('Input Macros')) + emu.register_menu(menu_callback, menu_populate, _p('plugin-inputmacro', 'Input Macros')) end return exports diff --git a/plugins/inputmacro/inputmacro_menu.lua b/plugins/inputmacro/inputmacro_menu.lua index 7c03d5b95a7..b6b8c9413c0 100644 --- a/plugins/inputmacro/inputmacro_menu.lua +++ b/plugins/inputmacro/inputmacro_menu.lua @@ -9,6 +9,7 @@ local MENU_TYPES = { MACROS = 0, ADD = 1, EDIT = 2, INPUT = 3 } -- Globals +local commonui local macros local menu_stack @@ -51,118 +52,43 @@ end -- Input menu -local input_action +local input_menu local input_start_field -local input_choices -local input_item_first_choice -local input_item_cancel -function handle_input(index, action) - if (action == 'cancel') or ((index == input_item_cancel) and (action == 'select')) then - table.remove(menu_stack) - input_action = nil - return true - elseif action == 'select' then - field = input_choices[index - input_item_first_choice + 1] - if field then - table.remove(menu_stack) - input_action(field) - input_action = nil +function start_input_menu(handler, start_field) + local function supported(f) + if f.is_analog or f.is_toggle then + return false + elseif (f.type_class == 'config') or (f.type_class == 'dipswitch') then + return false + else return true end end - return false -end -function populate_input() - local items = { } - - items[#items + 1] = { _p('plugin-inputmacro', 'Set Input'), '', 'off' } - items[#items + 1] = { '---', '', '' } - - if not input_choices then - local ioport = manager.machine.ioport - - local function supported(f) - if f.is_analog or f.is_toggle then - return false - elseif (f.type_class == 'config') or (f.type_class == 'dipswitch') then - return false - else - return true - end - end - - local function compare(a, b) - if a.device.tag < b.device.tag then - return true - elseif a.device.tag > b.device.tag then - return false - end - groupa = ioport:type_group(a.type, a.player) - groupb = ioport:type_group(b.type, b.player) - if groupa < groupb then - return true - elseif groupa > groupb then - return false - elseif a.type < b.type then - return true - elseif a.type > b.type then - return false - else - return a.name < b.name - end - end - - input_choices = { } - for tag, port in pairs(manager.machine.ioport.ports) do - for name, field in pairs(port.fields) do - if supported(field) then - table.insert(input_choices, field) - end - end - end - table.sort(input_choices, compare) - - local index = 1 - local prev - while index <= #input_choices do - local current = input_choices[index] - if (not prev) or (prev.device.tag ~= current.device.tag) then - table.insert(input_choices, index, false) - index = index + 2 - else - index = index + 1 - end - prev = current + local function action(field) + if field then + handler(field) end + table.remove(menu_stack) + input_menu = nil + input_start_field = nil end - input_item_first_choice = #items + 1 - local selection = input_item_first_choice - for index, field in ipairs(input_choices) do - if field then - items[#items + 1] = { _p('input-name', field.name), '', '' } - if input_start_field and (field.port.tag == input_start_field.port.tag) and (field.mask == input_start_field.mask) and (field.type == input_start_field.type) then - selection = #items - input_start_field = nil - end - else - local device = input_choices[index + 1].device - if device.owner then - items[#items + 1] = { string.format(_('plugin-inputmacro', '%s [root%s]'), device.name, device.tag), '', 'heading' } - else - items[#items + 1] = { string.format(_('plugin-inputmacro', '[root%s]'), device.tag), '', 'heading' } - end - end + if not commonui then + commonui = require('commonui') end - input_start_field = nil + input_menu = commonui.input_selection_menu(action, _p('plugin-inputmacro', 'Set Input'), supported) + input_start_field = start_field + table.insert(menu_stack, MENU_TYPES.INPUT) +end - items[#items + 1] = { '---', '', '' } - items[#items + 1] = { _p('plugin-inputmacro', 'Cancel'), '', '' } - input_item_cancel = #items +function handle_input(index, action) + return input_menu:handle(index, action) +end - return items, selection +function populate_input() + return input_menu:populate(input_start_field) end @@ -176,6 +102,7 @@ local edit_insert_position local edit_name_buffer local edit_items local edit_item_exit +local edit_switch_poller local function current_macro_complete() if not edit_current_macro.binding then @@ -188,36 +115,18 @@ local function current_macro_complete() return true end -local function set_binding() - local input = manager.machine.input - local poller = input:switch_sequence_poller() - manager.machine:popmessage(_p('plugin-inputmacro', 'Enter activation sequence or wait to leave unchanged')) - manager.machine.video:frame_update() - poller:start() - local time = os.clock() - local clearmsg = true - while (not poller:poll()) and (poller.modified or (os.clock() < (time + 1))) do - if poller.modified then - if not poller.valid then - manager.machine:popmessage(_p('plugin-inputmacro', 'Invalid sequence entered')) - clearmsg = false - break +local function handle_edit_items(index, event) + if edit_switch_poller then + if edit_switch_poller:poll() then + if edit_switch_poller.sequence then + edit_current_macro.binding = edit_switch_poller.sequence end - manager.machine:popmessage(input:seq_name(poller.sequence)) - manager.machine.video:frame_update() + edit_switch_poller = nil + return true end + return false end - if clearmsg then - manager.machine:popmessage() - end - if poller.valid then - edit_current_macro.binding = poller.sequence - return true - end - return false -end -local function handle_edit_items(index, event) local command = edit_items[index] local namecancel = false @@ -275,7 +184,11 @@ local function handle_edit_items(index, event) end elseif command.action == 'binding' then if event == 'select' then - return set_binding() + if not commonui then + commonui = require('commonui') + end + edit_switch_poller = commonui.switch_polling_helper() + return true end elseif command.action == 'releaseaction' then if (event == 'select') or (event == 'left') or (event == 'right') then @@ -320,14 +233,13 @@ local function handle_edit_items(index, event) elseif command.action == 'input' then local inputs = edit_current_macro.steps[command.step].inputs if event == 'select' then - input_action = + local hanlder = function(field) inputs[command.input].port = field.port inputs[command.input].field = field end - input_start_field = inputs[command.input].field + start_input_menu(hanlder, inputs[command.input].field) edit_start_selection = index - table.insert(menu_stack, MENU_TYPES.INPUT) return true elseif event == 'clear' then if #inputs > 1 then @@ -338,14 +250,14 @@ local function handle_edit_items(index, event) elseif command.action == 'addinput' then if event == 'select' then local inputs = edit_current_macro.steps[command.step].inputs - input_action = + local handler = function(field) inputs[#inputs + 1] = { port = field.port, field = field } end + start_input_menu(handler) edit_start_selection = index - table.insert(menu_stack, MENU_TYPES.INPUT) return true end elseif command.action == 'deletestep' then @@ -368,7 +280,7 @@ local function handle_edit_items(index, event) elseif command.action == 'addstep' then if event == 'select' then local steps = edit_current_macro.steps - input_action = + local handler = function(field) local newstep = { inputs = { @@ -384,8 +296,8 @@ local function handle_edit_items(index, event) edit_start_step = edit_insert_position edit_insert_position = edit_insert_position + 1 end + start_input_menu(handler) edit_start_selection = index - table.insert(menu_stack, MENU_TYPES.INPUT) return true elseif event == 'left' then edit_insert_position = edit_insert_position - 1 @@ -413,7 +325,7 @@ local function add_edit_items(items) local binding = edit_current_macro.binding local activation = binding and input:seq_name(binding) or _p('plugin-inputmacro', '[not set]') - items[#items + 1] = { _p('plugin-inputmacro', 'Activation sequence'), activation, '' } + items[#items + 1] = { _p('plugin-inputmacro', 'Activation sequence'), activation, edit_switch_poller and 'lr' or '' } edit_items[#items] = { action = 'binding' } local releaseaction = edit_current_macro.earlycancel and _p('plugin-inputmacro', 'Stop immediately') or _p('plugin-inputmacro', 'Complete macro') @@ -484,7 +396,6 @@ local function handle_add(index, event) if handle_edit_items(index, event) then return true elseif event == 'cancel' then - input_choices = nil edit_current_macro = nil edit_menu_active = false edit_items = nil @@ -495,7 +406,6 @@ local function handle_add(index, event) table.insert(macros, edit_current_macro) macros_start_macro = #macros end - input_choices = nil edit_menu_active = false edit_current_macro = nil edit_items = nil @@ -509,7 +419,6 @@ local function handle_edit(index, event) if handle_edit_items(index, event) then return true elseif (event == 'cancel') or ((index == edit_item_exit) and (event == 'select')) then - input_choices = nil edit_current_macro = nil edit_menu_active = false edit_items = nil @@ -537,7 +446,11 @@ local function populate_add() local selection = edit_start_selection edit_start_selection = nil - return items, selection, 'lrrepeat' + if edit_switch_poller then + return edit_switch_poller:overlay(items, selection, 'lrrepeat') + else + return items, selection, 'lrrepeat' + end end local function populate_edit() @@ -554,7 +467,11 @@ local function populate_edit() local selection = edit_start_selection edit_start_selection = nil - return items, selection, 'lrrepeat' + if edit_switch_poller then + return edit_switch_poller:overlay(items, selection, 'lrrepeat') + else + return items, selection, 'lrrepeat' + end end @@ -601,7 +518,7 @@ function populate_macros() local items = { } items[#items + 1] = { _p('plugin-inputmacro', 'Input Macros'), '', 'off' } - items[#items + 1] = { string.format(_p('plugin-inputmacro', 'Press %s to delete'), input:seq_name(ioport:type_seq(ioport:token_to_input_type('UI_CLEAR')))), '', 'off' } + items[#items + 1] = { string.format(_p('plugin-inputmacro', 'Press %s to delete'), manager.ui:get_general_input_setting(ioport:token_to_input_type('UI_CLEAR'))), '', 'off' } items[#items + 1] = { '---', '', '' } macros_item_first_macro = #items + 1 diff --git a/plugins/timer/init.lua b/plugins/timer/init.lua index 9457c1fc038..19755f7a348 100644 --- a/plugins/timer/init.lua +++ b/plugins/timer/init.lua @@ -86,7 +86,7 @@ function timer.startplugin() local hrs = math.floor(time / 3600) local min = math.floor((time % 3600) / 60) local sec = time % 60 - return string.format("%03d:%02d:%02d", hrs, min, sec) + return string.format(_p("plugin-timer", "%03d:%02d:%02d"), hrs, min, sec) end local lastupdate @@ -95,9 +95,9 @@ function timer.startplugin() lastupdate = os.time() local time = lastupdate - start_time return - {{ _("Current time"), sectohms(time), "off" }, - { _("Total time"), sectohms(total_time + time), "off" }, - { _("Play Count"), play_count, "off" }}, + {{ _p("plugin-timer", "Current time"), sectohms(time), "off" }, + { _p("plugin-timer", "Total time"), sectohms(total_time + time), "off" }, + { _p("plugin-timer", "Play Count"), play_count, "off" }}, nil, "idle" end @@ -106,7 +106,7 @@ function timer.startplugin() return os.time() > lastupdate end - emu.register_menu(menu_callback, menu_populate, _("Timer")) + emu.register_menu(menu_callback, menu_populate, _p("plugin-timer", "Timer")) end return exports |
