mirror of
https://github.com/holub/mame
synced 2025-06-06 12:53:46 +03:00
Multiple synth layouts: updated to the latest slider script. (#13422)
* Explicit configuration: Using add_* functions, instead of scanning through the layout for sliders. * Separated the copy-pasteable portion of the script. * A no-op for the DMX and LinnDrum layouts. * Fixes multitouch and slider "drift", and adds support for relative adjustments to the D70 sliders (they were using an older version of the script).
This commit is contained in:
parent
d96222a3f0
commit
15f9fa724a
@ -594,121 +594,140 @@ copyright-holders:m1macrophage
|
||||
<script><![CDATA[
|
||||
file:set_resolve_tags_callback(
|
||||
function()
|
||||
local id_port_index <const> = string.len("slider_knob_") + 1
|
||||
|
||||
-- State used by pointer handlers.
|
||||
local sliders = {} -- Info about all sliders (constant after initialization).
|
||||
local pointers = {} -- Pointer tracking state.
|
||||
|
||||
-- Gather relevant elements and inputs into `sliders`.
|
||||
local view = file.views["Default Layout"]
|
||||
for i = 1, #view.items do
|
||||
local item = view.items:at(i)
|
||||
if item.id ~= nil and string.find(item.id, "slider_knob_") == 1 then
|
||||
local slider_id = string.sub(item.id, id_port_index)
|
||||
|
||||
local port = file.device:ioport(slider_id)
|
||||
local field = nil
|
||||
if port ~= nil then
|
||||
for k, val in pairs(port.fields) do
|
||||
field = val
|
||||
break
|
||||
end
|
||||
if field == nil then
|
||||
print("LAYOUT ERROR - Port does not have a field: " .. slider_id)
|
||||
end
|
||||
else
|
||||
print("LAYOUT ERROR - Port not found: " .. slider_id)
|
||||
end
|
||||
|
||||
local slider = view.items["slider_" .. slider_id]
|
||||
if slider == nil then
|
||||
print("LAYOUT ERROR - Element: 'slider_" .. slider_id .. "' does not exist.")
|
||||
end
|
||||
|
||||
local slider_info = {}
|
||||
slider_info.slider = slider
|
||||
slider_info.knob = item
|
||||
slider_info.field = field
|
||||
table.insert(sliders, slider_info)
|
||||
end
|
||||
install_slider_callbacks(view)
|
||||
for i = 1, 16 do
|
||||
local pan_input = "pot_pan_" .. i
|
||||
add_vertical_slider(view, "slider_" .. pan_input, "slider_knob_" .. pan_input, pan_input)
|
||||
local gain_input = "pot_gain_" .. i
|
||||
add_vertical_slider(view, "slider_" .. gain_input, "slider_knob_" .. gain_input, gain_input)
|
||||
end
|
||||
|
||||
local function forget_pointers()
|
||||
pointers = {}
|
||||
end
|
||||
|
||||
local function pointer_lost(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function pointer_updated(type, id, dev, x, y, btn, dn, up, cnt)
|
||||
-- Button not pressed? Reset state of current pointer.
|
||||
if btn & 1 == 0 then
|
||||
pointers[id] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Button just pressed? Find affected slider, if any.
|
||||
if dn & 1 ~= 0 then
|
||||
for i = 1, #sliders do
|
||||
if sliders[i].knob.bounds:includes(x, y) then
|
||||
local pointer = {}
|
||||
pointer.selected_slider = i
|
||||
pointer.relative = true
|
||||
pointer.start_y = y
|
||||
pointer.start_value = sliders[i].field.user_value
|
||||
pointers[id] = pointer
|
||||
break
|
||||
elseif sliders[i].slider.bounds:includes(x, y) then
|
||||
local pointer = {}
|
||||
pointer.selected_slider = i
|
||||
pointer.relative = false
|
||||
pointers[id] = pointer
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- No slider selected by current pointer? Nothing to do.
|
||||
if pointers[id] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- A slider is selected. Update state and, indirectly,
|
||||
-- slider knob position, based on the pointer's Y position.
|
||||
-- It is assumed the attached IO field is an IPT_ADJUSTER
|
||||
-- with a range of 0-100 (the default).
|
||||
|
||||
local pointer = pointers[id]
|
||||
local slider_info = sliders[pointer.selected_slider]
|
||||
|
||||
local knob_half_height = slider_info.knob.bounds.height / 2
|
||||
local min_y = slider_info.slider.bounds.y0 + knob_half_height
|
||||
local max_y = slider_info.slider.bounds.y1 - knob_half_height
|
||||
|
||||
local new_value = 0
|
||||
if pointer.relative then
|
||||
-- User clicked on the knob. New value depends on how
|
||||
-- much the knob was dragged.
|
||||
new_value = pointer.start_value - 100 * (y - pointer.start_y) / (max_y - min_y)
|
||||
else
|
||||
-- User clicked elsewhere on the slider. New value
|
||||
-- depends on the absolute position of the click.
|
||||
new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
|
||||
end
|
||||
|
||||
new_value = math.floor(new_value + 0.5)
|
||||
if new_value < 0 then new_value = 0 end
|
||||
if new_value > 100 then new_value = 100 end
|
||||
slider_info.field.user_value = new_value
|
||||
end
|
||||
|
||||
view:set_pointer_updated_callback(pointer_updated)
|
||||
view:set_pointer_left_callback(pointer_lost)
|
||||
view:set_pointer_aborted_callback(pointer_lost)
|
||||
view:set_forget_pointers_callback(forget_pointers)
|
||||
end)
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Slider library starts.
|
||||
-- Can be copied as-is to other layouts.
|
||||
-----------------------------------------------------------------------
|
||||
local sliders = {} -- Stores slider information.
|
||||
local pointers = {} -- Tracks pointer state.
|
||||
|
||||
-- The knob's Y position must be animated using <animate inputtag="{port_name}">.
|
||||
-- The click area's vertical size must exactly span the range of the
|
||||
-- knob's movement.
|
||||
function add_vertical_slider(view, clickarea_id, knob_id, port_name)
|
||||
local slider = {}
|
||||
|
||||
slider.clickarea = view.items[clickarea_id]
|
||||
if slider.clickarea == nil then
|
||||
emu.print_error("Slider element: '" .. clickarea_id .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
slider.knob = view.items[knob_id]
|
||||
if slider.knob == nil then
|
||||
emu.print_error("Slider knob element: '" .. knob_id .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
local port = file.device:ioport(port_name)
|
||||
if port == nil then
|
||||
emu.print_error("Port: '" .. port_name .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
slider.field = nil
|
||||
for k, val in pairs(port.fields) do
|
||||
slider.field = val
|
||||
break
|
||||
end
|
||||
if slider.field == nil then
|
||||
emu.print_error("Port: '" .. port_name .."' does not seem to be an IPT_ADJUSTER.")
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(sliders, slider)
|
||||
end
|
||||
|
||||
local function pointer_updated(type, id, dev, x, y, btn, dn, up, cnt)
|
||||
-- If a button is not pressed, reset the state of the current pointer.
|
||||
if btn & 1 == 0 then
|
||||
pointers[id] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- If a button was just pressed, find the affected slider, if any.
|
||||
if dn & 1 ~= 0 then
|
||||
for i = 1, #sliders do
|
||||
if sliders[i].knob.bounds:includes(x, y) then
|
||||
pointers[id] = {
|
||||
selected_slider = i,
|
||||
relative = true,
|
||||
start_y = y,
|
||||
start_value = sliders[i].field.user_value }
|
||||
break
|
||||
elseif sliders[i].clickarea.bounds:includes(x, y) then
|
||||
pointers[id] = {
|
||||
selected_slider = i,
|
||||
relative = false }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If there is no slider selected by the current pointer, we are done.
|
||||
if pointers[id] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- A slider is selected. Update state and, indirectly, slider knob position,
|
||||
-- based on the pointer's Y position. It is assumed the attached IO field is
|
||||
-- an IPT_ADJUSTER with a range of 0-100 (the default).
|
||||
|
||||
local pointer = pointers[id]
|
||||
local slider = sliders[pointer.selected_slider]
|
||||
|
||||
local knob_half_height = slider.knob.bounds.height / 2
|
||||
local min_y = slider.clickarea.bounds.y0 + knob_half_height
|
||||
local max_y = slider.clickarea.bounds.y1 - knob_half_height
|
||||
|
||||
local new_value = 0
|
||||
if pointer.relative then
|
||||
-- User clicked on the knob. The new value will depend on how much the
|
||||
-- knob was dragged.
|
||||
new_value = pointer.start_value - 100 * (y - pointer.start_y) / (max_y - min_y)
|
||||
else
|
||||
-- User clicked elsewhere on the slider. The new value will depend on
|
||||
-- the absolute position of the click.
|
||||
new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
|
||||
end
|
||||
|
||||
new_value = math.floor(new_value + 0.5)
|
||||
if new_value < 0 then new_value = 0 end
|
||||
if new_value > 100 then new_value = 100 end
|
||||
slider.field.user_value = new_value
|
||||
end
|
||||
|
||||
local function pointer_left(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function pointer_aborted(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function forget_pointers()
|
||||
pointers = {}
|
||||
end
|
||||
|
||||
function install_slider_callbacks(view)
|
||||
view:set_pointer_updated_callback(pointer_updated)
|
||||
view:set_pointer_left_callback(pointer_left)
|
||||
view:set_pointer_aborted_callback(pointer_aborted)
|
||||
view:set_forget_pointers_callback(forget_pointers)
|
||||
end
|
||||
-----------------------------------------------------------------------
|
||||
-- Slider library ends.
|
||||
-----------------------------------------------------------------------
|
||||
]]></script>
|
||||
</mamelayout>
|
||||
|
||||
|
@ -92,13 +92,13 @@ copyright-holders:m1macrophage
|
||||
</element>
|
||||
|
||||
<group name="trimmer">
|
||||
<element ref="invisible-rect" id="slider_pitch_adj_~trimmer_id~">
|
||||
<element ref="invisible-rect" id="trimmer_~trimmer_id~">
|
||||
<bounds x="0" y="0" width="15" height="40"/>
|
||||
</element>
|
||||
<element ref="trimmer-rail">
|
||||
<bounds x="5" y="0" width="5" height="40"/>
|
||||
</element>
|
||||
<element ref="trimmer-knob" id="slider_knob_pitch_adj_~trimmer_id~">
|
||||
<element ref="trimmer-knob" id="trimmer_knob_~trimmer_id~">
|
||||
<animate inputtag="pitch_adj_~trimmer_id~" inputmask="0x7f"/>
|
||||
<bounds state="100" x="0" y="0" width="15" height="5"/>
|
||||
<bounds state="0" x="0" y="35" width="15" height="5"/>
|
||||
@ -117,13 +117,13 @@ copyright-holders:m1macrophage
|
||||
</element>
|
||||
|
||||
<group name="fader">
|
||||
<element id="slider_fader_p~fader_id~" ref="invisible-rect">
|
||||
<element id="fader_~fader_id~" ref="invisible-rect">
|
||||
<bounds x="15" y="0" width="35" height="162"/>
|
||||
</element>
|
||||
<element ref="black-rect"> <!-- slider rail -->
|
||||
<bounds x="29" y="9" width="7" height="144"/>
|
||||
</element>
|
||||
<element id="slider_knob_fader_p~fader_id~" ref="fader-knob">
|
||||
<element id="fader_knob_~fader_id~" ref="fader-knob">
|
||||
<animate inputtag="fader_p~fader_id~" inputmask="0x7f"/>
|
||||
<bounds state="100" x="15" y="0" width="35" height="35"/>
|
||||
<bounds state="0" x="15" y="127" width="35" height="35"/>
|
||||
@ -355,8 +355,8 @@ copyright-holders:m1macrophage
|
||||
</element>
|
||||
</repeat>
|
||||
|
||||
<!-- Tunning trimmers -->
|
||||
<collection name="tunning">
|
||||
<!-- Tuning trimmers -->
|
||||
<collection name="tuning">
|
||||
<element ref="text-tune">
|
||||
<bounds x="20" y="110" width="30" height="10"/>
|
||||
</element>
|
||||
@ -541,121 +541,144 @@ copyright-holders:m1macrophage
|
||||
<script><![CDATA[
|
||||
file:set_resolve_tags_callback(
|
||||
function()
|
||||
local id_port_index <const> = string.len("slider_knob_") + 1
|
||||
|
||||
-- State used by pointer handlers.
|
||||
local sliders = {} -- Info about all sliders (constant after initialization).
|
||||
local pointers = {} -- Pointer tracking state.
|
||||
|
||||
-- Gather relevant elements and inputs into `sliders`.
|
||||
local view = file.views["Default Layout"]
|
||||
for i = 1, #view.items do
|
||||
local item = view.items:at(i)
|
||||
if item.id ~= nil and string.find(item.id, "slider_knob_") == 1 then
|
||||
local slider_id = string.sub(item.id, id_port_index)
|
||||
install_slider_callbacks(view)
|
||||
|
||||
local port = file.device:ioport(slider_id)
|
||||
local field = nil
|
||||
if port ~= nil then
|
||||
for k, val in pairs(port.fields) do
|
||||
field = val
|
||||
break
|
||||
end
|
||||
if field == nil then
|
||||
print("LAYOUT ERROR - Port does not have a field: " .. slider_id)
|
||||
end
|
||||
else
|
||||
print("LAYOUT ERROR - Port not found: " .. slider_id)
|
||||
end
|
||||
|
||||
local slider = view.items["slider_" .. slider_id]
|
||||
if slider == nil then
|
||||
print("LAYOUT ERROR - Element: 'slider_" .. slider_id .. "' does not exist.")
|
||||
end
|
||||
|
||||
local slider_info = {}
|
||||
slider_info.slider = slider
|
||||
slider_info.knob = item
|
||||
slider_info.field = field
|
||||
table.insert(sliders, slider_info)
|
||||
end
|
||||
-- Configure volume faders.
|
||||
for i = 1, 10 do
|
||||
add_vertical_slider(view, "fader_" .. i, "fader_knob_" .. i, "fader_p" .. i)
|
||||
end
|
||||
|
||||
local function forget_pointers()
|
||||
pointers = {}
|
||||
-- Configure tuning trimmers.
|
||||
for i = 1, 8 do
|
||||
add_vertical_slider(view, "trimmer_" .. i, "trimmer_knob_" .. i, "pitch_adj_" .. i)
|
||||
end
|
||||
|
||||
local function pointer_lost(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function pointer_updated(type, id, dev, x, y, btn, dn, up, cnt)
|
||||
-- Button not pressed? Reset state of current pointer.
|
||||
if btn & 1 == 0 then
|
||||
pointers[id] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Button just pressed? Find affected slider, if any.
|
||||
if dn & 1 ~= 0 then
|
||||
for i = 1, #sliders do
|
||||
if sliders[i].knob.bounds:includes(x, y) then
|
||||
local pointer = {}
|
||||
pointer.selected_slider = i
|
||||
pointer.relative = true
|
||||
pointer.start_y = y
|
||||
pointer.start_value = sliders[i].field.user_value
|
||||
pointers[id] = pointer
|
||||
break
|
||||
elseif sliders[i].slider.bounds:includes(x, y) then
|
||||
local pointer = {}
|
||||
pointer.selected_slider = i
|
||||
pointer.relative = false
|
||||
pointers[id] = pointer
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- No slider selected by current pointer? Nothing to do.
|
||||
if pointers[id] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- A slider is selected. Update state and, indirectly,
|
||||
-- slider knob position, based on the pointer's Y position.
|
||||
-- It is assumed the attached IO field is an IPT_ADJUSTER
|
||||
-- with a range of 0-100 (the default).
|
||||
|
||||
local pointer = pointers[id]
|
||||
local slider_info = sliders[pointer.selected_slider]
|
||||
|
||||
local knob_half_height = slider_info.knob.bounds.height / 2
|
||||
local min_y = slider_info.slider.bounds.y0 + knob_half_height
|
||||
local max_y = slider_info.slider.bounds.y1 - knob_half_height
|
||||
|
||||
local new_value = 0
|
||||
if pointer.relative then
|
||||
-- User clicked on the knob. New value depends on how
|
||||
-- much the knob was dragged.
|
||||
new_value = pointer.start_value - 100 * (y - pointer.start_y) / (max_y - min_y)
|
||||
else
|
||||
-- User clicked elsewhere on the slider. New value
|
||||
-- depends on the absolute position of the click.
|
||||
new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
|
||||
end
|
||||
|
||||
new_value = math.floor(new_value + 0.5)
|
||||
if new_value < 0 then new_value = 0 end
|
||||
if new_value > 100 then new_value = 100 end
|
||||
slider_info.field.user_value = new_value
|
||||
end
|
||||
|
||||
view:set_pointer_updated_callback(pointer_updated)
|
||||
view:set_pointer_left_callback(pointer_lost)
|
||||
view:set_pointer_aborted_callback(pointer_lost)
|
||||
view:set_forget_pointers_callback(forget_pointers)
|
||||
end)
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Slider library starts.
|
||||
-- Can be copied as-is to other layouts.
|
||||
-----------------------------------------------------------------------
|
||||
local sliders = {} -- Stores slider information.
|
||||
local pointers = {} -- Tracks pointer state.
|
||||
|
||||
-- The knob's Y position must be animated using <animate inputtag="{port_name}">.
|
||||
-- The click area's vertical size must exactly span the range of the
|
||||
-- knob's movement.
|
||||
function add_vertical_slider(view, clickarea_id, knob_id, port_name)
|
||||
local slider = {}
|
||||
|
||||
slider.clickarea = view.items[clickarea_id]
|
||||
if slider.clickarea == nil then
|
||||
emu.print_error("Slider element: '" .. clickarea_id .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
slider.knob = view.items[knob_id]
|
||||
if slider.knob == nil then
|
||||
emu.print_error("Slider knob element: '" .. knob_id .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
local port = file.device:ioport(port_name)
|
||||
if port == nil then
|
||||
emu.print_error("Port: '" .. port_name .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
slider.field = nil
|
||||
for k, val in pairs(port.fields) do
|
||||
slider.field = val
|
||||
break
|
||||
end
|
||||
if slider.field == nil then
|
||||
emu.print_error("Port: '" .. port_name .."' does not seem to be an IPT_ADJUSTER.")
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(sliders, slider)
|
||||
end
|
||||
|
||||
local function pointer_updated(type, id, dev, x, y, btn, dn, up, cnt)
|
||||
-- If a button is not pressed, reset the state of the current pointer.
|
||||
if btn & 1 == 0 then
|
||||
pointers[id] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- If a button was just pressed, find the affected slider, if any.
|
||||
if dn & 1 ~= 0 then
|
||||
for i = 1, #sliders do
|
||||
if sliders[i].knob.bounds:includes(x, y) then
|
||||
pointers[id] = {
|
||||
selected_slider = i,
|
||||
relative = true,
|
||||
start_y = y,
|
||||
start_value = sliders[i].field.user_value }
|
||||
break
|
||||
elseif sliders[i].clickarea.bounds:includes(x, y) then
|
||||
pointers[id] = {
|
||||
selected_slider = i,
|
||||
relative = false }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If there is no slider selected by the current pointer, we are done.
|
||||
if pointers[id] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- A slider is selected. Update state and, indirectly, slider knob position,
|
||||
-- based on the pointer's Y position. It is assumed the attached IO field is
|
||||
-- an IPT_ADJUSTER with a range of 0-100 (the default).
|
||||
|
||||
local pointer = pointers[id]
|
||||
local slider = sliders[pointer.selected_slider]
|
||||
|
||||
local knob_half_height = slider.knob.bounds.height / 2
|
||||
local min_y = slider.clickarea.bounds.y0 + knob_half_height
|
||||
local max_y = slider.clickarea.bounds.y1 - knob_half_height
|
||||
|
||||
local new_value = 0
|
||||
if pointer.relative then
|
||||
-- User clicked on the knob. The new value will depend on how much the
|
||||
-- knob was dragged.
|
||||
new_value = pointer.start_value - 100 * (y - pointer.start_y) / (max_y - min_y)
|
||||
else
|
||||
-- User clicked elsewhere on the slider. The new value will depend on
|
||||
-- the absolute position of the click.
|
||||
new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
|
||||
end
|
||||
|
||||
new_value = math.floor(new_value + 0.5)
|
||||
if new_value < 0 then new_value = 0 end
|
||||
if new_value > 100 then new_value = 100 end
|
||||
slider.field.user_value = new_value
|
||||
end
|
||||
|
||||
local function pointer_left(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function pointer_aborted(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function forget_pointers()
|
||||
pointers = {}
|
||||
end
|
||||
|
||||
function install_slider_callbacks(view)
|
||||
view:set_pointer_updated_callback(pointer_updated)
|
||||
view:set_pointer_left_callback(pointer_left)
|
||||
view:set_pointer_aborted_callback(pointer_aborted)
|
||||
view:set_forget_pointers_callback(forget_pointers)
|
||||
end
|
||||
-----------------------------------------------------------------------
|
||||
-- Slider library ends.
|
||||
-----------------------------------------------------------------------
|
||||
]]></script>
|
||||
</mamelayout>
|
||||
|
||||
|
@ -248,7 +248,7 @@ copyright-holders:Felipe Sanches, m1macrophage
|
||||
|
||||
<element ref="slider-wider-well" id="SLIDER4"><bounds x="200" y="349" width="43" height="97"/></element>
|
||||
<element ref="slider-well"><bounds x="217" y="357" width="10" height="82"/></element>
|
||||
<element ref="slider-knob">
|
||||
<element ref="slider-knob" id="SLIDER-KNOB4">
|
||||
<animate inputtag="SLIDER4" inputmask="0x7f"/>
|
||||
<bounds state="100" x="203" y="352" width="37" height="24"/>
|
||||
<bounds state="0" x="203" y="417" width="37" height="24"/>
|
||||
@ -256,7 +256,7 @@ copyright-holders:Felipe Sanches, m1macrophage
|
||||
|
||||
<element ref="slider-wider-well" id="SLIDER5"><bounds x="269" y="349" width="43" height="97"/></element>
|
||||
<element ref="slider-well"><bounds x="286" y="357" width="10" height="82"/></element>
|
||||
<element ref="slider-knob">
|
||||
<element ref="slider-knob" id="SLIDER-KNOB5">
|
||||
<animate inputtag="SLIDER5" inputmask="0x7f"/>
|
||||
<bounds state="100" x="272" y="352" width="37" height="24"/>
|
||||
<bounds state="0" x="272" y="417" width="37" height="24"/>
|
||||
@ -264,7 +264,7 @@ copyright-holders:Felipe Sanches, m1macrophage
|
||||
|
||||
<element ref="slider-wider-well" id="SLIDER6"><bounds x="337" y="349" width="43" height="97"/></element>
|
||||
<element ref="slider-well"><bounds x="354" y="357" width="10" height="82"/></element>
|
||||
<element ref="slider-knob">
|
||||
<element ref="slider-knob" id="SLIDER-KNOB6">
|
||||
<animate inputtag="SLIDER6" inputmask="0x7f"/>
|
||||
<bounds state="100" x="340" y="352" width="37" height="24"/>
|
||||
<bounds state="0" x="340" y="417" width="37" height="24"/>
|
||||
@ -272,7 +272,7 @@ copyright-holders:Felipe Sanches, m1macrophage
|
||||
|
||||
<element ref="slider-wider-well" id="SLIDER7"><bounds x="405" y="349" width="43" height="97"/></element>
|
||||
<element ref="slider-well"><bounds x="422" y="357" width="10" height="82"/></element>
|
||||
<element ref="slider-knob">
|
||||
<element ref="slider-knob" id="SLIDER-KNOB7">
|
||||
<animate inputtag="SLIDER7" inputmask="0x7f"/>
|
||||
<bounds state="100" x="408" y="352" width="37" height="24"/>
|
||||
<bounds state="0" x="408" y="417" width="37" height="24"/>
|
||||
@ -550,84 +550,137 @@ copyright-holders:Felipe Sanches, m1macrophage
|
||||
<script><![CDATA[
|
||||
file:set_resolve_tags_callback(
|
||||
function()
|
||||
-- These constants need to match the "slider" and
|
||||
-- "slider-knob" element attributes.
|
||||
local slider_height <const> = 82
|
||||
local knob_height <const> = 24
|
||||
local knob_slider_delta_y <const> = 5 -- slider y - knob y
|
||||
|
||||
local slider_deadzone <const> = math.floor(knob_height / 2) - knob_slider_delta_y
|
||||
|
||||
-- Local state used by the pointer update handler.
|
||||
local sliders = {}
|
||||
local slider_fields = {}
|
||||
local selected = 0
|
||||
|
||||
-- Gather relevant elements and inputs into local state.
|
||||
local view = file.views["Internal Layout"]
|
||||
for i = 1, #view.items do
|
||||
local item = view.items:at(i)
|
||||
if item.id ~= nil and string.find(item.id, "SLIDER") == 1 then
|
||||
local port_tag = item.id
|
||||
local port = file.device:ioport(port_tag)
|
||||
local field = nil
|
||||
if port ~= nil then
|
||||
for k, val in pairs(port.fields) do
|
||||
field = val
|
||||
break
|
||||
end
|
||||
if field == nil then
|
||||
print("LAYOUT ERROR - Port does not have a field: " .. port_tag)
|
||||
end
|
||||
else
|
||||
print("LAYOUT ERROR - Port not found: " .. port_tag)
|
||||
end
|
||||
table.insert(sliders, item)
|
||||
table.insert(slider_fields, field)
|
||||
install_slider_callbacks(view)
|
||||
|
||||
for i = 4, 7 do
|
||||
add_vertical_slider(view, "SLIDER" .. i, "SLIDER-KNOB" .. i, "SLIDER" .. i)
|
||||
end
|
||||
end)
|
||||
-----------------------------------------------------------------------
|
||||
-- Slider library starts.
|
||||
-- Can be copied as-is to other layouts.
|
||||
-----------------------------------------------------------------------
|
||||
local sliders = {} -- Stores slider information.
|
||||
local pointers = {} -- Tracks pointer state.
|
||||
|
||||
-- The knob's Y position must be animated using <animate inputtag="{port_name}">.
|
||||
-- The click area's vertical size must exactly span the range of the
|
||||
-- knob's movement.
|
||||
function add_vertical_slider(view, clickarea_id, knob_id, port_name)
|
||||
local slider = {}
|
||||
|
||||
slider.clickarea = view.items[clickarea_id]
|
||||
if slider.clickarea == nil then
|
||||
emu.print_error("Slider element: '" .. clickarea_id .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
slider.knob = view.items[knob_id]
|
||||
if slider.knob == nil then
|
||||
emu.print_error("Slider knob element: '" .. knob_id .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
local port = file.device:ioport(port_name)
|
||||
if port == nil then
|
||||
emu.print_error("Port: '" .. port_name .. "' not found.")
|
||||
return
|
||||
end
|
||||
|
||||
slider.field = nil
|
||||
for k, val in pairs(port.fields) do
|
||||
slider.field = val
|
||||
break
|
||||
end
|
||||
if slider.field == nil then
|
||||
emu.print_error("Port: '" .. port_name .."' does not seem to be an IPT_ADJUSTER.")
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(sliders, slider)
|
||||
end
|
||||
|
||||
local function pointer_updated(type, id, dev, x, y, btn, dn, up, cnt)
|
||||
-- If a button is not pressed, reset the state of the current pointer.
|
||||
if btn & 1 == 0 then
|
||||
pointers[id] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- If a button was just pressed, find the affected slider, if any.
|
||||
if dn & 1 ~= 0 then
|
||||
for i = 1, #sliders do
|
||||
if sliders[i].knob.bounds:includes(x, y) then
|
||||
pointers[id] = {
|
||||
selected_slider = i,
|
||||
relative = true,
|
||||
start_y = y,
|
||||
start_value = sliders[i].field.user_value }
|
||||
break
|
||||
elseif sliders[i].clickarea.bounds:includes(x, y) then
|
||||
pointers[id] = {
|
||||
selected_slider = i,
|
||||
relative = false }
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
view:set_pointer_updated_callback(
|
||||
function(type, id, dev, x, y, btn, dn, up, cnt)
|
||||
-- No button pressed. Reset state.
|
||||
if btn & 1 == 0 then
|
||||
selected = 0
|
||||
return
|
||||
end
|
||||
-- If there is no slider selected by the current pointer, we are done.
|
||||
if pointers[id] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- Button just pressed. Find affected slider.
|
||||
if dn & 1 ~= 0 then
|
||||
for i = 1, #sliders do
|
||||
if sliders[i].bounds:includes(x, y) then
|
||||
selected = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- A slider is selected. Update state and, indirectly, slider knob position,
|
||||
-- based on the pointer's Y position. It is assumed the attached IO field is
|
||||
-- an IPT_ADJUSTER with a range of 0-100 (the default).
|
||||
|
||||
-- No slider selected. Nothing to do.
|
||||
if selected <= 0 then
|
||||
return
|
||||
end
|
||||
local pointer = pointers[id]
|
||||
local slider = sliders[pointer.selected_slider]
|
||||
|
||||
-- A slider is selected. Update state and, indirectly,
|
||||
-- slider knob position, based on the pointer's Y position.
|
||||
local knob_half_height = slider.knob.bounds.height / 2
|
||||
local min_y = slider.clickarea.bounds.y0 + knob_half_height
|
||||
local max_y = slider.clickarea.bounds.y1 - knob_half_height
|
||||
|
||||
-- It is assumed the attached IO field is an IPT_ADJUSTER
|
||||
-- with a range of 0-100 (the default).
|
||||
local new_value = 0
|
||||
if pointer.relative then
|
||||
-- User clicked on the knob. The new value will depend on how much the
|
||||
-- knob was dragged.
|
||||
new_value = pointer.start_value - 100 * (y - pointer.start_y) / (max_y - min_y)
|
||||
else
|
||||
-- User clicked elsewhere on the slider. The new value will depend on
|
||||
-- the absolute position of the click.
|
||||
new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
|
||||
end
|
||||
|
||||
local bbox = sliders[selected].bounds
|
||||
local scale_factor = bbox.height / slider_height
|
||||
local min_y = bbox.y0 + slider_deadzone * scale_factor
|
||||
local max_y = bbox.y1 - slider_deadzone * scale_factor
|
||||
new_value = math.floor(new_value + 0.5)
|
||||
if new_value < 0 then new_value = 0 end
|
||||
if new_value > 100 then new_value = 100 end
|
||||
slider.field.user_value = new_value
|
||||
end
|
||||
|
||||
local new_value = 100 - 100 * (y - min_y) / (max_y - min_y)
|
||||
new_value = math.floor(new_value + 0.5)
|
||||
if new_value < 0 then new_value = 0 end
|
||||
if new_value > 100 then new_value = 100 end
|
||||
slider_fields[selected].user_value = new_value
|
||||
end)
|
||||
end)
|
||||
local function pointer_left(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function pointer_aborted(type, id, dev, x, y, up, cnt)
|
||||
pointers[id] = nil
|
||||
end
|
||||
|
||||
local function forget_pointers()
|
||||
pointers = {}
|
||||
end
|
||||
|
||||
function install_slider_callbacks(view)
|
||||
view:set_pointer_updated_callback(pointer_updated)
|
||||
view:set_pointer_left_callback(pointer_left)
|
||||
view:set_pointer_aborted_callback(pointer_aborted)
|
||||
view:set_forget_pointers_callback(forget_pointers)
|
||||
end
|
||||
-----------------------------------------------------------------------
|
||||
-- Slider library ends.
|
||||
-----------------------------------------------------------------------
|
||||
]]></script>
|
||||
|
||||
</mamelayout>
|
||||
|
Loading…
Reference in New Issue
Block a user