2019-05-14 16:26:27 +02:00
local lib = { }
2021-10-31 02:31:16 +01:00
-- Common UI helper library
local commonui
2019-05-14 16:26:27 +02:00
-- 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 }
2021-10-21 16:14:05 +02:00
-- 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
2021-10-31 02:31:16 +01:00
-- Helper for polling for hotkeys
local hotkey_poller
2019-05-14 16:26:27 +02:00
-- Button being created/edited
local current_button = { }
2021-10-21 16:14:05 +02:00
-- Initial button to select when opening buttons menu
local initial_input
2021-10-31 02:31:16 +01:00
-- Handler for BUTTON menu
local input_menu
2019-05-14 16:26:27 +02:00
-- 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 )
2021-11-03 23:55:26 +01:00
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
2019-05-14 16:26:27 +02:00
end
-- Main menu
local function populate_main_menu ( buttons )
2020-12-26 18:27:42 +01:00
local ioport = manager.machine . ioport
local input = manager.machine . input
2019-05-14 16:26:27 +02:00
local menu = { }
2021-11-04 16:46:04 +01:00
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 , { ' --- ' , ' ' , ' ' } )
2019-05-14 16:26:27 +02:00
header_height = # menu
2021-10-21 16:14:05 +02:00
-- 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
2021-10-31 02:31:16 +01:00
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
2021-11-04 16:46:04 +01:00
local text
if button.button then
2021-12-08 21:42:12 +01:00
text = string.format ( _p ( ' plugin-autofire ' , ' %s [%g Hz] ' ) , button.button . name , rate )
2021-11-04 16:46:04 +01:00
else
text = string.format ( _p ( ' plugin-autofire ' , ' n/a [%g Hz] ' ) , rate )
end
table.insert ( menu , { text , input : seq_name ( button.key ) , ' ' } )
2021-10-31 02:31:16 +01:00
if index == initial_button then
main_selection_save = # menu
end
2021-10-21 16:14:05 +02:00
end
2021-10-31 02:31:16 +01:00
else
2021-11-04 16:46:04 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' [no autofire buttons] ' ) , ' ' , ' off ' } )
2019-05-14 16:26:27 +02:00
end
2021-10-21 16:14:05 +02:00
initial_button = nil
2019-05-14 16:26:27 +02:00
content_height = # menu
2021-11-04 16:46:04 +01:00
table.insert ( menu , { ' --- ' , ' ' , ' ' } )
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Add autofire button ' ) , ' ' , ' ' } )
2021-10-21 16:14:05 +02:00
local selection = main_selection_save
main_selection_save = nil
return menu , selection
2019-05-14 16:26:27 +02:00
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
2021-10-21 16:14:05 +02:00
main_selection_save = index
2019-05-14 16:26:27 +02:00
current_button = buttons [ adjusted_index ]
table.insert ( menu_stack , MENU_TYPES.EDIT )
return true
elseif event == ' clear ' then
table.remove ( buttons , adjusted_index )
2021-10-21 16:14:05 +02:00
main_selection_save = index
if adjusted_index > # buttons then
main_selection_save = main_selection_save - 1
end
2019-05-14 16:26:27 +02:00
return true
end
elseif section == MENU_SECTIONS.FOOTER then
if event == ' select ' then
2021-10-21 16:14:05 +02:00
main_selection_save = index
2019-05-14 16:26:27 +02:00
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 )
2021-11-04 16:46:04 +01:00
local button_name
if current_button.button then
2021-12-08 21:42:12 +01:00
button_name = current_button.button . name
2021-11-04 16:46:04 +01:00
elseif current_button.port then
button_name = _p ( ' plugin-autofire ' , ' n/a ' )
else
button_name = _p ( ' plugin-autofire ' , ' [not set] ' )
end
2021-10-31 02:31:16 +01:00
local key_name = current_button.key and manager.machine . input : seq_name ( current_button.key ) or _p ( ' plugin-autofire ' , ' [not set] ' )
2021-11-04 16:46:04 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Input ' ) , button_name , ' ' } )
2021-10-21 16:14:05 +02:00
if not ( configure_menu_active or configure_selection_save ) then
configure_selection_save = # menu
end
2021-11-04 16:46:04 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Hotkey ' ) , key_name , hotkey_poller and ' lr ' or ' ' } )
2022-03-23 10:27:30 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' On frames ' ) , tostring ( current_button.on_frames ) , current_button.on_frames > 1 and ' lr ' or ' r ' } )
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Off frames ' ) , tostring ( current_button.off_frames ) , current_button.off_frames > 1 and ' lr ' or ' r ' } )
2021-10-21 16:14:05 +02:00
configure_menu_active = true
2019-05-14 16:26:27 +02:00
end
2021-10-31 02:31:16 +01:00
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
2021-11-04 16:46:04 +01:00
current_button.key_cfg = manager.machine . input : seq_to_tokens ( hotkey_poller.sequence )
2019-11-21 05:35:13 +01:00
end
2021-10-31 02:31:16 +01:00
hotkey_poller = nil
return true
2019-11-21 05:35:13 +01:00
end
2021-10-31 02:31:16 +01:00
return false
2019-11-21 05:35:13 +01:00
end
2019-05-14 16:26:27 +02:00
if index == 1 then
2020-12-15 16:18:52 +01:00
-- Input
2019-05-14 16:26:27 +02:00
if event == ' select ' then
2021-10-21 16:14:05 +02:00
configure_selection_save = header_height + index
2019-05-14 16:26:27 +02:00
table.insert ( menu_stack , MENU_TYPES.BUTTON )
2021-11-03 23:55:26 +01:00
if current_button.port and current_button.button then
2021-10-31 02:31:16 +01:00
initial_input = current_button.button
2021-10-21 16:14:05 +02:00
end
2019-05-14 16:26:27 +02:00
return true
end
elseif index == 2 then
2020-12-15 16:18:52 +01:00
-- Hotkey
2019-05-14 16:26:27 +02:00
if event == ' select ' then
2021-10-31 02:31:16 +01:00
if not commonui then
commonui = require ( ' commonui ' )
2019-05-14 16:26:27 +02:00
end
2021-10-31 02:31:16 +01:00
hotkey_poller = commonui.switch_polling_helper ( )
return true
2019-05-14 16:26:27 +02:00
end
elseif index == 3 then
2020-12-15 16:18:52 +01:00
-- On frames
2021-10-31 02:31:16 +01:00
manager.machine : popmessage ( _p ( ' plugin-autofire ' , ' Number of frames button will be pressed ' ) )
2019-05-14 16:26:27 +02:00
if event == ' left ' then
current_button.on_frames = current_button.on_frames - 1
2021-10-15 00:42:35 +02:00
return true
2019-05-14 16:26:27 +02:00
elseif event == ' right ' then
current_button.on_frames = current_button.on_frames + 1
2021-10-15 00:42:35 +02:00
return true
2021-10-21 16:14:05 +02:00
elseif event == ' clear ' then
current_button.on_frames = 1
return true
2019-05-14 16:26:27 +02:00
end
elseif index == 4 then
2020-12-15 16:18:52 +01:00
-- Off frames
2021-10-31 02:31:16 +01:00
manager.machine : popmessage ( _p ( ' plugin-autofire ' , ' Number of frames button will be released ' ) )
2019-05-14 16:26:27 +02:00
if event == ' left ' then
current_button.off_frames = current_button.off_frames - 1
2021-10-15 00:42:35 +02:00
return true
2019-05-14 16:26:27 +02:00
elseif event == ' right ' then
current_button.off_frames = current_button.off_frames + 1
2021-10-15 00:42:35 +02:00
return true
2021-10-21 16:14:05 +02:00
elseif event == ' clear ' then
current_button.off_frames = 1
return true
2019-05-14 16:26:27 +02:00
end
end
2021-10-15 00:42:35 +02:00
return false
2019-05-14 16:26:27 +02:00
end
local function populate_edit_menu ( )
local menu = { }
2021-11-04 16:46:04 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Edit autofire button ' ) , ' ' , ' off ' } )
table.insert ( menu , { ' --- ' , ' ' , ' ' } )
2019-05-14 16:26:27 +02:00
header_height = # menu
populate_configure_menu ( menu )
content_height = # menu
2021-11-04 16:46:04 +01:00
table.insert ( menu , { ' --- ' , ' ' , ' ' } )
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Done ' ) , ' ' , ' ' } )
2021-10-21 16:14:05 +02:00
local selection = configure_selection_save
configure_selection_save = nil
2021-10-31 02:31:16 +01:00
if hotkey_poller then
return hotkey_poller : overlay ( menu , selection , ' lrrepeat ' )
else
return menu , selection , ' lrrepeat '
end
2019-05-14 16:26:27 +02:00
end
local function handle_edit_menu ( index , event , buttons )
local section , adjusted_index = menu_section ( index )
2023-02-17 20:18:45 +01:00
if ( ( section == MENU_SECTIONS.FOOTER ) and ( event == ' select ' ) ) or ( event == ' back ' ) then
2021-10-21 16:14:05 +02:00
configure_menu_active = false
2021-10-15 00:42:35 +02:00
table.remove ( menu_stack )
return true
elseif section == MENU_SECTIONS.CONTENT then
2019-05-14 16:26:27 +02:00
return handle_configure_menu ( adjusted_index , event )
end
return false
end
local function populate_add_menu ( )
local menu = { }
2021-11-04 16:46:04 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Add autofire button ' ) , ' ' , ' off ' } )
table.insert ( menu , { ' --- ' , ' ' , ' ' } )
2019-05-14 16:26:27 +02:00
header_height = # menu
populate_configure_menu ( menu )
content_height = # menu
2021-11-04 16:46:04 +01:00
table.insert ( menu , { ' --- ' , ' ' , ' ' } )
2019-05-14 16:26:27 +02:00
if is_button_complete ( current_button ) then
2021-11-04 16:46:04 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Create ' ) , ' ' , ' ' } )
2019-05-14 16:26:27 +02:00
else
2021-11-04 16:46:04 +01:00
table.insert ( menu , { _p ( ' plugin-autofire ' , ' Cancel ' ) , ' ' , ' ' } )
2019-05-14 16:26:27 +02:00
end
2021-10-21 16:14:05 +02:00
local selection = configure_selection_save
configure_selection_save = nil
2021-10-31 02:31:16 +01:00
if hotkey_poller then
return hotkey_poller : overlay ( menu , selection , ' lrrepeat ' )
else
return menu , selection , ' lrrepeat '
end
2019-05-14 16:26:27 +02:00
end
local function handle_add_menu ( index , event , buttons )
local section , adjusted_index = menu_section ( index )
2023-02-17 20:18:45 +01:00
if ( ( section == MENU_SECTIONS.FOOTER ) and ( event == ' select ' ) ) or ( event == ' back ' ) then
2021-10-21 16:14:05 +02:00
configure_menu_active = false
2021-10-15 00:42:35 +02:00
table.remove ( menu_stack )
if is_button_complete ( current_button ) and ( event == ' select ' ) then
2021-10-21 16:14:05 +02:00
table.insert ( buttons , current_button )
initial_button = # buttons
2019-05-14 16:26:27 +02:00
end
2021-10-15 00:42:35 +02:00
return true
elseif section == MENU_SECTIONS.CONTENT then
return handle_configure_menu ( adjusted_index , event )
2019-05-14 16:26:27 +02:00
end
return false
end
-- Button selection menu
local function populate_button_menu ( )
2021-10-31 02:31:16 +01:00
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
2021-10-20 19:11:43 +02:00
end
2021-10-31 02:31:16 +01:00
local function action ( field )
if field then
current_button.port = field.port . tag
2021-11-03 23:55:26 +01:00
current_button.mask = field.mask
current_button.type = field.type
2021-10-31 02:31:16 +01:00
current_button.button = field
2021-10-21 16:14:05 +02:00
end
2021-10-31 02:31:16 +01:00
initial_input = nil
input_menu = nil
table.remove ( menu_stack )
2021-10-15 00:42:35 +02:00
end
2021-10-31 02:31:16 +01:00
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 )
2019-05-14 16:26:27 +02:00
end
local function handle_button_menu ( index , event )
2021-10-31 02:31:16 +01:00
return input_menu : handle ( index , event )
2019-05-14 16:26:27 +02:00
end
2019-05-22 00:44:44 +02:00
function lib : init_menu ( buttons )
header_height = 0
content_height = 0
menu_stack = { MENU_TYPES.MAIN }
current_button = { }
2021-10-31 02:31:16 +01:00
input_menu = nil
2019-05-22 00:44:44 +02:00
end
2019-05-14 16:26:27 +02:00
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 )
2020-12-26 15:32:37 +01:00
manager.machine : popmessage ( )
2019-05-14 16:26:27 +02:00
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