mirror of
https://github.com/holub/mame
synced 2025-04-27 18:53:05 +03:00

Added pseudo format specifiers to controller port names: %p for player and %% for literal percent symbol. This lets you get the localised player identifier in overridden input names (see NES and Neo-Geo for examples), and reduces the number of messages to translate. For translators, the new messages are mostly previously existing messages with wording adjusted for clarity (e.g. referring to "media" rather than "ROMs" in several places, as things like disk and tape images are included). It's also possible to localise the "???" dipslayed for an input without a valid name, but that should never actually appear in practice.
369 lines
11 KiB
Lua
369 lines
11 KiB
Lua
local lib = {}
|
|
|
|
-- Common UI helper library
|
|
local commonui
|
|
|
|
-- Set of all menus
|
|
local MENU_TYPES = { MAIN = 0, EDIT = 1, ADD = 2, BUTTON = 3 }
|
|
|
|
-- Set of sections within a menu
|
|
local MENU_SECTIONS = { HEADER = 0, CONTENT = 1, FOOTER = 2 }
|
|
|
|
-- Last index of header items (above main content) in menu
|
|
local header_height = 0
|
|
|
|
-- Last index of content items (below header, above footer) in menu
|
|
local content_height = 0
|
|
|
|
-- Stack of menus (see MENU_TYPES)
|
|
local menu_stack = { MENU_TYPES.MAIN }
|
|
|
|
-- Button to select when showing the main menu (so newly added button can be selected)
|
|
local initial_button
|
|
|
|
-- Saved selection on main menu (to restore after configure menu is dismissed)
|
|
local main_selection_save
|
|
|
|
-- Whether configure menu is active (so first item can be selected initially)
|
|
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
|
|
|
|
-- Handler for BUTTON menu
|
|
local input_menu
|
|
|
|
-- Returns the section (from MENU_SECTIONS) and the index within that section
|
|
local function menu_section(index)
|
|
if index <= header_height then
|
|
return MENU_SECTIONS.HEADER, index
|
|
elseif index <= content_height then
|
|
return MENU_SECTIONS.CONTENT, index - header_height
|
|
else
|
|
return MENU_SECTIONS.FOOTER, index - content_height
|
|
end
|
|
end
|
|
|
|
local function create_new_button()
|
|
return {
|
|
on_frames = 1,
|
|
off_frames = 1,
|
|
counter = 0
|
|
}
|
|
end
|
|
|
|
local function is_button_complete(button)
|
|
return button.port and button.mask and button.type and button.key and button.on_frames and button.off_frames and button.button and button.counter
|
|
end
|
|
|
|
-- Main menu
|
|
|
|
local function populate_main_menu(buttons)
|
|
local ioport = manager.machine.ioport
|
|
local input = manager.machine.input
|
|
local menu = {}
|
|
table.insert(menu, {_p('plugin-autofire', 'Autofire buttons'), '', 'off'})
|
|
table.insert(menu, {string.format(_p('plugin-autofire', 'Press %s to delete'), manager.ui:get_general_input_setting(ioport:token_to_input_type('UI_CLEAR'))), '', 'off'})
|
|
table.insert(menu, {'---', '', ''})
|
|
header_height = #menu
|
|
|
|
-- Use frame rate of first screen or 60Hz if no screens
|
|
local freq = 60
|
|
local screen = manager.machine.screens:at(1)
|
|
if screen then
|
|
freq = 1 / screen.frame_period
|
|
end
|
|
|
|
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
|
|
if button.button then
|
|
text = string.format(_p('plugin-autofire', '%s [%g Hz]'), button.button.name, rate)
|
|
else
|
|
text = string.format(_p('plugin-autofire', 'n/a [%g Hz]'), rate)
|
|
end
|
|
table.insert(menu, {text, input:seq_name(button.key), ''})
|
|
if index == initial_button then
|
|
main_selection_save = #menu
|
|
end
|
|
end
|
|
else
|
|
table.insert(menu, {_p('plugin-autofire', '[no autofire buttons]'), '', 'off'})
|
|
end
|
|
initial_button = nil
|
|
content_height = #menu
|
|
|
|
table.insert(menu, {'---', '', ''})
|
|
table.insert(menu, {_p('plugin-autofire', 'Add autofire button'), '', ''})
|
|
|
|
local selection = main_selection_save
|
|
main_selection_save = nil
|
|
return menu, selection
|
|
end
|
|
|
|
local function handle_main_menu(index, event, buttons)
|
|
local section, adjusted_index = menu_section(index)
|
|
if section == MENU_SECTIONS.CONTENT then
|
|
if event == 'select' then
|
|
main_selection_save = index
|
|
current_button = buttons[adjusted_index]
|
|
table.insert(menu_stack, MENU_TYPES.EDIT)
|
|
return true
|
|
elseif event == 'clear' then
|
|
table.remove(buttons, adjusted_index)
|
|
main_selection_save = index
|
|
if adjusted_index > #buttons then
|
|
main_selection_save = main_selection_save - 1
|
|
end
|
|
return true
|
|
end
|
|
elseif section == MENU_SECTIONS.FOOTER then
|
|
if event == 'select' then
|
|
main_selection_save = index
|
|
current_button = create_new_button()
|
|
table.insert(menu_stack, MENU_TYPES.ADD)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Add/edit menus (mostly identical)
|
|
|
|
local function populate_configure_menu(menu)
|
|
local button_name
|
|
if current_button.button then
|
|
button_name = current_button.button.name
|
|
elseif current_button.port then
|
|
button_name = _p('plugin-autofire', 'n/a')
|
|
else
|
|
button_name = _p('plugin-autofire', '[not set]')
|
|
end
|
|
local key_name = current_button.key and manager.machine.input:seq_name(current_button.key) or _p('plugin-autofire', '[not set]')
|
|
table.insert(menu, {_p('plugin-autofire', 'Input'), button_name, ''})
|
|
if not (configure_menu_active or configure_selection_save) then
|
|
configure_selection_save = #menu
|
|
end
|
|
table.insert(menu, {_p('plugin-autofire', 'Hotkey'), key_name, hotkey_poller and 'lr' or ''})
|
|
table.insert(menu, {_p('plugin-autofire', 'On frames'), current_button.on_frames, current_button.on_frames > 1 and 'lr' or 'r'})
|
|
table.insert(menu, {_p('plugin-autofire', 'Off frames'), current_button.off_frames, current_button.off_frames > 1 and 'lr' or 'r'})
|
|
configure_menu_active = true
|
|
end
|
|
|
|
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
|
|
current_button.key_cfg = manager.machine.input:seq_to_tokens(hotkey_poller.sequence)
|
|
end
|
|
hotkey_poller = nil
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
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.button then
|
|
initial_input = current_button.button
|
|
end
|
|
return true
|
|
end
|
|
elseif index == 2 then
|
|
-- Hotkey
|
|
if event == 'select' then
|
|
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(_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
|
|
elseif event == 'right' then
|
|
current_button.on_frames = current_button.on_frames + 1
|
|
return true
|
|
elseif event == 'clear' then
|
|
current_button.on_frames = 1
|
|
return true
|
|
end
|
|
elseif index == 4 then
|
|
-- Off frames
|
|
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
|
|
elseif event == 'right' then
|
|
current_button.off_frames = current_button.off_frames + 1
|
|
return true
|
|
elseif event == 'clear' then
|
|
current_button.off_frames = 1
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function populate_edit_menu()
|
|
local menu = {}
|
|
table.insert(menu, {_p('plugin-autofire', 'Edit autofire button'), '', 'off'})
|
|
table.insert(menu, {'---', '', ''})
|
|
header_height = #menu
|
|
|
|
populate_configure_menu(menu)
|
|
content_height = #menu
|
|
|
|
table.insert(menu, {'---', '', ''})
|
|
table.insert(menu, {_p('plugin-autofire', 'Done'), '', ''})
|
|
|
|
local selection = configure_selection_save
|
|
configure_selection_save = nil
|
|
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
|
|
configure_menu_active = false
|
|
table.remove(menu_stack)
|
|
return true
|
|
elseif section == MENU_SECTIONS.CONTENT then
|
|
return handle_configure_menu(adjusted_index, event)
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function populate_add_menu()
|
|
local menu = {}
|
|
table.insert(menu, {_p('plugin-autofire', 'Add autofire button'), '', 'off'})
|
|
table.insert(menu, {'---', '', ''})
|
|
header_height = #menu
|
|
|
|
populate_configure_menu(menu)
|
|
content_height = #menu
|
|
|
|
table.insert(menu, {'---', '', ''})
|
|
if is_button_complete(current_button) then
|
|
table.insert(menu, {_p('plugin-autofire', 'Create'), '', ''})
|
|
else
|
|
table.insert(menu, {_p('plugin-autofire', 'Cancel'), '', ''})
|
|
end
|
|
|
|
local selection = configure_selection_save
|
|
configure_selection_save = nil
|
|
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
|
|
configure_menu_active = false
|
|
table.remove(menu_stack)
|
|
if is_button_complete(current_button) and (event == 'select') then
|
|
table.insert(buttons, current_button)
|
|
initial_button = #buttons
|
|
end
|
|
return true
|
|
elseif section == MENU_SECTIONS.CONTENT then
|
|
return handle_configure_menu(adjusted_index, event)
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Button selection menu
|
|
|
|
local function populate_button_menu()
|
|
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 function action(field)
|
|
if field then
|
|
current_button.port = field.port.tag
|
|
current_button.mask = field.mask
|
|
current_button.type = field.type
|
|
current_button.button = field
|
|
end
|
|
initial_input = nil
|
|
input_menu = nil
|
|
table.remove(menu_stack)
|
|
end
|
|
|
|
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)
|
|
return input_menu:handle(index, event)
|
|
end
|
|
|
|
function lib:init_menu(buttons)
|
|
header_height = 0
|
|
content_height = 0
|
|
menu_stack = { MENU_TYPES.MAIN }
|
|
current_button = {}
|
|
input_menu = nil
|
|
end
|
|
|
|
function lib:populate_menu(buttons)
|
|
local current_menu = menu_stack[#menu_stack]
|
|
if current_menu == MENU_TYPES.MAIN then
|
|
return populate_main_menu(buttons)
|
|
elseif current_menu == MENU_TYPES.EDIT then
|
|
return populate_edit_menu()
|
|
elseif current_menu == MENU_TYPES.ADD then
|
|
return populate_add_menu()
|
|
elseif current_menu == MENU_TYPES.BUTTON then
|
|
return populate_button_menu()
|
|
end
|
|
end
|
|
|
|
function lib:handle_menu_event(index, event, buttons)
|
|
manager.machine:popmessage()
|
|
local current_menu = menu_stack[#menu_stack]
|
|
if current_menu == MENU_TYPES.MAIN then
|
|
return handle_main_menu(index, event, buttons)
|
|
elseif current_menu == MENU_TYPES.EDIT then
|
|
return handle_edit_menu(index, event, buttons)
|
|
elseif current_menu == MENU_TYPES.ADD then
|
|
return handle_add_menu(index, event, buttons)
|
|
elseif current_menu == MENU_TYPES.BUTTON then
|
|
return handle_button_menu(index, event)
|
|
end
|
|
end
|
|
|
|
return lib
|