summaryrefslogtreecommitdiffstatshomepage
path: root/plugins
diff options
context:
space:
mode:
author Vas Crabb <cuavas@users.noreply.github.com>2021-10-31 12:31:16 +1100
committer GitHub <noreply@github.com>2021-10-31 12:31:16 +1100
commitd64ea5331b2312f81df464fb22dcef0191216d86 (patch)
treea3f3784371da401e6a675e5b6b9734b88281a08d /plugins
parentcfffc54b61cabc5ef9533396bf87324eb5eeb63e (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.lua233
-rw-r--r--plugins/autofire/init.lua4
-rw-r--r--plugins/cheat/init.lua64
-rw-r--r--plugins/commonui/init.lua214
-rw-r--r--plugins/commonui/plugin.json9
-rw-r--r--plugins/data/data_command.lua2
-rw-r--r--plugins/data/data_gameinit.lua2
-rw-r--r--plugins/data/data_hiscore.lua4
-rw-r--r--plugins/data/data_history.lua2
-rw-r--r--plugins/data/data_mameinfo.lua4
-rw-r--r--plugins/data/data_marp.lua2
-rw-r--r--plugins/data/data_messinfo.lua4
-rw-r--r--plugins/data/data_story.lua2
-rw-r--r--plugins/data/data_sysinfo.lua2
-rw-r--r--plugins/inputmacro/init.lua2
-rw-r--r--plugins/inputmacro/inputmacro_menu.lua199
-rw-r--r--plugins/timer/init.lua10
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