summaryrefslogtreecommitdiffstatshomepage
path: root/plugins/commonui/init.lua
blob: 5219e7dbb9e1f8e8ec3b50a1d356d9611c0a8cbb (plain) (blame)
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