1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
-- 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 == 'back') 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()
uiinput:reset()
if (not poller.modified) or (modified_ticks == emu.osd_ticks()) then
-- cancelled immediately
self.sequence = nil -- TODO: communicate this better?
return true
else
-- entered something before cancelling
self.sequence = nil
return true
end
elseif poller:poll() then
uiinput:reset()
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
|