mirror of
https://github.com/holub/mame
synced 2025-04-19 23:12:11 +03:00
Plugin usability improvements:
* autofire, inputmacro: Made left/right repeat when held (makes setting long delays/durations easier). * autofire, inputmacro: Added headings for devices in input selection menus (helps when controller buttons have identical names, e.g. AES) * autofire: Made intial selection when moving between menus intuitive, log some errors on saving/loading configuration. * autofire: Fixed two errors in Chinese localisations.
This commit is contained in:
parent
9c61152a20
commit
f459eb6e13
@ -6697,7 +6697,7 @@ msgstr "总计发现 %d 符合"
|
||||
#: plugins/cheatfind/init.lua:623
|
||||
#, lua-format
|
||||
msgid "Perform Compare : Slot %d %s Slot %d"
|
||||
msgstr "执行比较:插槽 $d %s 插槽 %d"
|
||||
msgstr "执行比较:插槽 %d %s 插槽 %d"
|
||||
|
||||
#: plugins/cheatfind/init.lua:624
|
||||
#, lua-format
|
||||
@ -6886,7 +6886,7 @@ msgstr "连发按钮"
|
||||
#: plugins/autofire/autofire_menu.lua:59
|
||||
#, lua-format
|
||||
msgid "Press %s to delete"
|
||||
msgstr "按下 $s 删除"
|
||||
msgstr "按下 %s 删除"
|
||||
|
||||
#: plugins/autofire/autofire_menu.lua:68
|
||||
#, lua-format
|
||||
|
@ -6697,7 +6697,7 @@ msgstr "總計發現 %d 符合"
|
||||
#: plugins/cheatfind/init.lua:623
|
||||
#, lua-format
|
||||
msgid "Perform Compare : Slot %d %s Slot %d"
|
||||
msgstr "執行比較:插槽 $d %s 插槽 %d"
|
||||
msgstr "執行比較:插槽 %d %s 插槽 %d"
|
||||
|
||||
#: plugins/cheatfind/init.lua:624
|
||||
#, lua-format
|
||||
@ -6886,7 +6886,7 @@ msgstr "連射按鈕"
|
||||
#: plugins/autofire/autofire_menu.lua:59
|
||||
#, lua-format
|
||||
msgid "Press %s to delete"
|
||||
msgstr "按下 $s 刪除"
|
||||
msgstr "按下 %s 刪除"
|
||||
|
||||
#: plugins/autofire/autofire_menu.lua:68
|
||||
#, lua-format
|
||||
|
@ -15,11 +15,26 @@ 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
|
||||
|
||||
-- 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 = {}
|
||||
local inputs
|
||||
|
||||
-- Returns the section (from MENU_SECTIONS) and the index within that section
|
||||
local function menu_section(index)
|
||||
@ -60,35 +75,54 @@ local function populate_main_menu(buttons)
|
||||
menu[#menu + 1] = {'---', '', ''}
|
||||
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
|
||||
|
||||
for index, button in ipairs(buttons) do
|
||||
-- Assume refresh rate of 60 Hz; maybe better to use screen_device refresh()?
|
||||
local rate = 60 / (button.on_frames + button.off_frames)
|
||||
-- Round to two decimal places
|
||||
-- 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 [%d Hz]'), _p('input-name', button.button.name), rate)
|
||||
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
|
||||
end
|
||||
end
|
||||
initial_button = nil
|
||||
content_height = #menu
|
||||
|
||||
menu[#menu + 1] = {'---', '', ''}
|
||||
menu[#menu + 1] = {_('Add autofire button'), '', ''}
|
||||
return menu
|
||||
|
||||
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
|
||||
@ -103,9 +137,13 @@ 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, ''}
|
||||
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'}
|
||||
configure_menu_active = true
|
||||
end
|
||||
|
||||
-- Borrowed from the cheat plugin
|
||||
@ -138,7 +176,11 @@ 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}
|
||||
end
|
||||
return true
|
||||
end
|
||||
elseif index == 2 then
|
||||
@ -159,6 +201,9 @@ local function handle_configure_menu(index, event)
|
||||
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
|
||||
@ -169,6 +214,9 @@ local function handle_configure_menu(index, event)
|
||||
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
|
||||
@ -185,12 +233,17 @@ local function populate_edit_menu()
|
||||
|
||||
menu[#menu + 1] = {'---', '', ''}
|
||||
menu[#menu + 1] = {_('Done'), '', ''}
|
||||
return menu
|
||||
|
||||
local selection = configure_selection_save
|
||||
configure_selection_save = nil
|
||||
return menu, selection, 'lrrepeat'
|
||||
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
|
||||
elseif section == MENU_SECTIONS.CONTENT then
|
||||
@ -214,15 +267,21 @@ local function populate_add_menu()
|
||||
else
|
||||
menu[#menu + 1] = {_('Cancel'), '', ''}
|
||||
end
|
||||
return menu
|
||||
|
||||
local selection = configure_selection_save
|
||||
configure_selection_save = nil
|
||||
return menu, selection, 'lrrepeat'
|
||||
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
|
||||
buttons[#buttons + 1] = current_button
|
||||
table.insert(buttons, current_button)
|
||||
initial_button = #buttons
|
||||
end
|
||||
return true
|
||||
elseif section == MENU_SECTIONS.CONTENT then
|
||||
@ -236,54 +295,85 @@ end
|
||||
local function populate_button_menu()
|
||||
local ioport = manager.machine.ioport
|
||||
menu = {}
|
||||
inputs = {}
|
||||
menu[#menu + 1] = {_('Select an input for autofire'), '', 'off'}
|
||||
menu[#menu + 1] = {'---', '', ''}
|
||||
header_height = #menu
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
||||
for i, input in pairs(inputs) do
|
||||
menu[header_height + i] = { _p('input-name', input.ioport_field.name), '', '' }
|
||||
end
|
||||
content_height = #menu
|
||||
initial_input = nil
|
||||
|
||||
menu[#menu + 1] = {'---', '', ''}
|
||||
menu[#menu + 1] = {_('Cancel'), '', ''}
|
||||
|
||||
return menu
|
||||
return menu, selection
|
||||
end
|
||||
|
||||
local function handle_button_menu(index, event)
|
||||
@ -307,7 +397,7 @@ function lib:init_menu(buttons)
|
||||
content_height = 0
|
||||
menu_stack = { MENU_TYPES.MAIN }
|
||||
current_button = {}
|
||||
inputs = {}
|
||||
inputs = nil
|
||||
end
|
||||
|
||||
function lib:populate_menu(buttons)
|
||||
|
@ -50,13 +50,15 @@ end
|
||||
function lib:load_settings()
|
||||
local buttons = {}
|
||||
local json = require('json')
|
||||
local file = io.open(get_settings_path() .. get_settings_filename(), 'r')
|
||||
local filename = get_settings_path() .. get_settings_filename()
|
||||
local file = io.open(filename, 'r')
|
||||
if not file then
|
||||
return buttons
|
||||
end
|
||||
local loaded_settings = json.parse(file:read('a'))
|
||||
file:close()
|
||||
if not loaded_settings then
|
||||
emu.print_error(string.format('Error loading autofire settings: error parsing file "%s" as JSON\n', filename))
|
||||
return buttons
|
||||
end
|
||||
for index, button_settings in ipairs(loaded_settings) do
|
||||
@ -74,6 +76,7 @@ function lib:save_settings(buttons)
|
||||
if not attr then
|
||||
lfs.mkdir(path)
|
||||
elseif attr.mode ~= 'directory' then
|
||||
emu.print_error(string.format('Error autofire settings macros: "%s" is not a directory\n', path))
|
||||
return
|
||||
end
|
||||
if #buttons == 0 then
|
||||
@ -83,11 +86,14 @@ function lib:save_settings(buttons)
|
||||
local json = require('json')
|
||||
local settings = serialize_settings(buttons)
|
||||
local data = json.stringify(settings, {indent = true})
|
||||
local file = io.open(path .. get_settings_filename(), 'w')
|
||||
if file then
|
||||
file:write(data)
|
||||
file:close()
|
||||
local filename = path .. get_settings_filename()
|
||||
local file = io.open(filename, 'w')
|
||||
if not file then
|
||||
emu.print_error(string.format('Error saving autofire settings: error opening file "%s" for writing\n', filename))
|
||||
return
|
||||
end
|
||||
file:write(data)
|
||||
file:close()
|
||||
end
|
||||
|
||||
return lib
|
||||
|
@ -114,20 +114,42 @@ function populate_input()
|
||||
for tag, port in pairs(manager.machine.ioport.ports) do
|
||||
for name, field in pairs(port.fields) do
|
||||
if supported(field) then
|
||||
input_choices[#input_choices + 1] = field
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
input_item_first_choice = #items + 1
|
||||
local selection = input_item_first_choice
|
||||
for index, field in ipairs(input_choices) do
|
||||
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
|
||||
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
|
||||
end
|
||||
input_start_field = nil
|
||||
@ -145,6 +167,7 @@ end
|
||||
local edit_current_macro
|
||||
local edit_start_selection
|
||||
local edit_start_step
|
||||
local edit_menu_active
|
||||
local edit_insert_position
|
||||
local edit_name_buffer
|
||||
local edit_items
|
||||
@ -379,6 +402,10 @@ local function add_edit_items(items)
|
||||
|
||||
items[#items + 1] = { _p('plugin-inputmacro', 'Name'), edit_name_buffer and (edit_name_buffer .. '_') or edit_current_macro.name, '' }
|
||||
edit_items[#items] = { action = 'name' }
|
||||
if not (edit_start_selection or edit_start_step or edit_menu_active) then
|
||||
edit_start_selection = #items
|
||||
end
|
||||
edit_menu_active = true
|
||||
|
||||
local binding = edit_current_macro.binding
|
||||
local activation = binding and input:seq_name(binding) or _p('plugin-inputmacro', '[not set]')
|
||||
@ -453,7 +480,9 @@ 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
|
||||
table.remove(menu_stack)
|
||||
return true
|
||||
@ -462,6 +491,8 @@ 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
|
||||
table.remove(menu_stack)
|
||||
@ -474,7 +505,9 @@ 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
|
||||
table.remove(menu_stack)
|
||||
return true
|
||||
@ -500,7 +533,7 @@ local function populate_add()
|
||||
|
||||
local selection = edit_start_selection
|
||||
edit_start_selection = nil
|
||||
return items, selection
|
||||
return items, selection, 'lrrepeat'
|
||||
end
|
||||
|
||||
local function populate_edit()
|
||||
@ -517,7 +550,7 @@ local function populate_edit()
|
||||
|
||||
local selection = edit_start_selection
|
||||
edit_start_selection = nil
|
||||
return items, selection
|
||||
return items, selection, 'lrrepeat'
|
||||
end
|
||||
|
||||
|
||||
@ -532,7 +565,6 @@ function handle_macros(index, event)
|
||||
if event == 'select' then
|
||||
edit_current_macro = new_macro()
|
||||
edit_insert_position = #edit_current_macro.steps + 1
|
||||
edit_start_selection = 1 -- not actually selectable, but it will take the first item
|
||||
macros_selection_save = index
|
||||
table.insert(menu_stack, MENU_TYPES.ADD)
|
||||
return true
|
||||
|
@ -432,7 +432,7 @@ sol::object lua_engine::call_plugin(const std::string &name, sol::object in)
|
||||
return sol::lua_nil;
|
||||
}
|
||||
|
||||
std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string>> &menu_list)
|
||||
std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string> > &menu_list, std::string &flags)
|
||||
{
|
||||
std::string field = "menu_pop_" + menu;
|
||||
sol::object obj = sol().registry()[field];
|
||||
@ -446,7 +446,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
|
||||
}
|
||||
else
|
||||
{
|
||||
std::tuple<sol::table, std::optional<long> > table = res;
|
||||
std::tuple<sol::table, std::optional<long>, std::string> table = res;
|
||||
for (auto &entry : std::get<0>(table))
|
||||
{
|
||||
if (entry.second.is<sol::table>())
|
||||
@ -455,27 +455,31 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
|
||||
menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3));
|
||||
}
|
||||
}
|
||||
flags = std::get<2>(table);
|
||||
return std::get<1>(table);
|
||||
}
|
||||
}
|
||||
flags.clear();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
|
||||
std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
|
||||
{
|
||||
std::string field = "menu_cb_" + menu;
|
||||
bool ret = false;
|
||||
std::pair<bool, std::optional<long> > ret(false, std::nullopt);
|
||||
sol::object obj = sol().registry()[field];
|
||||
if(obj.is<sol::protected_function>())
|
||||
if (obj.is<sol::protected_function>())
|
||||
{
|
||||
auto res = invoke(obj.as<sol::protected_function>(), index, event);
|
||||
if(!res.valid())
|
||||
if (!res.valid())
|
||||
{
|
||||
sol::error err = res;
|
||||
osd_printf_error("[LUA ERROR] in menu_callback: %s\n", err.what());
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = res;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ public:
|
||||
|
||||
bool frame_hook();
|
||||
|
||||
std::optional<long> menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string>> &menu_list);
|
||||
bool menu_callback(const std::string &menu, int index, const std::string &event);
|
||||
std::optional<long> menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string> > &menu_list, std::string &flags);
|
||||
std::pair<bool, std::optional<long> > menu_callback(const std::string &menu, int index, const std::string &event);
|
||||
|
||||
void set_machine(running_machine *machine);
|
||||
std::vector<std::string> &get_menu() { return m_menu; }
|
||||
|
@ -37,7 +37,7 @@ class menu
|
||||
{
|
||||
public:
|
||||
// flags for menu items
|
||||
enum : unsigned
|
||||
enum : uint32_t
|
||||
{
|
||||
FLAG_LEFT_ARROW = 1U << 0,
|
||||
FLAG_RIGHT_ARROW = 1U << 1,
|
||||
|
@ -62,15 +62,17 @@ menu_plugin::~menu_plugin()
|
||||
{
|
||||
}
|
||||
|
||||
menu_plugin_opt::menu_plugin_opt(mame_ui_manager &mui, render_container &container, char *menu) :
|
||||
ui::menu(mui, container),
|
||||
m_menu(menu)
|
||||
menu_plugin_opt::menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu) :
|
||||
ui::menu(mui, container),
|
||||
m_menu(menu),
|
||||
m_process_flags(0U),
|
||||
m_need_idle(false)
|
||||
{
|
||||
}
|
||||
|
||||
void menu_plugin_opt::handle()
|
||||
{
|
||||
const event *menu_event = process(0);
|
||||
const event *menu_event = process(m_process_flags);
|
||||
|
||||
if (menu_event)
|
||||
{
|
||||
@ -105,9 +107,13 @@ void menu_plugin_opt::handle()
|
||||
key = std::to_string((u32)menu_event->unichar);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
if (!m_need_idle)
|
||||
return;
|
||||
}
|
||||
if (mame_machine_manager::instance()->lua()->menu_callback(m_menu, uintptr_t(menu_event->itemref), key))
|
||||
auto const result = mame_machine_manager::instance()->lua()->menu_callback(m_menu, uintptr_t(menu_event->itemref), key);
|
||||
if (result.second)
|
||||
set_selection(reinterpret_cast<void *>(uintptr_t(*result.second)));
|
||||
if (result.first)
|
||||
reset(reset_options::REMEMBER_REF);
|
||||
else if (menu_event->iptkey == IPT_UI_CANCEL)
|
||||
stack_pop();
|
||||
@ -117,41 +123,78 @@ void menu_plugin_opt::handle()
|
||||
void menu_plugin_opt::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
std::vector<std::tuple<std::string, std::string, std::string>> menu_list;
|
||||
auto const sel = mame_machine_manager::instance()->lua()->menu_populate(m_menu, menu_list);
|
||||
std::string flags;
|
||||
auto const sel = mame_machine_manager::instance()->lua()->menu_populate(m_menu, menu_list, flags);
|
||||
|
||||
uintptr_t i = 1;
|
||||
for(auto &item : menu_list)
|
||||
for (auto &item : menu_list)
|
||||
{
|
||||
const std::string &text = std::get<0>(item);
|
||||
const std::string &subtext = std::get<1>(item);
|
||||
const std::string &tflags = std::get<2>(item);
|
||||
std::string &text = std::get<0>(item);
|
||||
std::string &subtext = std::get<1>(item);
|
||||
std::string_view tflags = std::get<2>(item);
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (tflags == "off")
|
||||
flags = FLAG_DISABLE;
|
||||
else if (tflags == "heading")
|
||||
flags = FLAG_DISABLE | FLAG_UI_HEADING;
|
||||
else if (tflags == "l")
|
||||
flags = FLAG_LEFT_ARROW;
|
||||
else if (tflags == "r")
|
||||
flags = FLAG_RIGHT_ARROW;
|
||||
else if (tflags == "lr")
|
||||
flags = FLAG_RIGHT_ARROW | FLAG_LEFT_ARROW;
|
||||
|
||||
if(text == "---")
|
||||
uint32_t item_flags_or = uint32_t(0);
|
||||
uint32_t item_flags_and = ~uint32_t(0);
|
||||
auto flag_start = tflags.find_first_not_of(' ');
|
||||
while (std::string_view::npos != flag_start)
|
||||
{
|
||||
tflags.remove_prefix(flag_start);
|
||||
auto const flag_end = tflags.find(' ');
|
||||
auto const flag = tflags.substr(0, flag_end);
|
||||
tflags.remove_prefix(flag.length());
|
||||
flag_start = tflags.find_first_not_of(' ');
|
||||
|
||||
if (flag == "off")
|
||||
item_flags_or |= FLAG_DISABLE;
|
||||
else if (flag == "on")
|
||||
item_flags_and &= ~FLAG_DISABLE;
|
||||
else if (flag == "l")
|
||||
item_flags_or |= FLAG_LEFT_ARROW;
|
||||
else if (flag == "r")
|
||||
item_flags_or |= FLAG_RIGHT_ARROW;
|
||||
else if (flag == "lr")
|
||||
item_flags_or |= FLAG_RIGHT_ARROW | FLAG_LEFT_ARROW;
|
||||
else if (flag == "invert")
|
||||
item_flags_or |= FLAG_INVERT;
|
||||
else if (flag == "heading")
|
||||
item_flags_or |= FLAG_DISABLE | FLAG_UI_HEADING;
|
||||
}
|
||||
|
||||
if (text == "---")
|
||||
item_append(menu_item_type::SEPARATOR);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
item_append(text, subtext, flags, reinterpret_cast<void *>(i++));
|
||||
}
|
||||
item_append(std::move(text), std::move(subtext), item_flags_or & item_flags_and, reinterpret_cast<void *>(i));
|
||||
++i;
|
||||
}
|
||||
item_append(menu_item_type::SEPARATOR);
|
||||
|
||||
if (sel)
|
||||
set_selection(reinterpret_cast<void *>(uintptr_t(*sel)));
|
||||
|
||||
m_process_flags = 0U;
|
||||
m_need_idle = false;
|
||||
if (!flags.empty())
|
||||
{
|
||||
std::string_view mflags = flags;
|
||||
auto flag_start = mflags.find_first_not_of(' ');
|
||||
while (std::string_view::npos != flag_start)
|
||||
{
|
||||
mflags.remove_prefix(flag_start);
|
||||
auto const flag_end = mflags.find(' ');
|
||||
auto const flag = mflags.substr(0, flag_end);
|
||||
mflags.remove_prefix(flag.length());
|
||||
flag_start = mflags.find_first_not_of(' ');
|
||||
|
||||
if (flag == "lralways")
|
||||
m_process_flags |= PROCESS_LR_ALWAYS;
|
||||
else if (flag == "lrrepeat")
|
||||
m_process_flags |= PROCESS_LR_REPEAT;
|
||||
else if (flag == "customnav")
|
||||
m_process_flags |= PROCESS_CUSTOM_NAV;
|
||||
else if (flag == "idle")
|
||||
m_need_idle = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu_plugin_opt::~menu_plugin_opt()
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@ -40,7 +41,7 @@ private:
|
||||
class menu_plugin_opt : public menu
|
||||
{
|
||||
public:
|
||||
menu_plugin_opt(mame_ui_manager &mui, render_container &container, char *menu);
|
||||
menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu);
|
||||
virtual ~menu_plugin_opt();
|
||||
|
||||
protected:
|
||||
@ -50,7 +51,9 @@ private:
|
||||
virtual void populate(float &customtop, float &custombottom) override;
|
||||
virtual void handle() override;
|
||||
|
||||
std::string m_menu;
|
||||
std::string const m_menu;
|
||||
uint32_t m_process_flags;
|
||||
bool m_need_idle;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
Loading…
Reference in New Issue
Block a user