mirror of
https://github.com/holub/mame
synced 2025-04-26 10:13:37 +03:00

Added menus for controlling toggle inputs, and showing recognised input devices and control state. Moved input menu options off main menu to a submenu, as there are a lot of them now. Moved menu heading drawing into base class, added headings to more menus, and made headings more consistent with the menu items used to reach them. Also made terminology more consistent. Changed the default names for buttons and hat switches/D-pads to use 1-based numbering. DirectInput still returns 0-based button numbers for some devices. Removed local copy of MinGW xaudio2.h as it’s now included in the MSYS2 package. Also fixed building the DirectSound sound output module with the SDL OSD on Windows - the Windows headers are sensitive to include order. Started adding documentation for menus, to hopefully help people find menus they remember seeing but can't recall how to access. For translators, this makes terminology more consistent. In particular: * "Settings" is preferred over "configuration" in a number of places, as the latter can be construed as referring specifically to settings stored in .cfg files in the cfg_directory folder. Also, references to saving machine configuration could be interpreted as relating to the settings on the "Machine Configuration" menu. * The controls on host input devices (e.g. keys, buttons, joystick axes) are referred to as "controls", while emulated inputs are referred to as "inputs". * The menus for assigning host controls to emulated inputs are called "input assignments" menus to distinguish them from other input settings menus. * Combinations of controls that can be assigned to emulated inputs are referred to as "combinations" rather than "sequences". * The potentially confusing term "ROM set" has been removed altogether. Use "short name" to refer to a device or system's identifier. * "System" is used in almost places to refer to a complete, runnable system rather than "Machine". * "Driver" is now only used to refer to source files where systems or devices are defined - it is no longer used to refer to individual systems. * A few more menus have message context for the messages. This makes it a bit easier to guess where the messages are used. It also means you can use different translations in different places if necessary (e.g. if the same English text should be translated differently as an item in one menu and as a heading in another).
215 lines
5.2 KiB
Lua
215 lines
5.2 KiB
Lua
-- 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, { 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 combination entered'))
|
|
self.sequence = nil
|
|
return true
|
|
end
|
|
else
|
|
machine:popmessage(string.format(
|
|
_p('plugin-commonui', 'Enter combination or press %s to cancel\n%s'),
|
|
cancel_prompt,
|
|
input:seq_name(poller.sequence)))
|
|
return false
|
|
end
|
|
end
|
|
|
|
return helper
|
|
end
|
|
|
|
|
|
return exports
|