summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emu/render.cpp436
-rw-r--r--src/emu/render.h55
-rw-r--r--src/emu/rendlay.cpp45
-rw-r--r--src/emu/rendlay.h41
-rw-r--r--src/emu/ui/uimain.h4
-rw-r--r--src/emu/uiinput.cpp262
-rw-r--r--src/emu/uiinput.h88
-rw-r--r--src/frontend/mame/luaengine_input.cpp9
-rw-r--r--src/frontend/mame/luaengine_mem.cpp2
-rw-r--r--src/frontend/mame/luaengine_render.cpp56
-rw-r--r--src/frontend/mame/ui/about.cpp15
-rw-r--r--src/frontend/mame/ui/about.h3
-rw-r--r--src/frontend/mame/ui/analogipt.cpp395
-rw-r--r--src/frontend/mame/ui/analogipt.h32
-rw-r--r--src/frontend/mame/ui/auditmenu.cpp2
-rw-r--r--src/frontend/mame/ui/auditmenu.h2
-rw-r--r--src/frontend/mame/ui/confswitch.cpp38
-rw-r--r--src/frontend/mame/ui/confswitch.h4
-rw-r--r--src/frontend/mame/ui/custui.cpp6
-rw-r--r--src/frontend/mame/ui/custui.h6
-rw-r--r--src/frontend/mame/ui/datmenu.cpp287
-rw-r--r--src/frontend/mame/ui/datmenu.h18
-rw-r--r--src/frontend/mame/ui/devopt.cpp8
-rw-r--r--src/frontend/mame/ui/devopt.h1
-rw-r--r--src/frontend/mame/ui/dirmenu.cpp8
-rw-r--r--src/frontend/mame/ui/filecreate.cpp2
-rw-r--r--src/frontend/mame/ui/filecreate.h2
-rw-r--r--src/frontend/mame/ui/filemngr.cpp2
-rw-r--r--src/frontend/mame/ui/filemngr.h2
-rw-r--r--src/frontend/mame/ui/filesel.cpp225
-rw-r--r--src/frontend/mame/ui/filesel.h60
-rw-r--r--src/frontend/mame/ui/floppycntrl.cpp72
-rw-r--r--src/frontend/mame/ui/floppycntrl.h2
-rw-r--r--src/frontend/mame/ui/imgcntrl.cpp61
-rw-r--r--src/frontend/mame/ui/imgcntrl.h7
-rw-r--r--src/frontend/mame/ui/info.cpp10
-rw-r--r--src/frontend/mame/ui/info.h2
-rw-r--r--src/frontend/mame/ui/inputdevices.cpp2
-rw-r--r--src/frontend/mame/ui/inputmap.cpp2
-rw-r--r--src/frontend/mame/ui/inputmap.h2
-rw-r--r--src/frontend/mame/ui/menu.cpp1442
-rw-r--r--src/frontend/mame/ui/menu.h189
-rw-r--r--src/frontend/mame/ui/miscmenu.cpp2
-rw-r--r--src/frontend/mame/ui/selector.cpp2
-rw-r--r--src/frontend/mame/ui/selector.h2
-rw-r--r--src/frontend/mame/ui/selgame.cpp190
-rw-r--r--src/frontend/mame/ui/selgame.h8
-rw-r--r--src/frontend/mame/ui/selmenu.cpp2967
-rw-r--r--src/frontend/mame/ui/selmenu.h185
-rw-r--r--src/frontend/mame/ui/selsoft.cpp114
-rw-r--r--src/frontend/mame/ui/selsoft.h5
-rw-r--r--src/frontend/mame/ui/simpleselgame.cpp214
-rw-r--r--src/frontend/mame/ui/simpleselgame.h5
-rw-r--r--src/frontend/mame/ui/sliders.cpp5
-rw-r--r--src/frontend/mame/ui/sliders.h2
-rw-r--r--src/frontend/mame/ui/slotopt.cpp2
-rw-r--r--src/frontend/mame/ui/slotopt.h2
-rw-r--r--src/frontend/mame/ui/state.cpp27
-rw-r--r--src/frontend/mame/ui/state.h6
-rw-r--r--src/frontend/mame/ui/submenu.cpp2
-rw-r--r--src/frontend/mame/ui/submenu.h2
-rw-r--r--src/frontend/mame/ui/text.cpp12
-rw-r--r--src/frontend/mame/ui/text.h2
-rw-r--r--src/frontend/mame/ui/textbox.cpp329
-rw-r--r--src/frontend/mame/ui/textbox.h30
-rw-r--r--src/frontend/mame/ui/ui.cpp321
-rw-r--r--src/frontend/mame/ui/ui.h79
-rw-r--r--src/frontend/mame/ui/utils.cpp27
-rw-r--r--src/frontend/mame/ui/utils.h30
-rw-r--r--src/frontend/mame/ui/viewgfx.cpp123
-rw-r--r--src/mame/layout/alphie.lay2
-rw-r--r--src/mame/layout/autorace.lay2
-rw-r--r--src/mame/layout/bcheetah.lay2
-rw-r--r--src/mame/layout/compperf.lay6
-rw-r--r--src/mame/layout/copycat.lay4
-rw-r--r--src/mame/layout/matchme.lay2
-rw-r--r--src/mame/layout/mdndclab.lay147
-rw-r--r--src/mame/layout/microvision.lay2
-rw-r--r--src/mame/layout/novag_savant.lay2
-rw-r--r--src/mame/layout/novag_supercon.lay18
-rw-r--r--src/mame/layout/saitek_mark5.lay4
-rw-r--r--src/mame/layout/saitek_mark6.lay4
-rw-r--r--src/mame/layout/saitek_ssystem3.lay4
-rw-r--r--src/mame/layout/saitek_ssystem4.lay2
-rw-r--r--src/mame/layout/simon.lay4
-rw-r--r--src/mame/layout/ssimon.lay6
-rw-r--r--src/mame/layout/strobe.lay2
-rw-r--r--src/mame/layout/tmbaskb.lay2
-rw-r--r--src/mame/layout/tmvolleyb.lay2
-rw-r--r--src/mame/layout/vclock3.lay2
-rw-r--r--src/mame/layout/videoart.lay2
-rw-r--r--src/osd/interface/uievents.h61
-rw-r--r--src/osd/modules/debugger/debugimgui.cpp247
-rw-r--r--src/osd/modules/input/input_dinput.cpp2
-rw-r--r--src/osd/modules/input/input_rawinput.cpp80
-rw-r--r--src/osd/modules/input/input_sdl.cpp9
-rw-r--r--src/osd/modules/input/input_win32.cpp242
-rw-r--r--src/osd/modules/input/input_wincommon.h5
-rw-r--r--src/osd/modules/osdwindow.h195
-rw-r--r--src/osd/sdl/osdsdl.cpp244
-rw-r--r--src/osd/sdl/osdsdl.h11
-rw-r--r--src/osd/sdl/sdlopts.cpp3
-rw-r--r--src/osd/sdl/sdlopts.h4
-rw-r--r--src/osd/sdl/window.cpp372
-rw-r--r--src/osd/sdl/window.h73
-rw-r--r--src/osd/windows/window.cpp565
-rw-r--r--src/osd/windows/window.h46
-rw-r--r--src/osd/windows/winmain.h11
108 files changed, 7792 insertions, 3201 deletions
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 617b1bf3756..2b9efa1c7fa 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -57,6 +57,7 @@
#include "util/xmlfile.h"
#include <algorithm>
+#include <limits>
@@ -99,6 +100,40 @@ struct render_target::object_transform
};
+struct render_target::pointer_info
+{
+ pointer_info() noexcept
+ : type(osd::ui_event_handler::pointer::UNKNOWN)
+ , oldpos(std::numeric_limits<float>::min(), std::numeric_limits<float>::min())
+ , newpos(std::numeric_limits<float>::min(), std::numeric_limits<float>::min())
+ , oldbuttons(0U)
+ , newbuttons(0U)
+ , edges(0U, 0U)
+ {
+ }
+
+ osd::ui_event_handler::pointer type;
+ std::pair<float, float> oldpos;
+ std::pair<float, float> newpos;
+ u32 oldbuttons, newbuttons;
+ std::pair<size_t, size_t> edges;
+};
+
+
+struct render_target::hit_test
+{
+ hit_test() noexcept
+ : inbounds(0U, 0U)
+ , hit(0U)
+ {
+ }
+
+ std::pair<u64, u64> inbounds;
+ u64 hit;
+};
+
+
+
//**************************************************************************
// GLOBAL VARIABLES
@@ -883,19 +918,21 @@ render_container::user_settings::user_settings()
// render_target - constructor
//-------------------------------------------------
-render_target::render_target(render_manager &manager, const internal_layout *layoutfile, u32 flags)
- : render_target(manager, layoutfile, flags, CONSTRUCTOR_IMPL)
+render_target::render_target(render_manager &manager, render_container *ui, const internal_layout *layoutfile, u32 flags)
+ : render_target(manager, ui, layoutfile, flags, CONSTRUCTOR_IMPL)
{
}
-render_target::render_target(render_manager &manager, util::xml::data_node const &layout, u32 flags)
- : render_target(manager, layout, flags, CONSTRUCTOR_IMPL)
+render_target::render_target(render_manager &manager, render_container *ui, util::xml::data_node const &layout, u32 flags)
+ : render_target(manager, ui, layout, flags, CONSTRUCTOR_IMPL)
{
}
-template <typename T> render_target::render_target(render_manager &manager, T &&layout, u32 flags, constructor_impl_t)
+template <typename T>
+render_target::render_target(render_manager &manager, render_container *ui, T &&layout, u32 flags, constructor_impl_t)
: m_next(nullptr)
, m_manager(manager)
+ , m_ui_container(ui)
, m_curview(0U)
, m_flags(flags)
, m_listindex(0)
@@ -1030,9 +1067,12 @@ void render_target::set_view(unsigned viewindex)
{
if (m_views.size() > viewindex)
{
+ forget_pointers();
m_curview = viewindex;
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
current_view().preload();
+ m_clickable_items.clear();
+ m_clickable_items.resize(current_view().interactive_items().size());
}
}
@@ -1050,6 +1090,229 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight)
//-------------------------------------------------
+// pointer_updated - pointer activity within
+// target
+//-------------------------------------------------
+
+void render_target::pointer_updated(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 buttons,
+ u32 pressed,
+ u32 released,
+ s16 clicks)
+{
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_updated(type, ptrid, device, target_f.first, target_f.second, buttons, pressed, released, clicks);
+
+ // 64 pointers ought to be enough for anyone
+ if (64 <= ptrid)
+ return;
+
+ // just store the updated pointer state
+ if (m_pointers.size() <= ptrid)
+ m_pointers.resize(ptrid + 1);
+ m_pointers[ptrid].type = type;
+ m_pointers[ptrid].newpos = target_f;
+ m_pointers[ptrid].newbuttons = buttons;
+}
+
+
+//-------------------------------------------------
+// pointer_left - pointer left target normally
+//-------------------------------------------------
+
+void render_target::pointer_left(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
+{
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_left(type, ptrid, device, target_f.first, target_f.second, released, clicks);
+
+ // store the updated state if relevant
+ if (m_pointers.size() > ptrid)
+ {
+ m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
+ m_pointers[ptrid].newbuttons = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// pointer_aborted - pointer left target
+// abnormally
+//-------------------------------------------------
+
+void render_target::pointer_aborted(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
+{
+ // let layout scripts handle pointer input
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_aborted(type, ptrid, device, target_f.first, target_f.second, released, clicks);
+
+ // store the updated state if relevant
+ if (m_pointers.size() > ptrid)
+ {
+ m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
+ m_pointers[ptrid].newbuttons = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// forget_pointers - stop processing pointer
+// input
+//-------------------------------------------------
+
+void render_target::forget_pointers()
+{
+ current_view().forget_pointers();
+ m_pointers.clear();
+ for (size_t i = 0; m_clickable_items.size() > i; ++i)
+ {
+ if (m_clickable_items[i].hit)
+ {
+ layout_view_item const &item(current_view().interactive_items()[i]);
+ auto const [port, mask] = item.input_tag_and_mask();
+ ioport_field *const field(port ? port->field(mask) : nullptr);
+ if (field)
+ field->set_value(0);
+ }
+ m_clickable_items[i] = hit_test();
+ }
+}
+
+
+//-------------------------------------------------
+// update_pointer_fields - update inputs for new
+// pointer state
+//-------------------------------------------------
+
+void render_target::update_pointer_fields()
+{
+ auto const &x_edges(current_view().interactive_edges_x());
+ auto const &y_edges(current_view().interactive_edges_y());
+
+ // update items bounds intersection checks for pointers
+ for (size_t ptr = 0; m_pointers.size() > ptr; ++ptr)
+ {
+ auto const [x, y] = m_pointers[ptr].newpos;
+ auto edges = m_pointers[ptr].edges;
+
+ // check for moving across horizontal edges
+ if (x < m_pointers[ptr].oldpos.first)
+ {
+ while (edges.first && (x < x_edges[edges.first - 1].position()))
+ {
+ --edges.first;
+ auto const &edge(x_edges[edges.first]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.first |= u64(1) << ptr;
+ else
+ m_clickable_items[edge.index()].inbounds.first &= ~(u64(1) << ptr);
+ }
+ }
+ else if (x > m_pointers[ptr].oldpos.first)
+ {
+ while ((x_edges.size() > edges.first) && (x >= x_edges[edges.first].position()))
+ {
+ auto const &edge(x_edges[edges.first]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.first &= ~(u64(1) << ptr);
+ else
+ m_clickable_items[edge.index()].inbounds.first |= u64(1) << ptr;
+ ++edges.first;
+ }
+ }
+
+ // check for moving across vertical edges
+ if (y < m_pointers[ptr].oldpos.second)
+ {
+ while (edges.second && (y < y_edges[edges.second - 1].position()))
+ {
+ --edges.second;
+ auto const &edge(y_edges[edges.second]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.second |= u64(1) << ptr;
+ else
+ m_clickable_items[edge.index()].inbounds.second &= ~(u64(1) << ptr);
+ }
+ }
+ else if (y > m_pointers[ptr].oldpos.second)
+ {
+ while ((y_edges.size() > edges.second) && (y >= y_edges[edges.second].position()))
+ {
+ auto const &edge(y_edges[edges.second]);
+ if (edge.trailing())
+ m_clickable_items[edge.index()].inbounds.second &= ~(u64(1) << ptr);
+ else
+ m_clickable_items[edge.index()].inbounds.second |= u64(1) << ptr;
+ ++edges.second;
+ }
+ }
+
+ // update the pointer's state
+ m_pointers[ptr].oldpos = m_pointers[ptr].newpos;
+ m_pointers[ptr].oldbuttons = m_pointers[ptr].newbuttons;
+ m_pointers[ptr].edges = edges;
+ }
+
+ // update item hit states
+ u64 obscured(0U);
+ for (size_t i = 0; m_clickable_items.size() > i; ++i)
+ {
+ layout_view_item const &item(current_view().interactive_items()[i]);
+ u64 const inbounds(m_clickable_items[i].inbounds.first & m_clickable_items[i].inbounds.second);
+ u64 hit(m_clickable_items[i].hit);
+ for (unsigned ptr = 0; m_pointers.size() > ptr; ++ptr)
+ {
+ pointer_info const &pointer(m_pointers[ptr]);
+ bool const prefilter(BIT(~obscured & inbounds, ptr));
+ if (!prefilter || !BIT(pointer.newbuttons, 0) || !item.bounds().includes(pointer.newpos.first, pointer.newpos.second))
+ {
+ hit &= ~(u64(1) << ptr);
+ }
+ else
+ {
+ hit |= u64(1) << ptr;
+ if (!item.clickthrough())
+ obscured |= u64(1) << ptr;
+ }
+ }
+
+ // update field state
+ if (bool(hit) != bool(m_clickable_items[i].hit))
+ {
+ auto const [port, mask] = item.input_tag_and_mask();
+ ioport_field *const field(port ? port->field(mask) : nullptr);
+ if (field)
+ {
+ if (hit)
+ field->set_value(1);
+ else
+ field->clear_value();
+ }
+ }
+ m_clickable_items[i].hit = hit;
+ }
+}
+
+
+//-------------------------------------------------
// set_visibility_toggle - show or hide selected
// parts of a view
//-------------------------------------------------
@@ -1061,7 +1324,7 @@ void render_target::set_visibility_toggle(unsigned index, bool enable)
m_views[m_curview].second |= u32(1) << index;
else
m_views[m_curview].second &= ~(u32(1) << index);
- current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ update_layer_config();
current_view().preload();
}
@@ -1454,8 +1717,8 @@ render_primitive_list &render_target::get_primitives()
}
}
- // process the UI if we are the UI target
- if (is_ui_target())
+ // process UI elements if applicable
+ if (m_ui_container)
{
// compute the transform for the UI
object_transform ui_xform;
@@ -1468,7 +1731,7 @@ render_primitive_list &render_target::get_primitives()
ui_xform.no_center = false;
// add UI elements
- add_container_primitives(list, root_xform, ui_xform, m_manager.ui_container(), BLENDMODE_ALPHA);
+ add_container_primitives(list, root_xform, ui_xform, *m_ui_container, BLENDMODE_ALPHA);
}
// optimize the list before handing it off
@@ -1489,7 +1752,7 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
std::pair<float, float> target_f(map_point_internal(target_x, target_y));
// explicitly check for the UI container
- if (&container == &m_manager.ui_container())
+ if (&container == m_ui_container)
{
// this hit test went against the UI container
if ((target_f.first >= 0.0f) && (target_f.first < 1.0f) && (target_f.second >= 0.0f) && (target_f.second < 1.0f))
@@ -1536,74 +1799,6 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
//-------------------------------------------------
-// map_point_input - attempts to map a point on
-// the specified render_target to an input port
-// field, if possible
-//-------------------------------------------------
-
-bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y)
-{
- std::pair<float, float> target_f(map_point_internal(target_x, target_y));
- if (m_orientation & ORIENTATION_FLIP_X)
- target_f.first = 1.0f - target_f.first;
- if (m_orientation & ORIENTATION_FLIP_Y)
- target_f.second = 1.0f - target_f.second;
- if (m_orientation & ORIENTATION_SWAP_XY)
- std::swap(target_f.first, target_f.second);
-
- auto const &items(current_view().interactive_items());
- m_hit_test.resize(items.size() * 2);
- std::fill(m_hit_test.begin(), m_hit_test.end(), false);
-
- for (auto const &edge : current_view().interactive_edges_x())
- {
- if ((edge.position() > target_f.first) || ((edge.position() == target_f.first) && edge.trailing()))
- break;
- else
- m_hit_test[edge.index()] = !edge.trailing();
- }
-
- for (auto const &edge : current_view().interactive_edges_y())
- {
- if ((edge.position() > target_f.second) || ((edge.position() == target_f.second) && edge.trailing()))
- break;
- else
- m_hit_test[items.size() + edge.index()] = !edge.trailing();
- }
-
- for (unsigned i = 0; items.size() > i; ++i)
- {
- if (m_hit_test[i] && m_hit_test[items.size() + i])
- {
- layout_view_item &item(items[i]);
- render_bounds const bounds(item.bounds());
- if (bounds.includes(target_f.first, target_f.second))
- {
- if (item.has_input())
- {
- // point successfully mapped
- std::tie(input_port, input_mask) = item.input_tag_and_mask();
- input_x = (target_f.first - bounds.x0) / bounds.width();
- input_y = (target_f.second - bounds.y0) / bounds.height();
- return true;
- }
- else
- {
- break;
- }
- }
- }
- }
-
- // default to point not mapped
- input_port = nullptr;
- input_mask = 0;
- input_x = input_y = -1.0f;
- return false;
-}
-
-
-//-------------------------------------------------
// invalidate_all - if any of our primitive lists
// contain a reference to the given pointer,
// clear them
@@ -1632,7 +1827,7 @@ void render_target::resolve_tags()
for (layout_file &file : m_filelist)
file.resolve_tags();
- current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ update_layer_config();
current_view().preload();
}
@@ -1644,7 +1839,10 @@ void render_target::resolve_tags()
void render_target::update_layer_config()
{
+ forget_pointers();
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ m_clickable_items.clear();
+ m_clickable_items.resize(current_view().interactive_items().size());
}
@@ -2620,6 +2818,25 @@ std::pair<float, float> render_target::map_point_internal(s32 target_x, s32 targ
//-------------------------------------------------
+// map_point_layout - map point from screen
+// coordinates to layout coordinates
+//-------------------------------------------------
+
+std::pair<float, float> render_target::map_point_layout(s32 target_x, s32 target_y)
+{
+ using std::swap;
+ std::pair<float, float> result(map_point_internal(target_x, target_y));
+ if (m_orientation & ORIENTATION_FLIP_X)
+ result.first = 1.0f - result.first;
+ if (m_orientation & ORIENTATION_FLIP_Y)
+ result.second = 1.0f - result.second;
+ if (m_orientation & ORIENTATION_SWAP_XY)
+ swap(result.first, result.second);
+ return result;
+}
+
+
+//-------------------------------------------------
// view_name - return the name of the indexed
// view, or nullptr if it doesn't exist
//-------------------------------------------------
@@ -2698,12 +2915,11 @@ void render_target::config_load(util::xml::data_node const *targetnode)
set_orientation(orientation_add(rotate, orientation()));
// apply the opposite orientation to the UI
- if (is_ui_target())
+ if (m_ui_container)
{
- render_container &ui_container = m_manager.ui_container();
- render_container::user_settings settings = ui_container.get_user_settings();
+ render_container::user_settings settings = m_ui_container->get_user_settings();
settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation);
- ui_container.set_user_settings(settings);
+ m_ui_container->set_user_settings(settings);
}
}
@@ -2743,6 +2959,8 @@ void render_target::config_load(util::xml::data_node const *targetnode)
{
current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
current_view().preload();
+ m_clickable_items.clear();
+ m_clickable_items.resize(current_view().interactive_items().size());
}
}
}
@@ -3104,7 +3322,6 @@ render_manager::render_manager(running_machine &machine)
, m_ui_target(nullptr)
, m_live_textures(0)
, m_texture_id(0)
- , m_ui_container(std::make_unique<render_container>(*this))
{
// register callbacks
machine.configuration().config_register(
@@ -3125,7 +3342,7 @@ render_manager::render_manager(running_machine &machine)
render_manager::~render_manager()
{
// free all the containers since they may own textures
- m_ui_container.reset();
+ m_ui_containers.clear();
m_screen_container_list.clear();
// better not be any outstanding textures when we die
@@ -3181,12 +3398,14 @@ float render_manager::max_update_rate() const
render_target *render_manager::target_alloc(const internal_layout *layoutfile, u32 flags)
{
- return &m_targetlist.append(*new render_target(*this, layoutfile, flags));
+ render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this);
+ return &m_targetlist.append(*new render_target(*this, ui, layoutfile, flags));
}
render_target *render_manager::target_alloc(util::xml::data_node const &layout, u32 flags)
{
- return &m_targetlist.append(*new render_target(*this, layout, flags));
+ render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this);
+ return &m_targetlist.append(*new render_target(*this, ui, layout, flags));
}
@@ -3223,35 +3442,56 @@ render_target *render_manager::target_by_index(int index) const
float render_manager::ui_aspect(render_container *rc)
{
- int orient;
+ // work out if this is a UI container
+ render_target *target = nullptr;
+ if (!rc)
+ {
+ target = &ui_target();
+ rc = target->ui_container();
+ assert(rc);
+ }
+ else
+ {
+ for (render_target &t : m_targetlist)
+ {
+ if (t.ui_container() == rc)
+ {
+ target = &t;
+ break;
+ }
+ }
+ }
+
float aspect;
- if (rc == m_ui_container.get() || rc == nullptr) {
- // ui container, aggregated multi-screen target
+ if (target)
+ {
+ // UI container, aggregated multi-screen target
- orient = orientation_add(m_ui_target->orientation(), m_ui_container->orientation());
// based on the orientation of the target, compute height/width or width/height
+ int const orient = orientation_add(target->orientation(), rc->orientation());
if (!(orient & ORIENTATION_SWAP_XY))
- aspect = (float)m_ui_target->height() / (float)m_ui_target->width();
+ aspect = float(target->height()) / float(target->width());
else
- aspect = (float)m_ui_target->width() / (float)m_ui_target->height();
+ aspect = float(target->width()) / float(target->height());
// if we have a valid pixel aspect, apply that and return
- if (m_ui_target->pixel_aspect() != 0.0f)
+ if (target->pixel_aspect() != 0.0f)
{
- float pixel_aspect = m_ui_target->pixel_aspect();
+ float pixel_aspect = target->pixel_aspect();
if (orient & ORIENTATION_SWAP_XY)
pixel_aspect = 1.0f / pixel_aspect;
return aspect /= pixel_aspect;
}
-
- } else {
+ }
+ else
+ {
// single screen container
- orient = rc->orientation();
// based on the orientation of the target, compute height/width or width/height
+ int const orient = rc->orientation();
if (!(orient & ORIENTATION_SWAP_XY))
aspect = (float)rc->screen()->visible_area().height() / (float)rc->screen()->visible_area().width();
else
diff --git a/src/emu/render.h b/src/emu/render.h
index 2faa5f46394..c4c0f504edd 100644
--- a/src/emu/render.h
+++ b/src/emu/render.h
@@ -48,6 +48,8 @@
#include "rendertypes.h"
+#include "interface/uievents.h"
+
#include <cmath>
#include <list>
#include <memory>
@@ -492,14 +494,15 @@ class render_target
friend class render_manager;
// construction/destruction
- render_target(render_manager &manager, const internal_layout *layoutfile = nullptr, u32 flags = 0);
- render_target(render_manager &manager, util::xml::data_node const &layout, u32 flags = 0);
+ render_target(render_manager &manager, render_container *ui, const internal_layout *layoutfile, u32 flags);
+ render_target(render_manager &manager, render_container *ui, util::xml::data_node const &layout, u32 flags);
~render_target();
public:
// getters
render_target *next() const { return m_next; }
render_manager &manager() const { return m_manager; }
+ render_container *ui_container() const { return m_ui_container; }
u32 width() const { return m_width; }
u32 height() const { return m_height; }
float pixel_aspect() const { return m_pixel_aspect; }
@@ -525,6 +528,13 @@ public:
void set_keepaspect(bool keepaspect) { m_keepaspect = keepaspect; }
void set_scale_mode(int scale_mode) { m_scale_mode = scale_mode; }
+ // pointer input handling
+ void pointer_updated(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 buttons, u32 pressed, u32 released, s16 clicks);
+ void pointer_left(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks);
+ void pointer_aborted(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks);
+ void forget_pointers();
+ void update_pointer_fields();
+
// layer config getters
bool screen_overlay_enabled() const { return m_layerconfig.screen_overlay_enabled(); }
bool zoom_to_screen() const { return m_layerconfig.zoom_to_screen(); }
@@ -550,7 +560,6 @@ public:
// hit testing
bool map_point_container(s32 target_x, s32 target_y, render_container &container, float &container_x, float &container_y);
- bool map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y);
// reference tracking
void invalidate_all(void *refptr);
@@ -559,15 +568,24 @@ public:
void resolve_tags();
private:
+ // constants
+ static inline constexpr int NUM_PRIMLISTS = 3;
+ static inline constexpr int MAX_CLEAR_EXTENTS = 1000;
+
using view_mask_pair = std::pair<layout_view &, u32>;
using view_mask_vector = std::vector<view_mask_pair>;
// private classes declared in render.cpp
struct object_transform;
+ struct pointer_info;
+ struct hit_test;
+
+ using pointer_info_vector = std::vector<pointer_info>;
+ using hit_test_vector = std::vector<hit_test>;
// internal helpers
enum constructor_impl_t { CONSTRUCTOR_IMPL };
- template <typename T> render_target(render_manager &manager, T&& layout, u32 flags, constructor_impl_t);
+ template <typename T> render_target(render_manager &manager, render_container *ui, T&& layout, u32 flags, constructor_impl_t);
void update_layer_config();
void load_layout_files(const internal_layout *layoutfile, bool singlefile);
void load_layout_files(util::xml::data_node const &rootnode, bool singlefile);
@@ -578,6 +596,7 @@ private:
void add_container_primitives(render_primitive_list &list, const object_transform &root_xform, const object_transform &xform, render_container &container, int blendmode);
void add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_view_item &item);
std::pair<float, float> map_point_internal(s32 target_x, s32 target_y);
+ std::pair<float, float> map_point_layout(s32 target_x, s32 target_y);
// config callbacks
void config_load(util::xml::data_node const *targetnode);
@@ -593,13 +612,10 @@ private:
void add_clear_extents(render_primitive_list &list);
void add_clear_and_optimize_primitive_list(render_primitive_list &list);
- // constants
- static constexpr int NUM_PRIMLISTS = 3;
- static constexpr int MAX_CLEAR_EXTENTS = 1000;
-
// internal state
render_target * m_next; // link to next target
render_manager & m_manager; // reference to our owning manager
+ render_container *const m_ui_container; // container for drawing UI elements
std::list<layout_file> m_filelist; // list of layout files
view_mask_vector m_views; // views we consider
unsigned m_curview; // current view index
@@ -618,7 +634,8 @@ private:
float m_max_refresh; // maximum refresh rate, 0 or if none
int m_orientation; // orientation
render_layer_config m_layerconfig; // layer configuration
- std::vector<bool> m_hit_test; // used when mapping points to inputs
+ pointer_info_vector m_pointers; // state of pointers over this target
+ hit_test_vector m_clickable_items; // for tracking clicked elements
layout_view * m_base_view; // the view at the time of first frame
int m_base_orientation; // the orientation at the time of first frame
render_layer_config m_base_layerconfig; // the layer configuration at the time of first frame
@@ -665,7 +682,7 @@ public:
float ui_aspect(render_container *rc = nullptr);
// UI containers
- render_container &ui_container() const { assert(m_ui_container != nullptr); return *m_ui_container; }
+ render_container &ui_container() const { assert(ui_target().ui_container()); return *ui_target().ui_container(); }
// textures
render_texture *texture_alloc(texture_scaler_func scaler = nullptr, void *param = nullptr);
@@ -686,20 +703,20 @@ private:
void config_save(config_type cfg_type, util::xml::data_node *parentnode);
// internal state
- running_machine & m_machine; // reference back to the machine
+ running_machine & m_machine; // reference back to the machine
// array of live targets
- simple_list<render_target> m_targetlist; // list of targets
- render_target * m_ui_target; // current UI target
+ simple_list<render_target> m_targetlist; // list of targets
+ render_target * m_ui_target; // current UI target
// texture lists
- u32 m_live_textures; // number of live textures
- u64 m_texture_id; // rolling texture ID counter
- fixed_allocator<render_texture> m_texture_allocator;// texture allocator
+ u32 m_live_textures; // number of live textures
+ u64 m_texture_id; // rolling texture ID counter
+ fixed_allocator<render_texture> m_texture_allocator; // texture allocator
- // containers for the UI and for screens
- std::unique_ptr<render_container> m_ui_container; // UI container
- std::list<render_container> m_screen_container_list; // list of containers for the screen
+ // containers for UI elements and for screens
+ std::list<render_container> m_ui_containers; // containers for drawing UI elements
+ std::list<render_container> m_screen_container_list; // list of containers for the screen
};
#endif // MAME_EMU_RENDER_H
diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp
index c6e053fc713..de17b5dae6f 100644
--- a/src/emu/rendlay.cpp
+++ b/src/emu/rendlay.cpp
@@ -4251,6 +4251,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen)
// sort edges of interactive items
LOGMASKED(LOG_INTERACTIVE_ITEMS, "Recalculated view '%s' with %u interactive items\n",
name(), m_interactive_items.size());
+ //std::reverse(m_interactive_items.begin(), m_interactive_items.end()); TODO: flip hit test order to match visual order
m_interactive_edges_x.reserve(m_interactive_items.size() * 2);
m_interactive_edges_y.reserve(m_interactive_items.size() * 2);
for (unsigned i = 0; m_interactive_items.size() > i; ++i)
@@ -4315,6 +4316,50 @@ void layout_view::set_recomputed_callback(recomputed_delegate &&handler)
//-------------------------------------------------
+// set_pointer_updated_callback - set handler
+// called for pointer input
+//-------------------------------------------------
+
+void layout_view::set_pointer_updated_callback(pointer_updated_delegate &&handler)
+{
+ m_pointer_updated = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_pointer_left_callback - set handler for
+// pointer leaving normally
+//-------------------------------------------------
+
+void layout_view::set_pointer_left_callback(pointer_left_delegate &&handler)
+{
+ m_pointer_left = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_pointer_aborted_callback - set handler for
+// pointer leaving abnormally
+//-------------------------------------------------
+
+void layout_view::set_pointer_aborted_callback(pointer_left_delegate &&handler)
+{
+ m_pointer_aborted = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_forget_pointers_callback - set handler for
+// abandoning pointer input
+//-------------------------------------------------
+
+void layout_view::set_forget_pointers_callback(forget_pointers_delegate &&handler)
+{
+ m_forget_pointers = std::move(handler);
+}
+
+
+//-------------------------------------------------
// preload - perform expensive loading upfront
// for visible elements
//-------------------------------------------------
diff --git a/src/emu/rendlay.h b/src/emu/rendlay.h
index 0391bc6e961..7edeb4b40c5 100644
--- a/src/emu/rendlay.h
+++ b/src/emu/rendlay.h
@@ -16,6 +16,8 @@
#include "rendertypes.h"
#include "screen.h"
+#include "interface/uievents.h"
+
#include <array>
#include <functional>
#include <map>
@@ -436,6 +438,9 @@ public:
using prepare_items_delegate = delegate<void ()>;
using preload_delegate = delegate<void ()>;
using recomputed_delegate = delegate<void ()>;
+ using pointer_updated_delegate = delegate<void (osd::ui_event_handler::pointer, u16, u16, float, float, u32, u32, u32, s16)>;
+ using pointer_left_delegate = delegate<void (osd::ui_event_handler::pointer, u16, u16, float, float, u32, s16)>;
+ using forget_pointers_delegate = delegate<void ()>;
using item = layout_view_item;
using item_list = std::list<item>;
@@ -524,9 +529,13 @@ public:
bool has_art() const { return m_has_art; }
// set handlers
- void set_prepare_items_callback(prepare_items_delegate &&handler);
- void set_preload_callback(preload_delegate &&handler);
- void set_recomputed_callback(recomputed_delegate &&handler);
+ void set_prepare_items_callback(prepare_items_delegate &&handler) ATTR_COLD;
+ void set_preload_callback(preload_delegate &&handler) ATTR_COLD;
+ void set_recomputed_callback(recomputed_delegate &&handler) ATTR_COLD;
+ void set_pointer_updated_callback(pointer_updated_delegate &&handler) ATTR_COLD;
+ void set_pointer_left_callback(pointer_left_delegate &&handler) ATTR_COLD;
+ void set_pointer_aborted_callback(pointer_left_delegate &&handler) ATTR_COLD;
+ void set_forget_pointers_callback(forget_pointers_delegate &&handler) ATTR_COLD;
// operations
void prepare_items();
@@ -536,6 +545,28 @@ public:
// resolve tags, if any
void resolve_tags();
+ // pointer input
+ void pointer_updated(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, float x, float y, u32 buttons, u32 pressed, u32 released, s16 clicks)
+ {
+ if (!m_pointer_updated.isnull())
+ m_pointer_updated(type, ptrid, device, x, y, buttons, pressed, released, clicks);
+ }
+ void pointer_left(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, float x, float y, u32 released, s16 clicks)
+ {
+ if (!m_pointer_left.isnull())
+ m_pointer_left(type, ptrid, device, x, y, released, clicks);
+ }
+ void pointer_aborted(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, float x, float y, u32 released, s16 clicks)
+ {
+ if (!m_pointer_aborted.isnull())
+ m_pointer_aborted(type, ptrid, device, x, y, released, clicks);
+ }
+ void forget_pointers()
+ {
+ if (!m_forget_pointers.isnull())
+ m_forget_pointers();
+ }
+
private:
struct layer_lists;
@@ -576,6 +607,10 @@ private:
prepare_items_delegate m_prepare_items; // prepare items for adding to render container
preload_delegate m_preload; // additional actions when visible items change
recomputed_delegate m_recomputed; // additional actions on resizing/visibility change
+ pointer_updated_delegate m_pointer_updated; // pointer state updated
+ pointer_left_delegate m_pointer_left; // pointer left normally
+ pointer_left_delegate m_pointer_aborted; // pointer left abnormally
+ forget_pointers_delegate m_forget_pointers; // stop processing pointer input
// cold items
std::string m_name; // display name for the view
diff --git a/src/emu/ui/uimain.h b/src/emu/ui/uimain.h
index bf10957d743..e45c71f9468 100644
--- a/src/emu/ui/uimain.h
+++ b/src/emu/ui/uimain.h
@@ -13,6 +13,8 @@
#pragma once
+#include <functional>
+
/***************************************************************************
TYPE DEFINITIONS
@@ -35,6 +37,8 @@ public:
virtual void menu_reset() { }
+ virtual bool set_ui_event_handler(std::function<bool ()> &&handler) { return false; }
+
template <typename Format, typename... Params> void popup_time(int seconds, Format &&fmt, Params &&... args);
protected:
diff --git a/src/emu/uiinput.cpp b/src/emu/uiinput.cpp
index 138bf67055d..12ba8a08bb0 100644
--- a/src/emu/uiinput.cpp
+++ b/src/emu/uiinput.cpp
@@ -36,19 +36,11 @@ enum
ui_input_manager::ui_input_manager(running_machine &machine)
: m_machine(machine)
, m_presses_enabled(true)
- , m_current_mouse_target(nullptr)
- , m_current_mouse_x(-1)
- , m_current_mouse_y(-1)
- , m_current_mouse_down(false)
- , m_current_mouse_field(nullptr)
, m_events_start(0)
, m_events_end(0)
{
std::fill(std::begin(m_next_repeat), std::end(m_next_repeat), 0);
std::fill(std::begin(m_seqpressed), std::end(m_seqpressed), 0);
-
- // add a frame callback to poll inputs
- machine.add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&ui_input_manager::frame_update, this));
}
@@ -58,12 +50,12 @@ ui_input_manager::ui_input_manager(running_machine &machine)
***************************************************************************/
/*-------------------------------------------------
- frame_update - looks through pressed
- input as per events pushed our way and posts
+ check_ui_inputs - looks through pressed input
+ as per events pushed our way and posts
corresponding IPT_UI_* events
-------------------------------------------------*/
-void ui_input_manager::frame_update()
+void ui_input_manager::check_ui_inputs()
{
// update the state of all the UI keys
for (ioport_type code = ioport_type(IPT_UI_FIRST + 1); code < IPT_UI_LAST; ++code)
@@ -80,22 +72,6 @@ void ui_input_manager::frame_update()
m_seqpressed[code] = false;
}
}
-
- // perform mouse hit testing
- ioport_field *mouse_field = m_current_mouse_down ? find_mouse_field() : nullptr;
- if (m_current_mouse_field != mouse_field)
- {
- // clear the old field if there was one
- if (m_current_mouse_field != nullptr)
- m_current_mouse_field->set_value(0);
-
- // set the new field if it exists and isn't already being pressed
- if (mouse_field != nullptr && !mouse_field->digital_value())
- mouse_field->set_value(1);
-
- // update internal state
- m_current_mouse_field = mouse_field;
- }
}
@@ -106,37 +82,6 @@ void ui_input_manager::frame_update()
bool ui_input_manager::push_event(ui_event evt)
{
- // some pre-processing (this is an icky place to do this stuff!)
- switch (evt.event_type)
- {
- case ui_event::type::MOUSE_MOVE:
- m_current_mouse_target = evt.target;
- m_current_mouse_x = evt.mouse_x;
- m_current_mouse_y = evt.mouse_y;
- break;
-
- case ui_event::type::MOUSE_LEAVE:
- if (m_current_mouse_target == evt.target)
- {
- m_current_mouse_target = nullptr;
- m_current_mouse_x = -1;
- m_current_mouse_y = -1;
- }
- break;
-
- case ui_event::type::MOUSE_DOWN:
- m_current_mouse_down = true;
- break;
-
- case ui_event::type::MOUSE_UP:
- m_current_mouse_down = false;
- break;
-
- default:
- /* do nothing */
- break;
- }
-
// is the queue filled up?
if ((m_events_end + 1) % std::size(m_events) == m_events_start)
return false;
@@ -186,64 +131,12 @@ void ui_input_manager::reset()
}
-/*-------------------------------------------------
- find_mouse - retrieves the current
- location of the mouse
--------------------------------------------------*/
-
-render_target *ui_input_manager::find_mouse(s32 *x, s32 *y, bool *button) const
-{
- if (x != nullptr)
- *x = m_current_mouse_x;
- if (y != nullptr)
- *y = m_current_mouse_y;
- if (button != nullptr)
- *button = m_current_mouse_down;
- return m_current_mouse_target;
-}
-
-
-/*-------------------------------------------------
- find_mouse_field - retrieves the input field
- the mouse is currently pointing at
--------------------------------------------------*/
-
-ioport_field *ui_input_manager::find_mouse_field() const
-{
- // map the point and determine what was hit
- if (m_current_mouse_target != nullptr)
- {
- ioport_port *port = nullptr;
- ioport_value mask;
- float x, y;
- if (m_current_mouse_target->map_point_input(m_current_mouse_x, m_current_mouse_y, port, mask, x, y))
- {
- if (port != nullptr)
- return port->field(mask);
- }
- }
- return nullptr;
-}
-
-
/***************************************************************************
USER INTERFACE SEQUENCE READING
***************************************************************************/
/*-------------------------------------------------
- pressed - return true if a key down
- for the given user interface sequence is
- detected
--------------------------------------------------*/
-
-bool ui_input_manager::pressed(int code)
-{
- return pressed_repeat(code, 0);
-}
-
-
-/*-------------------------------------------------
pressed_repeat - return true if a key
down for the given user interface sequence is
detected, or if autorepeat at the given speed
@@ -272,7 +165,7 @@ bool ui_input_manager::pressed_repeat(int code, int speed)
/* if this is an autorepeat case, set a 1x delay and leave pressed = 1 */
else if (speed > 0 && (osd_ticks() + tps - m_next_repeat[code]) >= tps)
{
- // In the autorepeatcase, we need to double check the key is still pressed
+ // In the autorepeat case, we need to double-check the key is still pressed
// as there can be a delay between the key polling and our processing of the event
m_seqpressed[code] = machine().ioport().type_pressed(ioport_type(code));
pressed = (m_seqpressed[code] == SEQ_PRESSED_TRUE);
@@ -319,105 +212,84 @@ void ui_input_manager::push_window_defocus_event(render_target *target)
}
/*-------------------------------------------------
- push_mouse_move_event - pushes a mouse
- move event to the specified render_target
--------------------------------------------------*/
-
-void ui_input_manager::push_mouse_move_event(render_target *target, s32 x, s32 y)
-{
- ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_MOVE;
- event.target = target;
- event.mouse_x = x;
- event.mouse_y = y;
- push_event(event);
-}
-
-/*-------------------------------------------------
- push_mouse_leave_event - pushes a
- mouse leave event to the specified render_target
--------------------------------------------------*/
-
-void ui_input_manager::push_mouse_leave_event(render_target *target)
-{
- ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_LEAVE;
- event.target = target;
- push_event(event);
-}
-
-/*-------------------------------------------------
- push_mouse_down_event - pushes a mouse
- down event to the specified render_target
--------------------------------------------------*/
-
-void ui_input_manager::push_mouse_down_event(render_target *target, s32 x, s32 y)
-{
- ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_DOWN;
- event.target = target;
- event.mouse_x = x;
- event.mouse_y = y;
- push_event(event);
-}
-
-/*-------------------------------------------------
- push_mouse_down_event - pushes a mouse
- down event to the specified render_target
--------------------------------------------------*/
-
-void ui_input_manager::push_mouse_up_event(render_target *target, s32 x, s32 y)
-{
- ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_UP;
- event.target = target;
- event.mouse_x = x;
- event.mouse_y = y;
- push_event(event);
-}
-
-/*-------------------------------------------------
-push_mouse_down_event - pushes a mouse
-down event to the specified render_target
+ push_pointer_update - pushes a pointer update
+ event to the specified render_target
-------------------------------------------------*/
-void ui_input_manager::push_mouse_rdown_event(render_target *target, s32 x, s32 y)
+void ui_input_manager::push_pointer_update(
+ render_target *target,
+ pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 buttons,
+ u32 pressed,
+ u32 released,
+ s16 clicks)
{
ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_RDOWN;
+ event.event_type = ui_event::type::POINTER_UPDATE;
event.target = target;
- event.mouse_x = x;
- event.mouse_y = y;
+ event.pointer_type = type;
+ event.pointer_id = ptrid;
+ event.pointer_device = device;
+ event.pointer_x = x;
+ event.pointer_y = y;
+ event.pointer_buttons = buttons;
+ event.pointer_pressed = pressed;
+ event.pointer_released = released;
+ event.pointer_clicks = clicks;
push_event(event);
}
-/*-------------------------------------------------
-push_mouse_down_event - pushes a mouse
-down event to the specified render_target
--------------------------------------------------*/
-
-void ui_input_manager::push_mouse_rup_event(render_target *target, s32 x, s32 y)
+void ui_input_manager::push_pointer_leave(
+ render_target *target,
+ pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
{
ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_RUP;
+ event.event_type = ui_event::type::POINTER_LEAVE;
event.target = target;
- event.mouse_x = x;
- event.mouse_y = y;
+ event.pointer_type = type;
+ event.pointer_id = ptrid;
+ event.pointer_device = device;
+ event.pointer_x = x;
+ event.pointer_y = y;
+ event.pointer_buttons = 0U;
+ event.pointer_pressed = 0U;
+ event.pointer_released = released;
+ event.pointer_clicks = clicks;
push_event(event);
}
-/*-------------------------------------------------
- push_mouse_double_click_event - pushes
- a mouse double-click event to the specified
- render_target
--------------------------------------------------*/
-void ui_input_manager::push_mouse_double_click_event(render_target *target, s32 x, s32 y)
+void ui_input_manager::push_pointer_abort(
+ render_target *target,
+ pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
{
ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_DOUBLE_CLICK;
+ event.event_type = ui_event::type::POINTER_ABORT;
event.target = target;
- event.mouse_x = x;
- event.mouse_y = y;
+ event.pointer_type = type;
+ event.pointer_id = ptrid;
+ event.pointer_device = device;
+ event.pointer_x = x;
+ event.pointer_y = y;
+ event.pointer_buttons = 0U;
+ event.pointer_pressed = 0U;
+ event.pointer_released = released;
+ event.pointer_clicks = clicks;
push_event(event);
}
@@ -439,7 +311,7 @@ void ui_input_manager::push_char_event(render_target *target, char32_t ch)
wheel event to the specified render_target
-------------------------------------------------*/
-void ui_input_manager::push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int ucNumLines)
+void ui_input_manager::push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int lines)
{
ui_event event = { ui_event::type::NONE };
event.event_type = ui_event::type::MOUSE_WHEEL;
@@ -447,7 +319,7 @@ void ui_input_manager::push_mouse_wheel_event(render_target *target, s32 x, s32
event.mouse_x = x;
event.mouse_y = y;
event.zdelta = delta;
- event.num_lines = ucNumLines;
+ event.num_lines = lines;
push_event(event);
}
diff --git a/src/emu/uiinput.h b/src/emu/uiinput.h
index 3fc89d7a4ff..54170bad7ec 100644
--- a/src/emu/uiinput.h
+++ b/src/emu/uiinput.h
@@ -13,6 +13,8 @@
#pragma once
+#include "interface/uievents.h"
+
/***************************************************************************
TYPE DEFINITIONS
@@ -25,17 +27,15 @@ struct ui_event
NONE,
WINDOW_FOCUS,
WINDOW_DEFOCUS,
- MOUSE_MOVE,
- MOUSE_LEAVE,
- MOUSE_DOWN,
- MOUSE_UP,
- MOUSE_RDOWN,
- MOUSE_RUP,
- MOUSE_DOUBLE_CLICK,
MOUSE_WHEEL,
+ POINTER_UPDATE,
+ POINTER_LEAVE,
+ POINTER_ABORT,
IME_CHAR
};
+ using pointer = osd::ui_event_handler::pointer;
+
type event_type;
render_target * target;
s32 mouse_x;
@@ -44,17 +44,27 @@ struct ui_event
char32_t ch;
short zdelta;
int num_lines;
+
+ pointer pointer_type; // type of input controlling this pointer
+ u16 pointer_id; // pointer ID - will be recycled aggressively
+ u16 pointer_device; // for grouping pointers for multi-touch gesture recognition
+ s32 pointer_x; // pointer X coordinate
+ s32 pointer_y; // pointer Y coordinate
+ u32 pointer_buttons; // currently depressed buttons
+ u32 pointer_pressed; // buttons pressed since last update (primary action in LSB)
+ u32 pointer_released; // buttons released since last update (primary action in LSB)
+ s16 pointer_clicks; // positive for multi-click, negative on release if turned into hold or drag
};
// ======================> ui_input_manager
-class ui_input_manager
+class ui_input_manager final : public osd::ui_event_handler
{
public:
// construction/destruction
ui_input_manager(running_machine &machine);
- void frame_update();
+ void check_ui_inputs();
// pushes a single event onto the queue
bool push_event(ui_event event);
@@ -68,17 +78,13 @@ public:
// clears all outstanding events
void reset();
- // retrieves the current location of the mouse
- render_target *find_mouse(s32 *x, s32 *y, bool *button) const;
- ioport_field *find_mouse_field() const;
-
- // return true if a key down for the given user interface sequence is detected
- bool pressed(int code);
-
// enable/disable UI key presses
bool presses_enabled() const { return m_presses_enabled; }
void set_presses_enabled(bool enabled) { m_presses_enabled = enabled; }
+ // return true if a key down for the given user interface sequence is detected
+ bool pressed(int code) { return pressed_repeat(code, 0); }
+
// return true if a key down for the given user interface sequence is detected, or if autorepeat at the given speed is triggered
bool pressed_repeat(int code, int speed);
@@ -86,44 +92,32 @@ public:
running_machine &machine() const { return m_machine; }
// queueing events
- void push_window_focus_event(render_target *target);
- void push_window_defocus_event(render_target *target);
- void push_mouse_move_event(render_target *target, s32 x, s32 y);
- void push_mouse_leave_event(render_target *target);
- void push_mouse_down_event(render_target *target, s32 x, s32 y);
- void push_mouse_up_event(render_target *target, s32 x, s32 y);
- void push_mouse_rdown_event(render_target *target, s32 x, s32 y);
- void push_mouse_rup_event(render_target *target, s32 x, s32 y);
- void push_mouse_double_click_event(render_target *target, s32 x, s32 y);
- void push_char_event(render_target *target, char32_t ch);
- void push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int ucNumLines);
+ virtual void push_window_focus_event(render_target *target) override;
+ virtual void push_window_defocus_event(render_target *target) override;
+ virtual void push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int lines) override;
+ virtual void push_pointer_update(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 buttons, u32 pressed, u32 released, s16 clicks) override;
+ virtual void push_pointer_leave(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) override;
+ virtual void push_pointer_abort(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) override;
+ virtual void push_char_event(render_target *target, char32_t ch) override;
void mark_all_as_pressed();
private:
-
// constants
- static constexpr unsigned EVENT_QUEUE_SIZE = 128;
+ static constexpr unsigned EVENT_QUEUE_SIZE = 256;
// internal state
- running_machine & m_machine; // reference to our machine
-
- // pressed states; retrieved with ui_input_pressed()
- bool m_presses_enabled;
- osd_ticks_t m_next_repeat[IPT_COUNT];
- u8 m_seqpressed[IPT_COUNT];
-
- // mouse position/info
- render_target * m_current_mouse_target;
- s32 m_current_mouse_x;
- s32 m_current_mouse_y;
- bool m_current_mouse_down;
- ioport_field * m_current_mouse_field;
-
- // popped states; ring buffer of ui_events
- ui_event m_events[EVENT_QUEUE_SIZE];
- int m_events_start;
- int m_events_end;
+ running_machine & m_machine;
+
+ // pressed states; retrieved with pressed() or pressed_repeat()
+ bool m_presses_enabled;
+ osd_ticks_t m_next_repeat[IPT_COUNT];
+ u8 m_seqpressed[IPT_COUNT];
+
+ // ring buffer of ui_events
+ ui_event m_events[EVENT_QUEUE_SIZE];
+ int m_events_start;
+ int m_events_end;
};
#endif // MAME_EMU_UIINPUT_H
diff --git a/src/frontend/mame/luaengine_input.cpp b/src/frontend/mame/luaengine_input.cpp
index 42fe2d49d78..04db2a13e84 100644
--- a/src/frontend/mame/luaengine_input.cpp
+++ b/src/frontend/mame/luaengine_input.cpp
@@ -513,15 +513,6 @@ void lua_engine::initialize_input(sol::table &emu)
auto uiinput_type = sol().registry().new_usertype<ui_input_manager>("uiinput", sol::no_constructor);
uiinput_type.set_function("reset", &ui_input_manager::reset);
- uiinput_type.set_function(
- "find_mouse",
- [] (ui_input_manager &ui)
- {
- int32_t x, y;
- bool button;
- render_target *rt = ui.find_mouse(&x, &y, &button);
- return std::make_tuple(x, y, button, rt);
- });
uiinput_type.set_function("pressed", &ui_input_manager::pressed);
uiinput_type.set_function("pressed_repeat", &ui_input_manager::pressed_repeat);
uiinput_type["presses_enabled"] = sol::property(&ui_input_manager::presses_enabled, &ui_input_manager::set_presses_enabled);
diff --git a/src/frontend/mame/luaengine_mem.cpp b/src/frontend/mame/luaengine_mem.cpp
index 4e5fac3341f..ff2f406828b 100644
--- a/src/frontend/mame/luaengine_mem.cpp
+++ b/src/frontend/mame/luaengine_mem.cpp
@@ -132,7 +132,7 @@ void share_write(memory_share &share, offs_t address, T val)
int sol_lua_push(sol::types<map_handler_type>, lua_State *L, map_handler_type &&value)
{
const char *typestr;
- switch(value)
+ switch (value)
{
case AMH_NONE:
typestr = "none";
diff --git a/src/frontend/mame/luaengine_render.cpp b/src/frontend/mame/luaengine_render.cpp
index 2568f72f95d..3fd0bfdba10 100644
--- a/src/frontend/mame/luaengine_render.cpp
+++ b/src/frontend/mame/luaengine_render.cpp
@@ -18,6 +18,7 @@
#include "rendlay.h"
#include "rendutil.h"
+#include "interface/uievents.h"
#include "ioprocs.h"
#include <algorithm>
@@ -449,6 +450,33 @@ public:
} // namespace sol
+//-------------------------------------------------
+// sol_lua_push - automatically convert
+// osd::ui_event_handler::pointer to a string
+//-------------------------------------------------
+
+int sol_lua_push(sol::types<osd::ui_event_handler::pointer>, lua_State *L, osd::ui_event_handler::pointer &&value)
+{
+ const char *typestr = "invalid";
+ switch (value)
+ {
+ case osd::ui_event_handler::pointer::UNKNOWN:
+ typestr = "unknown";
+ break;
+ case osd::ui_event_handler::pointer::MOUSE:
+ typestr = "mouse";
+ break;
+ case osd::ui_event_handler::pointer::PEN:
+ typestr = "pen";
+ break;
+ case osd::ui_event_handler::pointer::TOUCH:
+ typestr = "touch";
+ break;
+ }
+ return sol::stack::push(L, typestr);
+}
+
+
template <typename T>
class lua_engine::bitmap_helper : public T
{
@@ -931,6 +959,34 @@ void lua_engine::initialize_render(sol::table &emu)
nullptr,
"set_recomputed_callback",
"recomputed"));
+ layout_view_type.set_function(
+ "set_pointer_updated_callback",
+ make_simple_callback_setter(
+ &layout_view::set_pointer_updated_callback,
+ nullptr,
+ "set_pointer_updated_callback",
+ "pointer updated"));
+ layout_view_type.set_function(
+ "set_pointer_left_callback",
+ make_simple_callback_setter(
+ &layout_view::set_pointer_left_callback,
+ nullptr,
+ "set_pointer_left_callback",
+ "pointer left"));
+ layout_view_type.set_function(
+ "set_pointer_aborted_callback",
+ make_simple_callback_setter(
+ &layout_view::set_pointer_aborted_callback,
+ nullptr,
+ "set_pointer_aborted_callback",
+ "pointer aborted"));
+ layout_view_type.set_function(
+ "set_forget_pointers_callback",
+ make_simple_callback_setter(
+ &layout_view::set_forget_pointers_callback,
+ nullptr,
+ "set_forget_pointers_callback",
+ "forget pointers"));
layout_view_type["items"] = sol::property([] (layout_view &v) { return layout_view_items(v); });
layout_view_type["name"] = sol::property(&layout_view::name);
layout_view_type["unqualified_name"] = sol::property(&layout_view::unqualified_name);
diff --git a/src/frontend/mame/ui/about.cpp b/src/frontend/mame/ui/about.cpp
index afb2b73d5d3..7b737273c3a 100644
--- a/src/frontend/mame/ui/about.cpp
+++ b/src/frontend/mame/ui/about.cpp
@@ -80,7 +80,7 @@ void menu_about::recompute_metrics(uint32_t width, uint32_t height, float aspect
// perform our special rendering
//-------------------------------------------------
-void menu_about::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_about::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
// draw the title
draw_text_box(
@@ -120,17 +120,4 @@ void menu_about::populate()
{
}
-
-//-------------------------------------------------
-// handle - manages inputs in the about modal
-//-------------------------------------------------
-
-bool menu_about::handle(event const *ev)
-{
- if (ev)
- return handle_key(ev->iptkey);
- else
- return false;
-}
-
} // namespace ui
diff --git a/src/frontend/mame/ui/about.h b/src/frontend/mame/ui/about.h
index 3e84aff7a5e..90e9d738fa3 100644
--- a/src/frontend/mame/ui/about.h
+++ b/src/frontend/mame/ui/about.h
@@ -30,13 +30,12 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
virtual void populate() override;
- virtual bool handle(event const *ev) override;
std::vector<std::string> const m_header;
};
diff --git a/src/frontend/mame/ui/analogipt.cpp b/src/frontend/mame/ui/analogipt.cpp
index f8767ad3c58..aa6ffa5a07f 100644
--- a/src/frontend/mame/ui/analogipt.cpp
+++ b/src/frontend/mame/ui/analogipt.cpp
@@ -13,6 +13,8 @@
#include "ui/textbox.h"
+#include "uiinput.h"
+
#include <algorithm>
#include <iterator>
#include <string>
@@ -74,9 +76,20 @@ menu_analog::menu_analog(mame_ui_manager &mui, render_container &container)
, m_item_data()
, m_field_data()
, m_prompt()
- , m_visible_fields(0U)
+ , m_bottom_fields(0U)
+ , m_visible_fields(0)
, m_top_field(0)
, m_hide_menu(false)
+ , m_box_left(1.0F)
+ , m_box_top(1.0F)
+ , m_box_right(0.0F)
+ , m_box_bottom(0.0F)
+ , m_pointer_action(pointer_action::NONE)
+ , m_scroll_repeat(std::chrono::steady_clock::time_point::min())
+ , m_base_pointer(0.0F, 0.0F)
+ , m_last_pointer(0.0F, 0.0F)
+ , m_scroll_base(0)
+ , m_arrow_clicked_first(false)
{
set_process_flags(PROCESS_LR_REPEAT);
set_heading(_("menu-analoginput", "Analog Input Adjustments"));
@@ -92,51 +105,50 @@ void menu_analog::recompute_metrics(uint32_t width, uint32_t height, float aspec
{
menu::recompute_metrics(width, height, aspect);
+ m_box_left = m_box_top = 1.0F;
+ m_box_right = m_box_bottom = 0.0F;
+
// space for live display
- set_custom_space(0.0f, (line_height() * m_visible_fields) + (tb_border() * 3.0f));
+ set_custom_space(0.0F, (line_height() * m_bottom_fields) + (tb_border() * 3.0F));
}
-void menu_analog::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu_analog::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
// work out how much space to use for field names
- float const aspect(machine().render().ui_aspect(&container()));
- float const extrawidth(0.4f + (((ui().box_lr_border() * 2.0f) + ui().get_line_height()) * aspect));
- float const nameavail(1.0f - (lr_border() * 2.0f) - extrawidth);
- float namewidth(0.0f);
+ float const extrawidth(0.4F + (((ui().box_lr_border() * 2.0F) + ui().get_line_height()) * x_aspect()));
+ float const nameavail(1.0F - (lr_border() * 2.0F) - extrawidth);
+ float namewidth(0.0F);
for (field_data &data : m_field_data)
namewidth = (std::min)((std::max)(get_string_width(data.field.get().name()), namewidth), nameavail);
// make a box or two
rgb_t const fgcolor(ui().colors().text_color());
- float const boxleft((1.0f - namewidth - extrawidth) / 2.0f);
- float const boxright(boxleft + namewidth + extrawidth);
- float boxtop;
- float boxbottom;
+ m_box_left = (1.0F - namewidth - extrawidth) * 0.5F;
+ m_box_right = m_box_left + namewidth + extrawidth;
float firstliney;
- int visible_fields;
if (m_hide_menu)
{
if (m_prompt.empty())
m_prompt = util::string_format(_("menu-analoginput", "Press %s to show settings"), ui().get_general_input_setting(IPT_UI_ON_SCREEN_DISPLAY));
draw_text_box(
&m_prompt, &m_prompt + 1,
- boxleft, boxright, y - top, y - top + line_height() + (tb_border() * 2.0f),
+ m_box_left, m_box_right, y - top, y - top + line_height() + (tb_border() * 2.0F),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
fgcolor, ui().colors().background_color());
- boxtop = y - top + line_height() + (tb_border() * 3.0f);
- firstliney = y - top + line_height() + (tb_border() * 4.0f);
- visible_fields = std::min<int>(m_field_data.size(), int((y2 + bottom - tb_border() - firstliney) / line_height()));
- boxbottom = firstliney + (line_height() * visible_fields) + tb_border();
+ m_box_top = y - top + line_height() + (tb_border() * 3.0F);
+ firstliney = y - top + line_height() + (tb_border() * 4.0F);
+ m_visible_fields = std::min<int>(m_field_data.size(), int((y2 + bottom - tb_border() - firstliney) / line_height()));
+ m_box_bottom = firstliney + (line_height() * m_visible_fields) + tb_border();
}
else
{
- boxtop = y2 + tb_border();
- boxbottom = y2 + bottom;
- firstliney = y2 + (tb_border() * 2.0f);
- visible_fields = m_visible_fields;
+ m_box_top = y2 + tb_border();
+ m_box_bottom = y2 + bottom;
+ firstliney = y2 + (tb_border() * 2.0F);
+ m_visible_fields = m_bottom_fields;
}
- ui().draw_outlined_box(container(), boxleft, boxtop, boxright, boxbottom, ui().colors().background_color());
+ ui().draw_outlined_box(container(), m_box_left, m_box_top, m_box_right, m_box_bottom, ui().colors().background_color());
// force the field being configured to be visible
ioport_field *const selfield(selectedref ? &reinterpret_cast<item_data *>(selectedref)->field.get() : nullptr);
@@ -152,35 +164,44 @@ void menu_analog::custom_render(void *selectedref, float top, float bottom, floa
auto const i(std::distance(m_field_data.begin(), found));
if (m_top_field > i)
m_top_field = i;
- if ((m_top_field + visible_fields) <= i)
- m_top_field = i - m_visible_fields + 1;
+ if ((m_top_field + m_visible_fields) <= i)
+ m_top_field = i - m_bottom_fields + 1;
}
}
if (0 > m_top_field)
m_top_field = 0;
- if ((m_top_field + visible_fields) > m_field_data.size())
- m_top_field = m_field_data.size() - visible_fields;
+ if ((m_top_field + m_visible_fields) > m_field_data.size())
+ m_top_field = m_field_data.size() - m_visible_fields;
// show live fields
- namewidth += line_height() * aspect;
- float const nameleft(boxleft + lr_border());
+ namewidth += line_height() * x_aspect();
+ float const nameleft(m_box_left + lr_border());
float const indleft(nameleft + namewidth);
- float const indright(indleft + 0.4f);
- for (unsigned line = 0; visible_fields > line; ++line)
+ float const indright(indleft + 0.4F);
+ for (unsigned line = 0; m_visible_fields > line; ++line)
{
// draw arrows if scrolling is possible and menu is hidden
- float const liney(firstliney + (line_height() * line));
+ float const liney(firstliney + (line_height() * float(line)));
if (m_hide_menu)
{
bool const uparrow(!line && m_top_field);
- bool const downarrow(((visible_fields - 1) == line) && ((m_field_data.size() - 1) > (line + m_top_field)));
+ bool const downarrow(((m_visible_fields - 1) == line) && ((m_field_data.size() - 1) > (line + m_top_field)));
if (uparrow || downarrow)
{
- float const arrowwidth = line_height() * aspect;
+ bool const active((uparrow && (pointer_action::SCROLL_UP == m_pointer_action)) || (downarrow && (pointer_action::SCROLL_DOWN == m_pointer_action)));
+ bool const hovered((active || pointer_idle()) && pointer_in_rect(nameleft, liney, indright, liney + line_height()));
+ float const arrowwidth(line_height() * x_aspect());
+ rgb_t const arrowcolor(!(active || hovered) ? fgcolor : (active && hovered) ? ui().colors().selected_color() : ui().colors().mouseover_color());
+ if (active || hovered)
+ {
+ highlight(
+ nameleft, liney, indright, liney + line_height(),
+ (active && hovered) ? ui().colors().selected_bg_color() : ui().colors().mouseover_bg_color());
+ }
draw_arrow(
- 0.5f * (nameleft + indright - arrowwidth), liney + (0.25f * line_height()),
- 0.5f * (nameleft + indright + arrowwidth), liney + (0.75f * line_height()),
- fgcolor, line ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
+ 0.5F * (nameleft + indright - arrowwidth), liney + (0.25F * line_height()),
+ 0.5F * (nameleft + indright + arrowwidth), liney + (0.75F * line_height()),
+ arrowcolor, line ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
continue;
}
}
@@ -200,42 +221,201 @@ void menu_analog::custom_render(void *selectedref, float top, float bottom, floa
cur = ((cur >> data.shift) - data.field.get().minval()) & (data.field.get().mask() >> data.shift);
float fill(float(cur) / data.range);
if (data.field.get().analog_reverse())
- fill = 1.0f - fill;
+ fill = 1.0F - fill;
- float const indtop(liney + (line_height() * 0.2f));
- float const indbottom(liney + (line_height() * 0.8f));
+ float const indtop(liney + (line_height() * 0.2F));
+ float const indbottom(liney + (line_height() * 0.8F));
if (data.origin > fill)
- container().add_rect(indleft + (fill * 0.4f), indtop, indleft + (data.origin * 0.4f), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_rect(indleft + (fill * 0.4F), indtop, indleft + (data.origin * 0.4F), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
else
- container().add_rect(indleft + (data.origin * 0.4f), indtop, indleft + (fill * 0.4f), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_rect(indleft + (data.origin * 0.4F), indtop, indleft + (fill * 0.4F), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indleft, indtop, indright, indtop, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indright, indtop, indright, indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indright, indbottom, indleft, indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indleft, indbottom, indleft, indtop, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
if (data.show_neutral)
- container().add_line(indleft + (data.neutral * 0.4f), indtop, indleft + (data.neutral * 0.4f), indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_line(indleft + (data.neutral * 0.4F), indtop, indleft + (data.neutral * 0.4F), indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
}
+std::tuple<int, bool, bool> menu_analog::custom_pointer_updated(bool changed, ui_event const &uievt)
+{
+ // no pointer input if we don't have up-to-date content on-screen
+ if ((m_box_left > m_box_right) || (ui_event::type::POINTER_ABORT == uievt.event_type))
+ {
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
+
+ // if nothing's happening, check for clicks
+ if (pointer_idle())
+ {
+ if ((uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
+ {
+ if (1 == uievt.pointer_clicks)
+ m_arrow_clicked_first = false;
+
+ float const firstliney(m_box_top + tb_border());
+ float const fieldleft(m_box_left + lr_border());
+ float const fieldright(m_box_right - lr_border());
+ auto const [x, y] = pointer_location();
+ bool const inwidth((x >= fieldleft) && (x < fieldright));
+ if (m_hide_menu && m_top_field && inwidth && (y >= firstliney) && (y < (firstliney + line_height())))
+ {
+ // scroll up arrow
+ if (1 == uievt.pointer_clicks)
+ m_arrow_clicked_first = true;
+
+ --m_top_field;
+ m_pointer_action = pointer_action::SCROLL_UP;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if (m_hide_menu && ((m_top_field + m_visible_fields) < m_field_data.size()) && inwidth && (y >= (firstliney + (float(m_visible_fields - 1) * line_height()))) && (y < (firstliney + (float(m_visible_fields) * line_height()))))
+ {
+ // scroll down arrow
+ if (1 == uievt.pointer_clicks)
+ m_arrow_clicked_first = true;
+
+ ++m_top_field;
+ m_pointer_action = pointer_action::SCROLL_DOWN;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if ((x >= m_box_left) && (x < m_box_right) && (y >= m_box_top) && (y < m_box_bottom))
+ {
+ if (!m_arrow_clicked_first && (2 == uievt.pointer_clicks))
+ {
+ // toggle menu display
+ // FIXME: this should really use the start point of the multi-click action
+ m_pointer_action = pointer_action::CHECK_TOGGLE_MENU;
+ m_base_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ else if (ui_event::pointer::TOUCH == uievt.pointer_type)
+ {
+ m_pointer_action = pointer_action::SCROLL_DRAG;
+ m_base_pointer = std::make_pair(x, y);
+ m_last_pointer = m_base_pointer;
+ m_scroll_base = m_top_field;
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ }
+ }
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
+
+ // handle in-progress actions
+ switch (m_pointer_action)
+ {
+ case pointer_action::NONE:
+ break;
+
+ case pointer_action::SCROLL_UP:
+ case pointer_action::SCROLL_DOWN:
+ {
+ // check for re-entry
+ bool redraw(false);
+ float const linetop(m_box_top + tb_border() + ((pointer_action::SCROLL_DOWN == m_pointer_action) ? (float(m_visible_fields - 1) * line_height()) : 0.0F));
+ float const linebottom(linetop + line_height());
+ auto const [x, y] = pointer_location();
+ bool const reentered(reentered_rect(m_last_pointer.first, m_last_pointer.second, x, y, m_box_left + lr_border(), linetop, m_box_right - lr_border(), linebottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (scroll_if_expired(now))
+ {
+ redraw = true;
+ m_scroll_repeat = now + std::chrono::milliseconds(100);
+ }
+ }
+ m_last_pointer = std::make_pair(x, y);
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+ }
+ break;
+
+ case pointer_action::SCROLL_DRAG:
+ {
+ bool const scrolled(update_scroll_drag(uievt));
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, scrolled);
+ }
+
+ case pointer_action::CHECK_TOGGLE_MENU:
+ if ((ui_event::pointer::TOUCH == uievt.pointer_type) && (0 > uievt.pointer_clicks))
+ {
+ // converted to hold/drag - treat as scroll if it's touch
+ m_pointer_action = pointer_action::SCROLL_DRAG;
+ m_last_pointer = m_base_pointer;
+ m_scroll_base = m_top_field;
+ bool const scrolled(update_scroll_drag(uievt));
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, scrolled);
+ }
+ else if (uievt.pointer_released & 0x01)
+ {
+ // primary button released - simulate the on-screen display key if it wasn't converted to a hold/drag
+ return std::make_tuple((2 == uievt.pointer_clicks) ? IPT_UI_ON_SCREEN_DISPLAY : IPT_INVALID, false, false);
+ }
+ else if ((2 != uievt.pointer_clicks) || (uievt.pointer_buttons & ~u32(0x01)))
+ {
+ // treat converting to a hold/drag or pressing another button as cancelling the action
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+
void menu_analog::menu_activated()
{
// scripts could have changed something in the mean time
m_item_data.clear();
m_field_data.clear();
reset(reset_options::REMEMBER_POSITION);
+
+ m_box_left = m_box_top = 1.0F;
+ m_box_right = m_box_bottom = 0.0F;
+ m_pointer_action = pointer_action::NONE;
+ m_arrow_clicked_first = false;
}
bool menu_analog::handle(event const *ev)
{
+ // deal with repeating scroll arrows
+ bool scrolled(false);
+ if ((pointer_action::SCROLL_UP == m_pointer_action) || (pointer_action::SCROLL_DOWN == m_pointer_action))
+ {
+ float const linetop(m_box_top + tb_border() + ((pointer_action::SCROLL_DOWN == m_pointer_action) ? (float(m_visible_fields - 1) * line_height()) : 0.0F));
+ float const linebottom(linetop + line_height());
+ if (pointer_in_rect(m_box_left + lr_border(), linetop, m_box_right - lr_border(), linebottom))
+ {
+ while (scroll_if_expired(std::chrono::steady_clock::now()))
+ {
+ scrolled = true;
+ m_scroll_repeat += std::chrono::milliseconds(100);
+ }
+ }
+ }
+
if (!ev)
{
- return false;
+ return scrolled;
}
else if (IPT_UI_ON_SCREEN_DISPLAY == ev->iptkey)
{
m_hide_menu = !m_hide_menu;
+
+ m_box_left = m_box_top = 1.0F;
+ m_box_right = m_box_bottom = 0.0F;
+ m_pointer_action = pointer_action::NONE;
+ m_arrow_clicked_first = false;
+
set_process_flags(PROCESS_LR_REPEAT | (m_hide_menu ? (PROCESS_CUSTOM_NAV | PROCESS_CUSTOM_ONLY) : 0));
return true;
}
@@ -254,26 +434,53 @@ bool menu_analog::handle(event const *ev)
ui().get_general_input_setting(IPT_UI_PREV_GROUP),
ui().get_general_input_setting(IPT_UI_NEXT_GROUP),
ui().get_general_input_setting(IPT_UI_BACK)));
- return false;
}
else if (m_hide_menu)
{
switch (ev->iptkey)
{
case IPT_UI_UP:
- --m_top_field;
- return true;
+ if (m_top_field)
+ {
+ --m_top_field;
+ return true;
+ }
+ break;
case IPT_UI_DOWN:
- ++m_top_field;
- return true;
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ {
+ ++m_top_field;
+ return true;
+ }
+ break;
+ case IPT_UI_PAGE_UP:
+ if (m_visible_fields)
+ {
+ m_top_field -= std::min<int>(m_visible_fields - 3, m_top_field);
+ return true;
+ }
+ break;
+ case IPT_UI_PAGE_DOWN:
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ {
+ m_top_field = std::min<int>(m_top_field + m_visible_fields - 3, m_field_data.size() - m_visible_fields);
+ return true;
+ }
+ break;
case IPT_UI_HOME:
- m_top_field = 0;
- return true;
+ if (m_top_field)
+ {
+ m_top_field = 0;
+ return true;
+ }
+ break;
case IPT_UI_END:
- m_top_field = m_field_data.size();
- return true;
- default:
- return false;
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ {
+ m_top_field = m_field_data.size() - m_visible_fields;
+ return true;
+ }
+ break;
}
}
else if (ev->itemref)
@@ -384,15 +591,9 @@ bool menu_analog::handle(event const *ev)
ev->item->set_flags((data.cur <= data.min) ? FLAG_RIGHT_ARROW : (data.cur >= data.max) ? FLAG_LEFT_ARROW : FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW);
return true;
}
- else
- {
- return false;
- }
- }
- else
- {
- return false;
}
+
+ return scrolled;
}
@@ -466,7 +667,7 @@ void menu_analog::populate()
item_append(menu_item_type::SEPARATOR);
// space for live display
- set_custom_space(0.0f, (line_height() * m_visible_fields) + (tb_border() * 3.0f));
+ set_custom_space(0.0F, (line_height() * m_bottom_fields) + (tb_border() * 3.0F));
}
@@ -518,10 +719,72 @@ void menu_analog::find_fields()
}
// restrict live display to 40% height plus borders
- if ((line_height() * m_field_data.size()) > 0.4f)
- m_visible_fields = unsigned(0.4f / line_height());
+ if ((line_height() * m_field_data.size()) > 0.4F)
+ m_bottom_fields = unsigned(0.4F / line_height());
+ else
+ m_bottom_fields = m_field_data.size();
+}
+
+
+bool menu_analog::scroll_if_expired(std::chrono::steady_clock::time_point now)
+{
+ if (now < m_scroll_repeat)
+ return false;
+
+ if (pointer_action::SCROLL_DOWN == m_pointer_action)
+ {
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ ++m_top_field;
+ if ((m_top_field + m_visible_fields) == m_field_data.size())
+ m_pointer_action = pointer_action::NONE;
+ }
else
- m_visible_fields = m_field_data.size();
+ {
+ if (0 < m_top_field)
+ --m_top_field;
+ if (!m_top_field)
+ m_pointer_action = pointer_action::NONE;
+ }
+ return true;
+}
+
+
+bool menu_analog::update_scroll_drag(ui_event const &uievt)
+{
+ // set thresholds depending on the direction for hysteresis
+ float const y(pointer_location().second);
+ float const base(m_base_pointer.second + (line_height() * ((y > m_last_pointer.second) ? -0.3F : 0.3F)));
+ auto const target(int((base - y) / line_height()));
+ m_last_pointer.second = base + (float(target) * line_height());
+
+ // scroll if it moved
+ int newtop(std::clamp<int>(m_scroll_base + target, 0, m_field_data.size() - m_visible_fields));
+ if (!m_hide_menu && (newtop != m_top_field))
+ {
+ // if the menu is visible, keep the highlighted field on-screen
+ void *const selectedref(get_selection_ref());
+ ioport_field *const selfield(selectedref ? &reinterpret_cast<item_data *>(selectedref)->field.get() : nullptr);
+ if (selfield)
+ {
+ auto const found(
+ std::find_if(
+ m_field_data.begin(),
+ m_field_data.end(),
+ [selfield] (field_data const &d) { return &d.field.get() == selfield; }));
+ if (m_field_data.end() != found)
+ {
+ auto const i(std::distance(m_field_data.begin(), found));
+ newtop = std::clamp<int>(newtop, i + 1 - m_visible_fields, i);
+ }
+ }
+ }
+ bool const scrolled(newtop != m_top_field);
+ m_top_field = newtop;
+
+ // catch the end of the gesture
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return scrolled;
}
diff --git a/src/frontend/mame/ui/analogipt.h b/src/frontend/mame/ui/analogipt.h
index d60aafd6cc4..ee59954911c 100644
--- a/src/frontend/mame/ui/analogipt.h
+++ b/src/frontend/mame/ui/analogipt.h
@@ -14,7 +14,10 @@
#include "ui/menu.h"
+#include <chrono>
#include <functional>
+#include <tuple>
+#include <utility>
#include <vector>
@@ -28,10 +31,20 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
virtual void menu_activated() override;
private:
+ enum class pointer_action
+ {
+ NONE,
+ SCROLL_UP,
+ SCROLL_DOWN,
+ SCROLL_DRAG,
+ CHECK_TOGGLE_MENU
+ };
+
enum
{
ANALOG_ITEM_KEYSPEED = 0,
@@ -71,15 +84,30 @@ private:
virtual bool handle(event const *ev) override;
void find_fields();
+ bool scroll_if_expired(std::chrono::steady_clock::time_point now);
+ bool update_scroll_drag(ui_event const &uievt);
static std::string item_text(int type, int value);
item_data_vector m_item_data;
field_data_vector m_field_data;
std::string m_prompt;
- unsigned m_visible_fields;
+ unsigned m_bottom_fields;
+ int m_visible_fields;
int m_top_field;
bool m_hide_menu;
+
+ float m_box_left;
+ float m_box_top;
+ float m_box_right;
+ float m_box_bottom;
+
+ pointer_action m_pointer_action;
+ std::chrono::steady_clock::time_point m_scroll_repeat;
+ std::pair<float, float> m_base_pointer;
+ std::pair<float, float> m_last_pointer;
+ int m_scroll_base;
+ bool m_arrow_clicked_first;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/auditmenu.cpp b/src/frontend/mame/ui/auditmenu.cpp
index d225535c4e7..180c3005b73 100644
--- a/src/frontend/mame/ui/auditmenu.cpp
+++ b/src/frontend/mame/ui/auditmenu.cpp
@@ -83,7 +83,7 @@ void menu_audit::recompute_metrics(uint32_t width, uint32_t height, float aspect
}
-void menu_audit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu_audit::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
switch (m_phase)
{
diff --git a/src/frontend/mame/ui/auditmenu.h b/src/frontend/mame/ui/auditmenu.h
index 5d71b63fd2f..3a1c236549d 100644
--- a/src/frontend/mame/ui/auditmenu.h
+++ b/src/frontend/mame/ui/auditmenu.h
@@ -30,7 +30,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual bool custom_ui_back() override;
private:
diff --git a/src/frontend/mame/ui/confswitch.cpp b/src/frontend/mame/ui/confswitch.cpp
index 946062b9a1d..f2f8a241d98 100644
--- a/src/frontend/mame/ui/confswitch.cpp
+++ b/src/frontend/mame/ui/confswitch.cpp
@@ -11,6 +11,8 @@
#include "emu.h"
#include "ui/confswitch.h"
+#include "uiinput.h"
+
#include <algorithm>
#include <cstring>
@@ -176,6 +178,13 @@ bool menu_confswitch::handle(event const *ev)
if (!ev || !ev->itemref)
return false;
+ if (IPT_CUSTOM == ev->iptkey)
+ {
+ // clicked a switch
+ reset(reset_options::REMEMBER_REF);
+ return true;
+ }
+
if (uintptr_t(ev->itemref) == 1U)
{
// reset
@@ -339,16 +348,15 @@ void menu_settings_dip_switches::recompute_metrics(uint32_t width, uint32_t heig
}
-void menu_settings_dip_switches::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+void menu_settings_dip_switches::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
// catch if no DIP locations have to be drawn
if (!m_visible_switch_groups)
return;
// calculate optimal width
- float const aspect(machine().render().ui_aspect(&container()));
float const maxwidth(1.0f - (lr_border() * 2.0f));
- m_single_width = (line_height() * SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * aspect);
+ m_single_width = (line_height() * SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * x_aspect());
float width(0.0f);
unsigned maxswitches(0U);
for (switch_group_descriptor const &group : switch_groups())
@@ -358,7 +366,7 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
maxswitches = (std::max)(group.switch_count(), maxswitches);
float const namewidth(get_string_width(group.name));
float const switchwidth(m_single_width * maxswitches);
- width = (std::min)((std::max)(namewidth + switchwidth + (line_height() * aspect), width), maxwidth);
+ width = (std::min)((std::max)(namewidth + switchwidth + (line_height() * x_aspect()), width), maxwidth);
}
}
@@ -370,12 +378,12 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
// calculate centred layout
float const nameleft((1.0f - width) * 0.5f);
float const switchleft(nameleft + width - (m_single_width * maxswitches));
- float const namewidth(width - (m_single_width * maxswitches) - (line_height() * aspect));
+ float const namewidth(width - (m_single_width * maxswitches) - (line_height() * x_aspect()));
// iterate over switch groups
ioport_field *const field((uintptr_t(selectedref) != 1U) ? reinterpret_cast<ioport_field *>(selectedref) : nullptr);
float const nubheight(line_height() * SINGLE_TOGGLE_SWITCH_HEIGHT);
- m_nub_width = line_height() * SINGLE_TOGGLE_SWITCH_WIDTH * aspect;
+ m_nub_width = line_height() * SINGLE_TOGGLE_SWITCH_WIDTH * x_aspect();
float const ygap(line_height() * ((DIP_SWITCH_HEIGHT * 0.5f) - SINGLE_TOGGLE_SWITCH_HEIGHT) * 0.5f);
float const xgap((m_single_width + (UI_LINE_WIDTH * 0.5f) - m_nub_width) * 0.5f);
m_first_nub = switchleft + xgap;
@@ -449,13 +457,16 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
}
-bool menu_settings_dip_switches::custom_mouse_down()
+std::tuple<int, bool, bool> menu_settings_dip_switches::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- if (!m_visible_switch_groups || (get_mouse_x() < m_first_nub))
- return false;
+ if (!m_visible_switch_groups || !(uievt.pointer_pressed & 0x01) || (uievt.pointer_buttons & ~u32(0x01)))
+ return std::make_tuple(IPT_INVALID, false, false);
+
+ auto const [cx, y] = pointer_location();
+ if (cx < m_first_nub)
+ return std::make_tuple(IPT_INVALID, false, false);
- float const x(get_mouse_x() - m_first_nub);
- float const y(get_mouse_y());
+ float const x(cx - m_first_nub);
for (unsigned n = 0U, line = 0U; (switch_groups().size() > n) && (m_visible_switch_groups > line); ++n)
{
switch_group_descriptor const &group(switch_groups()[n]);
@@ -476,8 +487,7 @@ bool menu_settings_dip_switches::custom_mouse_down()
group.toggles[i].field->get_user_settings(settings);
settings.value ^= group.toggles[i].mask;
group.toggles[i].field->set_user_settings(settings);
- reset(reset_options::REMEMBER_REF);
- return true;
+ return std::make_tuple(IPT_CUSTOM, true, true);
}
}
}
@@ -485,7 +495,7 @@ bool menu_settings_dip_switches::custom_mouse_down()
}
}
- return false;
+ return std::make_tuple(IPT_INVALID, false, false);
}
diff --git a/src/frontend/mame/ui/confswitch.h b/src/frontend/mame/ui/confswitch.h
index 2d025f7dfc8..7f00783b0f1 100644
--- a/src/frontend/mame/ui/confswitch.h
+++ b/src/frontend/mame/ui/confswitch.h
@@ -83,8 +83,8 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool custom_mouse_down() override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
private:
virtual void populate() override;
diff --git a/src/frontend/mame/ui/custui.cpp b/src/frontend/mame/ui/custui.cpp
index 1db4a64ba13..9cfd7284d9b 100644
--- a/src/frontend/mame/ui/custui.cpp
+++ b/src/frontend/mame/ui/custui.cpp
@@ -548,7 +548,7 @@ void menu_font_ui::recompute_metrics(uint32_t width, uint32_t height, float aspe
// perform our special rendering
//-------------------------------------------------
-void menu_font_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_font_ui::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
if (uintptr_t(selectedref) == INFOS_SIZE)
{
@@ -670,7 +670,7 @@ void menu_colors_ui::recompute_metrics(uint32_t width, uint32_t height, float as
// perform our special rendering
//-------------------------------------------------
-void menu_colors_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_colors_ui::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
// get the text for 'UI Select'
std::string const bottomtext[] = { util::string_format(_("Double-click or press %1$s to change color"), ui().get_general_input_setting(IPT_UI_SELECT)) };
@@ -963,7 +963,7 @@ void menu_rgb_ui::recompute_metrics(uint32_t width, uint32_t height, float aspec
// perform our special rendering
//-------------------------------------------------
-void menu_rgb_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_rgb_ui::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
float maxwidth = origx2 - origx1;
diff --git a/src/frontend/mame/ui/custui.h b/src/frontend/mame/ui/custui.h
index 981546c64e3..be9df136f7e 100644
--- a/src/frontend/mame/ui/custui.h
+++ b/src/frontend/mame/ui/custui.h
@@ -60,7 +60,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual void menu_dismissed() override;
private:
@@ -96,7 +96,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual void menu_dismissed() override;
private:
@@ -145,7 +145,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
private:
enum
diff --git a/src/frontend/mame/ui/datmenu.cpp b/src/frontend/mame/ui/datmenu.cpp
index a4f919a7f62..492bdd38e41 100644
--- a/src/frontend/mame/ui/datmenu.cpp
+++ b/src/frontend/mame/ui/datmenu.cpp
@@ -13,7 +13,6 @@
#include "ui/systemlist.h"
#include "ui/ui.h"
-#include "ui/utils.h"
#include "luaengine.h"
#include "mame.h"
@@ -31,7 +30,8 @@
namespace ui {
//-------------------------------------------------
-// ctor / dtor
+// construct for currently running or specified
+// system
//-------------------------------------------------
menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container, const ui_system_info *system)
@@ -39,8 +39,9 @@ menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container
, m_system(!system ? &system_list::instance().systems()[driver_list::find(mui.machine().system().name)] : system)
, m_swinfo(nullptr)
, m_issoft(false)
- , m_actual(0)
-
+ , m_current_tab(0)
+ , m_tab_line(1.0F, 0.0F)
+ , m_clicked_tab(-1)
{
set_process_flags(PROCESS_LR_ALWAYS | PROCESS_CUSTOM_NAV);
for (device_image_interface& image : image_interface_enumerator(mui.machine().root_device()))
@@ -54,15 +55,17 @@ menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container
break;
}
}
+
std::vector<std::string> lua_list;
if (mame_machine_manager::instance()->lua()->call_plugin("data_list", system ? system->driver->name : "", lua_list))
{
int count = 0;
+ m_items_list.reserve(lua_list.size());
for (std::string& item : lua_list)
{
std::string version;
mame_machine_manager::instance()->lua()->call_plugin("data_version", count, version);
- m_items_list.emplace_back(item.c_str(), count, std::move(version));
+ m_items_list.emplace_back(std::move(item), count, std::move(version));
count++;
}
}
@@ -77,25 +80,33 @@ menu_dats_view::menu_dats_view(mame_ui_manager &mui, render_container &container
, m_system(nullptr)
, m_swinfo(&swinfo)
, m_issoft(true)
- , m_actual(0)
+ , m_current_tab(0)
, m_list(swinfo.listname)
, m_short(swinfo.shortname)
, m_long(swinfo.longname)
, m_parent(swinfo.parentname)
-
+ , m_tab_line(1.0F, 0.0F)
+ , m_clicked_tab(-1)
{
set_process_flags(PROCESS_LR_ALWAYS | PROCESS_CUSTOM_NAV);
- if (!swinfo.infotext.empty())
- m_items_list.emplace_back(_("Software List Info"), 0, "");
+
std::vector<std::string> lua_list;
- if (mame_machine_manager::instance()->lua()->call_plugin("data_list", std::string(m_short).append(1, ',').append(m_list).c_str(), lua_list))
+ bool const retrieved(mame_machine_manager::instance()->lua()->call_plugin("data_list", std::string(m_short).append(1, ',').append(m_list).c_str(), lua_list));
+
+ if (!swinfo.infotext.empty() || retrieved)
+ m_items_list.reserve((!swinfo.infotext.empty() ? 1 : 0) + (retrieved ? lua_list.size() : 0));
+
+ if (!swinfo.infotext.empty())
+ m_items_list.emplace_back(_("Software List Info"), -1, "");
+
+ if (retrieved)
{
- int count = 1;
+ int count = 0;
for (std::string &item : lua_list)
{
std::string version;
- mame_machine_manager::instance()->lua()->call_plugin("data_version", count - 1, version);
- m_items_list.emplace_back(item.c_str(), count, std::move(version));
+ mame_machine_manager::instance()->lua()->call_plugin("data_version", count, version);
+ m_items_list.emplace_back(std::move(item), count, std::move(version));
count++;
}
}
@@ -169,34 +180,36 @@ void menu_dats_view::add_info_text(text_layout &layout, std::string_view text, r
bool menu_dats_view::handle(event const *ev)
{
- if (!ev)
- return false;
-
- switch (ev->iptkey)
+ if (ev)
{
- case IPT_UI_LEFT:
- if (m_actual > 0)
+ // don't bother with parent event handling if we need to redraw anyway
+ switch (ev->iptkey)
{
- m_actual--;
- reset_layout();
- return true;
- }
- break;
+ case IPT_UI_LEFT:
+ if (m_current_tab > 0)
+ {
+ m_current_tab--;
+ m_tab_line = std::make_pair(1.0F, 0.0F);
+ m_clicked_tab = -1;
+ reset_layout();
+ return true;
+ }
+ break;
- case IPT_UI_RIGHT:
- if ((m_actual + 1) < m_items_list.size())
- {
- m_actual++;
- reset_layout();
- return true;
+ case IPT_UI_RIGHT:
+ if ((m_current_tab + 1) < m_items_list.size())
+ {
+ m_current_tab++;
+ m_tab_line = std::make_pair(1.0F, 0.0F);
+ m_clicked_tab = -1;
+ reset_layout();
+ return true;
+ }
+ break;
}
- break;
-
- default:
- return handle_key(ev->iptkey);
}
- return false;
+ return menu_textbox::handle(ev);
}
//-------------------------------------------------
@@ -215,6 +228,9 @@ void menu_dats_view::recompute_metrics(uint32_t width, uint32_t height, float as
{
menu_textbox::recompute_metrics(width, height, aspect);
+ m_tab_line = std::make_pair(1.0F, 0.0F);
+ m_clicked_tab = -1;
+
set_custom_space(2.0F * line_height() + 4.0F * tb_border(), line_height() + 3.0F * tb_border());
}
@@ -222,103 +238,112 @@ void menu_dats_view::recompute_metrics(uint32_t width, uint32_t height, float as
// perform our special rendering
//-------------------------------------------------
-void menu_dats_view::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_dats_view::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
- float maxwidth = origx2 - origx1;
- float width;
+ float maxwidth;
std::string_view const driver = m_issoft ? m_swinfo->longname : m_system->description;
- width = get_string_width(driver);
- width += 2 * lr_border();
- maxwidth = std::max(maxwidth, width);
+ maxwidth = std::max(origx2 - origx1, get_string_width(driver) + (2.0F * lr_border()));
// compute our bounds
- float x1 = 0.5F - 0.5F * maxwidth;
+ float x1 = 0.5F - (0.5F * maxwidth);
float x2 = x1 + maxwidth;
float y1 = origy1 - top;
- float y2 = origy1 - 2.0F * tb_border() - line_height();
+ float y2 = y1 + (2.0F * tb_border()) + line_height();
// draw a box
ui().draw_outlined_box(container(), x1, y1, x2, y2, UI_GREEN_COLOR);
- // take off the borders
- x1 += lr_border();
- x2 -= lr_border();
- y1 += tb_border();
-
draw_text_normal(
driver,
- x1, y1, x2 - x1,
+ x1 + lr_border(), y1 + tb_border(), x2 - x1 - (2.0F * lr_border()),
text_layout::text_justify::CENTER, ui::text_layout::word_wrapping::NEVER,
ui().colors().text_color());
- maxwidth = 0;
- for (auto const &elem : m_items_list)
- {
- width = get_string_width(elem.label);
- maxwidth += width;
- }
-
- float space = (1.0F - maxwidth) / (m_items_list.size() * 2);
+ // draw a box
+ ui().draw_outlined_box(container(), x1, origy1 - line_height() - tb_border(), x2, origy1, ui().colors().background_color());
- // compute our bounds
- x1 -= lr_border();
- x2 += lr_border();
- y1 = y2 + tb_border();
- y2 += line_height() + 2.0F * tb_border();
+ // calculate geometry of tab line
+ if (m_tab_line.first >= m_tab_line.second)
+ {
+ m_tab_line = std::make_pair(origy1 - line_height(), origy1);
- // draw a box
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ // FIXME: deal with overflow when there are a lot of tabs
+ float total(0.0F);
+ for (auto const &elem : m_items_list)
+ total += get_string_width(elem.label);
+ float const space((1.0F - total) / (m_items_list.size() * 2.0F));
- // take off the borders
- y1 += tb_border();
+ float left(x1 + (space * 0.5F));
+ for (auto &elem : m_items_list)
+ {
+ float const width(get_string_width(elem.label));
+ elem.bounds = std::make_pair(left, left + width + space);
+ left += width + (space * 2.0F);
+ }
+ }
// draw the text within it
- int x = 0;
- for (auto const &elem : m_items_list)
+ for (int i = 0; m_items_list.size() > i; ++i)
{
- x1 += space;
- if (mouse_in_rect(x1 - (space / 2), y1, x1 + width + (space / 2), y2))
- set_hover(HOVER_INFO_TEXT + 1 + x);
+ auto &elem(m_items_list[i]);
- rgb_t const fcolor = (m_actual == x) ? rgb_t(0xff, 0xff, 0xff, 0x00) : ui().colors().text_color();
- rgb_t const bcolor = (m_actual == x) ? rgb_t(0xff, 0xff, 0xff, 0xff) : ui().colors().text_bg_color();
- width = get_string_width(elem.label);
-
- if (bcolor != ui().colors().text_bg_color())
+ rgb_t fgcolor;
+ rgb_t bgcolor;
+ if (i == m_current_tab)
{
+ fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
+ bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
ui().draw_textured_box(
container(),
- x1 - (space / 2), y1, x1 + width + (space / 2), y2,
- bcolor, rgb_t(255, 43, 43, 43),
+ elem.bounds.first, m_tab_line.first, elem.bounds.second, m_tab_line.second,
+ bgcolor, rgb_t(255, 43, 43, 43),
hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
+ else
+ {
+ bool const hovered(pointer_in_rect(elem.bounds.first, m_tab_line.first, elem.bounds.second, m_tab_line.second));
+ if ((i == m_clicked_tab) && hovered)
+ {
+ fgcolor = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ highlight(elem.bounds.first, m_tab_line.first, elem.bounds.second, m_tab_line.second, bgcolor);
+ }
+ else if ((i == m_clicked_tab) || (hovered && pointer_idle()))
+ {
+ fgcolor = ui().colors().mouseover_color();
+ bgcolor = ui().colors().mouseover_bg_color();
+ highlight(elem.bounds.first, m_tab_line.first, elem.bounds.second, m_tab_line.second, bgcolor);
+ }
+ else
+ {
+ fgcolor = ui().colors().text_color();
+ bgcolor = ui().colors().text_bg_color();
+ }
+ }
ui().draw_text_full(
container(),
elem.label,
- x1, y1, 1.0F,
- text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, fcolor, bcolor,
- &width, nullptr,
+ elem.bounds.first, m_tab_line.first, elem.bounds.second - elem.bounds.first,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
+ mame_ui_manager::NORMAL, fgcolor, bgcolor,
+ nullptr, nullptr,
line_height());
- x1 += width + space;
- ++x;
}
// bottom
if (!m_items_list.empty())
{
- std::string const revision(util::string_format(_("Revision: %1$s"), m_items_list[m_actual].revision));
- width = get_text_width(
+ std::string const revision(util::string_format(_("Revision: %1$s"), m_items_list[m_current_tab].revision));
+ float const width(get_text_width(
revision,
0.0F, 0.0F, 1.0F,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE);
- width += 2 * lr_border();
- maxwidth = std::max(origx2 - origx1, width);
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE));
+ maxwidth = std::max(origx2 - origx1, width + (2.0F * lr_border()));
// compute our bounds
- x1 = 0.5F - 0.5F * maxwidth;
+ x1 = 0.5F - (0.5F * maxwidth);
x2 = x1 + maxwidth;
y1 = origy2 + tb_border();
y2 = origy2 + bottom;
@@ -326,39 +351,65 @@ void menu_dats_view::custom_render(void *selectedref, float top, float bottom, f
// draw a box
ui().draw_outlined_box(container(), x1, y1, x2, y2, UI_GREEN_COLOR);
- // take off the borders
- x1 += lr_border();
- x2 -= lr_border();
- y1 += tb_border();
-
// draw the text within it
draw_text_normal(
revision,
- x1, y1, x2 - x1,
+ x1 + lr_border(), y1 + tb_border(), x2 - x1 - (2.0F * lr_border()),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
ui().colors().text_color());
}
}
//-------------------------------------------------
-// custom mouse click handling
+// custom pointer handling
//-------------------------------------------------
-bool menu_dats_view::custom_mouse_down()
+std::tuple<int, bool, bool> menu_dats_view::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- if ((hover() > HOVER_INFO_TEXT) && ((hover() - HOVER_INFO_TEXT) <= m_items_list.size()))
+ if (0 <= m_clicked_tab)
{
- if ((hover() - HOVER_INFO_TEXT - 1) != m_actual)
+ if ((ui_event::type::POINTER_ABORT != uievt.event_type) && uievt.pointer_released & 0x01)
+ {
+ // primary button released - take action if still on the tab
+ bool const hit(pointer_in_rect(
+ m_items_list[m_clicked_tab].bounds.first, m_tab_line.first,
+ m_items_list[m_clicked_tab].bounds.second, m_tab_line.second));
+ if (hit && (m_current_tab != m_clicked_tab))
+ {
+ m_current_tab = m_clicked_tab;
+ m_tab_line = std::make_pair(1.0F, 0.0F);
+ reset_layout();
+ }
+ m_clicked_tab = -1;
+ return std::make_tuple(hit ? IPT_CUSTOM : IPT_INVALID, false, true);
+ }
+ else if ((ui_event::type::POINTER_ABORT == uievt.event_type) || (uievt.pointer_buttons & ~u32(0x01)))
{
- m_actual = hover() - HOVER_INFO_TEXT - 1;
- reset_layout();
+ // treat pressing another button as cancellation
+ m_clicked_tab = -1;
+ return std::make_tuple(IPT_INVALID, false, true);
}
- return true;
+ return std::make_tuple(IPT_INVALID, true, false);
}
- else
+ else if (pointer_idle() && (uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
{
- return false;
+ // primary click - see if it's over a tab
+ auto const [x, y] = pointer_location();
+ if ((y >= m_tab_line.first) && (y < m_tab_line.second))
+ {
+ for (int i = 0; m_items_list.size() > i; ++i)
+ {
+ if ((x >= m_items_list[i].bounds.first) && (x < m_items_list[i].bounds.second))
+ {
+ m_clicked_tab = i;
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+ }
}
+
+ // let the base class have a look
+ return menu_textbox::custom_pointer_updated(changed, uievt);
}
//-------------------------------------------------
@@ -369,13 +420,16 @@ void menu_dats_view::populate_text(std::optional<text_layout> &layout, float &wi
{
if (!layout || (layout->width() != width))
{
+ m_tab_line = std::make_pair(1.0F, 0.0F);
+ m_clicked_tab = -1;
+
std::string buffer;
if (!m_items_list.empty())
{
- if (m_issoft)
- get_data_sw(buffer);
+ if (0 > m_items_list[m_current_tab].option)
+ buffer = m_swinfo->infotext;
else
- get_data(buffer);
+ mame_machine_manager::instance()->lua()->call_plugin("data", m_items_list[m_current_tab].option, buffer);
}
layout.emplace(create_layout(width));
add_info_text(*layout, buffer, ui().colors().text_color());
@@ -383,21 +437,4 @@ void menu_dats_view::populate_text(std::optional<text_layout> &layout, float &wi
}
}
-//-------------------------------------------------
-// load data from DATs
-//-------------------------------------------------
-
-void menu_dats_view::get_data(std::string &buffer)
-{
- mame_machine_manager::instance()->lua()->call_plugin("data", m_items_list[m_actual].option, buffer);
-}
-
-void menu_dats_view::get_data_sw(std::string &buffer)
-{
- if (m_items_list[m_actual].option == 0)
- buffer = m_swinfo->infotext;
- else
- mame_machine_manager::instance()->lua()->call_plugin("data", m_items_list[m_actual].option - 1, buffer);
-}
-
} // namespace ui
diff --git a/src/frontend/mame/ui/datmenu.h b/src/frontend/mame/ui/datmenu.h
index 5c255c5fbff..e8e7cea2887 100644
--- a/src/frontend/mame/ui/datmenu.h
+++ b/src/frontend/mame/ui/datmenu.h
@@ -44,33 +44,35 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool custom_mouse_down() override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
struct list_items
{
- list_items(std::string &&l, int i, std::string &&rev) : label(std::move(l)), option(i), revision(std::move(rev)) { }
+ list_items(std::string &&l, int i, std::string &&rev) : label(std::move(l)), revision(std::move(rev)), option(i), bounds(1.0F, 0.0F) { }
std::string label;
- int option;
std::string revision;
+ int option;
+
+ std::pair<float, float> bounds;
};
virtual void populate() override;
virtual bool handle(event const *ev) override;
- void get_data(std::string &buffer);
- void get_data_sw(std::string &buffer);
-
ui_system_info const *const m_system;
ui_software_info const *const m_swinfo;
bool const m_issoft;
- int m_actual;
+ int m_current_tab;
std::string m_list, m_short, m_long, m_parent;
std::vector<list_items> m_items_list;
+
+ std::pair<float, float> m_tab_line;
+ int m_clicked_tab;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/devopt.cpp b/src/frontend/mame/ui/devopt.cpp
index 2dd7a758a0f..8d841c7d131 100644
--- a/src/frontend/mame/ui/devopt.cpp
+++ b/src/frontend/mame/ui/devopt.cpp
@@ -368,12 +368,4 @@ void menu_device_config::populate()
{
}
-bool menu_device_config::handle(event const *ev)
-{
- if (ev)
- return handle_key(ev->iptkey);
- else
- return false;
-}
-
} // namespace ui
diff --git a/src/frontend/mame/ui/devopt.h b/src/frontend/mame/ui/devopt.h
index 67500c81a41..2e67cd4524f 100644
--- a/src/frontend/mame/ui/devopt.h
+++ b/src/frontend/mame/ui/devopt.h
@@ -29,7 +29,6 @@ protected:
private:
virtual void populate() override;
- virtual bool handle(event const *ev) override;
device_slot_interface::slot_option const *const m_option;
bool const m_mounted;
diff --git a/src/frontend/mame/ui/dirmenu.cpp b/src/frontend/mame/ui/dirmenu.cpp
index bd4ddb0cfdd..083dfbeba79 100644
--- a/src/frontend/mame/ui/dirmenu.cpp
+++ b/src/frontend/mame/ui/dirmenu.cpp
@@ -165,7 +165,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual bool custom_ui_back() override { return !m_search.empty(); }
@@ -356,7 +356,7 @@ void menu_add_change_folder::recompute_metrics(uint32_t width, uint32_t height,
// perform our special rendering
//-------------------------------------------------
-void menu_add_change_folder::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_add_change_folder::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
std::string const toptext[] = {
util::string_format(
@@ -447,7 +447,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
private:
enum
@@ -557,7 +557,7 @@ void menu_display_actual::recompute_metrics(uint32_t width, uint32_t height, flo
// perform our special rendering
//-------------------------------------------------
-void menu_display_actual::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_display_actual::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
float const maxwidth(draw_text_box(
std::begin(m_folders), std::end(m_folders),
diff --git a/src/frontend/mame/ui/filecreate.cpp b/src/frontend/mame/ui/filecreate.cpp
index 75e78702567..828e59cbb74 100644
--- a/src/frontend/mame/ui/filecreate.cpp
+++ b/src/frontend/mame/ui/filecreate.cpp
@@ -153,7 +153,7 @@ void menu_file_create::recompute_metrics(uint32_t width, uint32_t height, float
// custom_render - perform our special rendering
//-------------------------------------------------
-void menu_file_create::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_file_create::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
extra_text_render(top, bottom, origx1, origy1, origx2, origy2,
m_current_directory,
diff --git a/src/frontend/mame/ui/filecreate.h b/src/frontend/mame/ui/filecreate.h
index 9537003a6f7..a5f1ab92857 100644
--- a/src/frontend/mame/ui/filecreate.h
+++ b/src/frontend/mame/ui/filecreate.h
@@ -48,7 +48,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual bool custom_ui_back() override;
private:
diff --git a/src/frontend/mame/ui/filemngr.cpp b/src/frontend/mame/ui/filemngr.cpp
index 92b68ccee1d..f9a99474a15 100644
--- a/src/frontend/mame/ui/filemngr.cpp
+++ b/src/frontend/mame/ui/filemngr.cpp
@@ -74,7 +74,7 @@ void menu_file_manager::recompute_metrics(uint32_t width, uint32_t height, float
// custom_render - perform our special rendering
//-------------------------------------------------
-void menu_file_manager::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_file_manager::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
// access the path
std::string_view path = m_selected_device && m_selected_device->exists() ? m_selected_device->filename() : std::string_view();
diff --git a/src/frontend/mame/ui/filemngr.h b/src/frontend/mame/ui/filemngr.h
index bae4546201e..a8c68ceb211 100644
--- a/src/frontend/mame/ui/filemngr.h
+++ b/src/frontend/mame/ui/filemngr.h
@@ -32,7 +32,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
private:
virtual void populate() override;
diff --git a/src/frontend/mame/ui/filesel.cpp b/src/frontend/mame/ui/filesel.cpp
index 14d62c206b3..5f4dddc65fc 100644
--- a/src/frontend/mame/ui/filesel.cpp
+++ b/src/frontend/mame/ui/filesel.cpp
@@ -19,6 +19,8 @@
#include "imagedev/floppy.h"
+#include "uiinput.h"
+
#include "util/corestr.h"
#include "util/zippath.h"
@@ -48,15 +50,26 @@ namespace ui {
// ctor
//-------------------------------------------------
-menu_file_selector::menu_file_selector(mame_ui_manager &mui, render_container &container, device_image_interface *image, std::string &current_directory, std::string &current_file, bool has_empty, bool has_softlist, bool has_create, menu_file_selector::result &result)
+menu_file_selector::menu_file_selector(
+ mame_ui_manager &mui,
+ render_container &container,
+ device_image_interface *image,
+ std::string_view directory,
+ std::string_view file,
+ bool has_empty,
+ bool has_softlist,
+ bool has_create,
+ handler_function &&handler)
: menu(mui, container)
+ , m_handler(std::move(handler))
, m_image(image)
- , m_current_directory(current_directory)
- , m_current_file(current_file)
+ , m_current_directory(directory)
+ , m_current_file(file)
+ , m_result(result::INVALID)
, m_has_empty(has_empty)
, m_has_softlist(has_softlist)
, m_has_create(has_create)
- , m_result(result)
+ , m_clicked_directory(std::string::npos, std::string::npos)
{
(void)m_image;
set_process_flags(PROCESS_IGNOREPAUSE);
@@ -69,6 +82,8 @@ menu_file_selector::menu_file_selector(mame_ui_manager &mui, render_container &c
menu_file_selector::~menu_file_selector()
{
+ if (m_handler)
+ m_handler(m_result, std::move(m_current_directory), std::move(m_current_file));
}
@@ -80,7 +95,9 @@ void menu_file_selector::recompute_metrics(uint32_t width, uint32_t height, floa
{
menu::recompute_metrics(width, height, aspect);
- // set up custom render proc
+ m_path_layout.reset();
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+
set_custom_space(line_height() + 3.0F * tb_border(), 0.0F);
}
@@ -89,63 +106,131 @@ void menu_file_selector::recompute_metrics(uint32_t width, uint32_t height, floa
// custom_render - perform our special rendering
//-------------------------------------------------
-void menu_file_selector::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_file_selector::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
// lay out extra text
- auto layout = create_layout();
- layout.add_text(m_current_directory);
+ if (!m_path_layout)
+ {
+ m_path_layout.emplace(create_layout());
+ m_path_layout->add_text(m_current_directory);
+ }
+ else
+ {
+ rgb_t const fgcolor = ui().colors().text_color();
+ rgb_t const bgcolor = rgb_t::transparent();
+ m_path_layout->restyle(0, m_current_directory.length(), &fgcolor, &bgcolor);
+ }
// position this extra text
- float x1, y1, x2, y2;
- extra_text_position(origx1, origx2, origy1, top, layout, -1, x1, y1, x2, y2);
+ float x2, y2;
+ extra_text_position(origx1, origx2, origy1, top, *m_path_layout, -1, m_path_position.first, m_path_position.second, x2, y2);
// draw a box
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ ui().draw_outlined_box(container(), m_path_position.first, m_path_position.second, x2, y2, ui().colors().background_color());
// take off the borders
- x1 += lr_border();
- y1 += tb_border();
+ m_path_position.first += lr_border();
+ m_path_position.second += tb_border();
- size_t hit_start = 0, hit_span = 0;
- if (is_mouse_hit()
- && layout.hit_test(get_mouse_x() - x1, get_mouse_y() - y1, hit_start, hit_span)
- && m_current_directory.substr(hit_start, hit_span) != PATH_SEPARATOR)
+ if (m_clicked_directory.second > m_clicked_directory.first)
{
- // we're hovering over a directory! highlight it
- auto target_dir_start = m_current_directory.rfind(PATH_SEPARATOR, hit_start) + 1;
- auto target_dir_end = m_current_directory.find(PATH_SEPARATOR, hit_start + hit_span);
- m_hover_directory = m_current_directory.substr(0, target_dir_end + strlen(PATH_SEPARATOR));
-
- // highlight the text in question
- rgb_t fgcolor = ui().colors().mouseover_color();
- rgb_t bgcolor = ui().colors().mouseover_bg_color();
- layout.restyle(target_dir_start, target_dir_end - target_dir_start, &fgcolor, &bgcolor);
+ // see if it's still over the clicked path component
+ auto const [x, y] = pointer_location();
+ size_t start = 0, span = 0;
+ if (m_path_layout->hit_test(x - m_path_position.first, y - m_path_position.second, start, span))
+ {
+ if ((start >= m_clicked_directory.first) && ((start + span) <= m_clicked_directory.second))
+ {
+ rgb_t const fgcolor = ui().colors().selected_color();
+ rgb_t const bgcolor = ui().colors().selected_bg_color();
+ m_path_layout->restyle(m_clicked_directory.first, m_clicked_directory.second - m_clicked_directory.first, &fgcolor, &bgcolor);
+ }
+ }
}
- else
+ else if (pointer_idle())
{
- // we are not hovering over anything
- m_hover_directory.clear();
+ // see if it's hovering over a path component
+ auto const [x, y] = pointer_location();
+ auto const [target_dir_start, target_dir_end] = get_directory_range(x, y);
+ if (target_dir_end > target_dir_start)
+ {
+ rgb_t const fgcolor = ui().colors().mouseover_color();
+ rgb_t const bgcolor = ui().colors().mouseover_bg_color();
+ m_path_layout->restyle(target_dir_start, target_dir_end - target_dir_start, &fgcolor, &bgcolor);
+ }
}
// draw the text within it
- layout.emit(container(), x1, y1);
+ m_path_layout->emit(container(), m_path_position.first, m_path_position.second);
}
//-------------------------------------------------
-// custom_mouse_down - perform our special mouse down
+// custom_pointer_updated - perform our special
+// pointer handling
//-------------------------------------------------
-bool menu_file_selector::custom_mouse_down()
+std::tuple<int, bool, bool> menu_file_selector::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- if (m_hover_directory.length() > 0)
+ // track pointer after clicking a path component
+ if (m_clicked_directory.second > m_clicked_directory.first)
{
- m_current_directory = m_hover_directory;
- reset(reset_options::SELECT_FIRST);
- return true;
+ if (ui_event::type::POINTER_ABORT == uievt.event_type)
+ {
+ // abort always cancels
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ return std::make_tuple(IPT_INVALID, false, true);
+ }
+ else if (uievt.pointer_released & 0x01)
+ {
+ // releasing the primary button - check for dragging out
+ auto const [x, y] = pointer_location();
+ size_t start = 0, span = 0;
+ if (m_path_layout->hit_test(x - m_path_position.first, y - m_path_position.second, start, span))
+ {
+ // abuse IPT_CUSTOM to change to the clicked directory
+ if ((start >= m_clicked_directory.first) && ((start + span) <= m_clicked_directory.second))
+ return std::make_tuple(IPT_CUSTOM, false, true);
+ }
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ return std::make_tuple(IPT_INVALID, false, true);
+ }
+ else if (uievt.pointer_buttons & ~u32(1))
+ {
+ // pressing more buttons cancels
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ return std::make_tuple(IPT_INVALID, false, true);
+ }
+ else
+ {
+ // keep tracking the pointer
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
}
- return false;
+ // check for clicks if we have up-to-date content on-screen
+ if (m_path_layout && pointer_idle() && (uievt.pointer_buttons & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
+ {
+ auto const [x, y] = pointer_location();
+ auto const [target_dir_start, target_dir_end] = get_directory_range(x, y);
+ if (target_dir_end > target_dir_start)
+ {
+ m_clicked_directory = std::make_pair(target_dir_start, target_dir_end);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+
+//-------------------------------------------------
+// menu_activated - menu has gained focus
+//-------------------------------------------------
+
+void menu_file_selector::menu_activated()
+{
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
}
@@ -299,7 +384,9 @@ void menu_file_selector::select_item(const file_selector_entry &entry)
break;
}
}
- m_current_directory.assign(entry.fullpath);
+ m_current_directory = entry.fullpath;
+ m_path_layout.reset();
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
reset(reset_options::SELECT_FIRST);
break;
@@ -314,7 +401,7 @@ void menu_file_selector::select_item(const file_selector_entry &entry)
//-------------------------------------------------
-// type_search_char
+// update_search
//-------------------------------------------------
void menu_file_selector::update_search()
@@ -357,6 +444,35 @@ void menu_file_selector::update_search()
//-------------------------------------------------
+// get_directory_range
+//-------------------------------------------------
+
+std::pair<size_t, size_t> menu_file_selector::get_directory_range(float x, float y)
+{
+ size_t start = 0, span = 0;
+ if (m_path_layout->hit_test(x - m_path_position.first, y - m_path_position.second, start, span))
+ {
+ if (std::string_view(m_current_directory).substr(start, span) != PATH_SEPARATOR)
+ {
+ auto target_start = m_current_directory.rfind(PATH_SEPARATOR, start);
+ if (std::string::npos == target_start)
+ target_start = 0;
+ else
+ target_start += 1;
+
+ auto target_end = m_current_directory.find(PATH_SEPARATOR, start + span);
+ if (std::string::npos == target_end)
+ target_end = m_current_directory.length();
+
+ return std::make_pair(target_start, target_end);
+ }
+ }
+
+ return std::make_pair(std::string::npos, std::string::npos);
+}
+
+
+//-------------------------------------------------
// populate
//-------------------------------------------------
@@ -479,6 +595,18 @@ bool menu_file_selector::handle(event const *ev)
return true;
}
}
+ else if (ev->iptkey == IPT_CUSTOM)
+ {
+ // clicked a path component
+ if (m_clicked_directory.second > m_clicked_directory.first)
+ {
+ m_current_directory.resize(m_clicked_directory.second + strlen(PATH_SEPARATOR));
+ m_path_layout.reset();
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ reset(reset_options::SELECT_FIRST);
+ return true;
+ }
+ }
else if (ev->itemref && (ev->iptkey == IPT_UI_SELECT))
{
// handle selections
@@ -502,11 +630,15 @@ bool menu_file_selector::handle(event const *ev)
// ctor
//-------------------------------------------------
-menu_select_rw::menu_select_rw(mame_ui_manager &mui, render_container &container,
- bool can_in_place, result &result)
- : menu(mui, container),
- m_can_in_place(can_in_place),
- m_result(result)
+menu_select_rw::menu_select_rw(
+ mame_ui_manager &mui,
+ render_container &container,
+ bool can_in_place,
+ handler_function &&handler)
+ : menu(mui, container)
+ , m_handler(std::move(handler))
+ , m_can_in_place(can_in_place)
+ , m_result(result::INVALID)
{
}
@@ -517,6 +649,8 @@ menu_select_rw::menu_select_rw(mame_ui_manager &mui, render_container &container
menu_select_rw::~menu_select_rw()
{
+ if (m_handler)
+ m_handler(m_result);
}
@@ -526,7 +660,8 @@ menu_select_rw::~menu_select_rw()
void menu_select_rw::populate()
{
- item_append(_("Select access mode"), FLAG_DISABLE, nullptr);
+ set_heading(_("Select access mode"));
+
item_append(_("Read-only"), 0, itemref_from_result(result::READONLY));
if (m_can_in_place)
item_append(_("Read-write"), 0, itemref_from_result(result::READWRITE));
diff --git a/src/frontend/mame/ui/filesel.h b/src/frontend/mame/ui/filesel.h
index 8bef06e53ca..6ca9cafbe68 100644
--- a/src/frontend/mame/ui/filesel.h
+++ b/src/frontend/mame/ui/filesel.h
@@ -15,6 +15,15 @@
#include "ui/menu.h"
+#include <functional>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+
namespace ui {
// ======================> menu_file_selector
@@ -31,23 +40,26 @@ public:
FILE
};
+ using handler_function = std::function<void (result result, std::string &&directory, std::string &&file)>;
+
menu_file_selector(
mame_ui_manager &mui,
render_container &container,
device_image_interface *image,
- std::string &current_directory,
- std::string &current_file,
+ std::string_view directory,
+ std::string_view file,
bool has_empty,
bool has_softlist,
bool has_create,
- result &result);
+ handler_function &&handler);
virtual ~menu_file_selector() override;
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual bool custom_ui_back() override { return !m_filename.empty(); }
- virtual bool custom_mouse_down() override;
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
+ virtual void menu_activated() override;
private:
enum file_selector_entry_type
@@ -72,16 +84,19 @@ private:
};
// internal state
- device_image_interface *const m_image;
- std::string & m_current_directory;
- std::string & m_current_file;
- bool const m_has_empty;
- bool const m_has_softlist;
- bool const m_has_create;
- result & m_result;
+ handler_function const m_handler;
+ device_image_interface *const m_image;
+ std::string m_current_directory;
+ std::string m_current_file;
+ std::optional<text_layout> m_path_layout;
+ std::pair<float, float> m_path_position;
+ result m_result;
+ bool const m_has_empty;
+ bool const m_has_softlist;
+ bool const m_has_create;
std::vector<file_selector_entry> m_entrylist;
- std::string m_hover_directory;
- std::string m_filename;
+ std::string m_filename;
+ std::pair<size_t, size_t> m_clicked_directory;
virtual void populate() override;
virtual bool handle(event const *ev) override;
@@ -93,6 +108,7 @@ private:
void append_entry_menu_item(const file_selector_entry *entry);
void select_item(const file_selector_entry &entry);
void update_search();
+ std::pair<size_t, size_t> get_directory_range(float x, float y);
};
@@ -109,23 +125,27 @@ public:
WRITE_OTHER,
WRITE_DIFF
};
+
+ using handler_function = std::function<void (result result)>;
+
menu_select_rw(
mame_ui_manager &mui,
render_container &container,
bool can_in_place,
- result &result);
+ handler_function &&handler);
virtual ~menu_select_rw() override;
- static void *itemref_from_result(result result);
- static result result_from_itemref(void *itemref);
-
private:
virtual void populate() override;
virtual bool handle(event const *ev) override;
+ static void *itemref_from_result(result result);
+ static result result_from_itemref(void *itemref);
+
// internal state
- bool m_can_in_place;
- result & m_result;
+ handler_function const m_handler;
+ bool const m_can_in_place;
+ result m_result;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/floppycntrl.cpp b/src/frontend/mame/ui/floppycntrl.cpp
index 83317129ca8..3f6f9bdfb61 100644
--- a/src/frontend/mame/ui/floppycntrl.cpp
+++ b/src/frontend/mame/ui/floppycntrl.cpp
@@ -86,7 +86,8 @@ void menu_control_floppy_image::hook_load(const std::string &filename)
else
{
bool can_in_place = input_format->supports_save();
- if(can_in_place) {
+ if (can_in_place)
+ {
std::string tmp_path;
util::core_file::ptr tmp_file;
// attempt to open the file for writing but *without* create
@@ -96,9 +97,41 @@ void menu_control_floppy_image::hook_load(const std::string &filename)
else
can_in_place = false;
}
- m_submenu_result.rw = menu_select_rw::result::INVALID;
- menu::stack_push<menu_select_rw>(ui(), container(), can_in_place, m_submenu_result.rw);
- m_state = SELECT_RW;
+ menu::stack_push<menu_select_rw>(
+ ui(),
+ container(),
+ can_in_place,
+ [this] (menu_select_rw::result result)
+ {
+ switch (result)
+ {
+ case menu_select_rw::result::READONLY:
+ do_load_create();
+ fd.setup_write(nullptr);
+ stack_pop();
+ break;
+
+ case menu_select_rw::result::READWRITE:
+ output_format = input_format;
+ do_load_create();
+ stack_pop();
+ break;
+
+ case menu_select_rw::result::WRITE_DIFF:
+ machine().popmessage("Sorry, diffs are not supported yet\n");
+ stack_pop();
+ break;
+
+ case menu_select_rw::result::WRITE_OTHER:
+ menu::stack_push<menu_file_create>(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok);
+ m_state = CHECK_CREATE;
+ break;
+
+ case menu_select_rw::result::INVALID:
+ m_state = START_FILE;
+ break;
+ }
+ });
}
}
@@ -182,37 +215,6 @@ void menu_control_floppy_image::menu_activated()
}
break;
- case SELECT_RW:
- switch(m_submenu_result.rw) {
- case menu_select_rw::result::READONLY:
- do_load_create();
- fd.setup_write(nullptr);
- stack_pop();
- break;
-
- case menu_select_rw::result::READWRITE:
- output_format = input_format;
- do_load_create();
- stack_pop();
- break;
-
- case menu_select_rw::result::WRITE_DIFF:
- machine().popmessage("Sorry, diffs are not supported yet\n");
- stack_pop();
- break;
-
- case menu_select_rw::result::WRITE_OTHER:
- menu::stack_push<menu_file_create>(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok);
- m_state = CHECK_CREATE;
- break;
-
- case menu_select_rw::result::INVALID:
- m_state = START_FILE;
- menu_activated();
- break;
- }
- break;
-
default:
menu_control_device_image::menu_activated();
}
diff --git a/src/frontend/mame/ui/floppycntrl.h b/src/frontend/mame/ui/floppycntrl.h
index 8be8e7ca02a..8bcb02d15b5 100644
--- a/src/frontend/mame/ui/floppycntrl.h
+++ b/src/frontend/mame/ui/floppycntrl.h
@@ -29,7 +29,7 @@ protected:
virtual void menu_activated() override;
private:
- enum { SELECT_FORMAT = LAST_ID, SELECT_MEDIA, SELECT_INIT, SELECT_RW };
+ enum { SELECT_FORMAT = LAST_ID, SELECT_MEDIA, SELECT_INIT };
floppy_image_device &fd;
const floppy_image_format_t *input_format, *output_format;
diff --git a/src/frontend/mame/ui/imgcntrl.cpp b/src/frontend/mame/ui/imgcntrl.cpp
index 8f8aaffd3bb..6dd909f711c 100644
--- a/src/frontend/mame/ui/imgcntrl.cpp
+++ b/src/frontend/mame/ui/imgcntrl.cpp
@@ -9,7 +9,6 @@
***************************************************************************/
#include "emu.h"
-
#include "ui/imgcntrl.h"
#include "ui/filecreate.h"
@@ -243,7 +242,6 @@ void menu_control_device_image::menu_activated()
switch(m_state)
{
case START_FILE:
- m_submenu_result.filesel = menu_file_selector::result::INVALID;
menu::stack_push<menu_file_selector>(
ui(), container(),
&m_image,
@@ -252,8 +250,35 @@ void menu_control_device_image::menu_activated()
true,
m_image.image_interface() != nullptr,
m_image.is_creatable(),
- m_submenu_result.filesel);
- m_state = SELECT_FILE;
+ [this] (menu_file_selector::result result, std::string &&directory, std::string &&file)
+ {
+ m_current_directory = std::move(directory);
+ m_current_file = std::move(file);
+ switch (result)
+ {
+ case menu_file_selector::result::EMPTY:
+ m_image.unload();
+ stack_pop();
+ break;
+
+ case menu_file_selector::result::FILE:
+ hook_load(m_current_file);
+ break;
+
+ case menu_file_selector::result::CREATE:
+ menu::stack_push<menu_file_create>(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok);
+ m_state = CHECK_CREATE;
+ break;
+
+ case menu_file_selector::result::SOFTLIST:
+ m_state = START_SOFTLIST;
+ break;
+
+ default: // return to system
+ stack_pop();
+ break;
+ }
+ });
break;
case START_SOFTLIST:
@@ -344,34 +369,6 @@ void menu_control_device_image::menu_activated()
}
break;
- case SELECT_FILE:
- switch (m_submenu_result.filesel)
- {
- case menu_file_selector::result::EMPTY:
- m_image.unload();
- stack_pop();
- break;
-
- case menu_file_selector::result::FILE:
- hook_load(m_current_file);
- break;
-
- case menu_file_selector::result::CREATE:
- menu::stack_push<menu_file_create>(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok);
- m_state = CHECK_CREATE;
- break;
-
- case menu_file_selector::result::SOFTLIST:
- m_state = START_SOFTLIST;
- menu_activated();
- break;
-
- default: // return to system
- stack_pop();
- break;
- }
- break;
-
case CREATE_FILE:
{
bool can_create, need_confirm;
diff --git a/src/frontend/mame/ui/imgcntrl.h b/src/frontend/mame/ui/imgcntrl.h
index 22911f70141..c081291f2e1 100644
--- a/src/frontend/mame/ui/imgcntrl.h
+++ b/src/frontend/mame/ui/imgcntrl.h
@@ -4,7 +4,7 @@
ui/imgcntrl.h
- MESS's clunky built-in file manager
+ MAME's clunky built-in file manager
***************************************************************************/
@@ -13,7 +13,6 @@
#pragma once
-#include "ui/filesel.h"
#include "ui/menu.h"
#include "ui/swlist.h"
@@ -33,7 +32,7 @@ protected:
{
START_FILE, START_OTHER_PART, START_SOFTLIST,
SELECT_PARTLIST, SELECT_ONE_PART, SELECT_OTHER_PART,
- SELECT_FILE, CREATE_FILE, CREATE_CONFIRM, CHECK_CREATE, DO_CREATE, SELECT_SOFTLIST,
+ CREATE_FILE, CREATE_CONFIRM, CHECK_CREATE, DO_CREATE, SELECT_SOFTLIST,
LAST_ID
};
@@ -41,9 +40,7 @@ protected:
// results we could get from child menus
union
{
- menu_file_selector::result filesel;
menu_software_parts::result swparts;
- menu_select_rw::result rw;
int i;
} m_submenu_result;
diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp
index 813bbd4b8c8..945c18d32a2 100644
--- a/src/frontend/mame/ui/info.cpp
+++ b/src/frontend/mame/ui/info.cpp
@@ -576,11 +576,6 @@ void menu_game_info::populate()
{
}
-bool menu_game_info::handle(event const *ev)
-{
- return ev && handle_key(ev->iptkey);
-}
-
/*-------------------------------------------------
menu_warn_info - handle the emulation warnings menu
@@ -640,11 +635,6 @@ void menu_warn_info::populate()
{
}
-bool menu_warn_info::handle(event const *ev)
-{
- return ev && handle_key(ev->iptkey);
-}
-
/*-------------------------------------------------
menu_image_info - handle the image information menu
diff --git a/src/frontend/mame/ui/info.h b/src/frontend/mame/ui/info.h
index 12eebc769c7..c1b5d318a69 100644
--- a/src/frontend/mame/ui/info.h
+++ b/src/frontend/mame/ui/info.h
@@ -104,7 +104,6 @@ protected:
private:
virtual void populate() override;
- virtual bool handle(event const *ev) override;
};
@@ -119,7 +118,6 @@ protected:
private:
virtual void populate() override;
- virtual bool handle(event const *ev) override;
};
diff --git a/src/frontend/mame/ui/inputdevices.cpp b/src/frontend/mame/ui/inputdevices.cpp
index 0fe8de2a1c0..1ee03ad5c81 100644
--- a/src/frontend/mame/ui/inputdevices.cpp
+++ b/src/frontend/mame/ui/inputdevices.cpp
@@ -44,7 +44,7 @@ protected:
set_custom_space(0.0F, (line_height() * (m_have_analog ? 2.0F : 1.0F)) + (tb_border() * 3.0F));
}
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
{
if (selectedref)
{
diff --git a/src/frontend/mame/ui/inputmap.cpp b/src/frontend/mame/ui/inputmap.cpp
index b5b56b6e1fb..d8a4d9656f4 100644
--- a/src/frontend/mame/ui/inputmap.cpp
+++ b/src/frontend/mame/ui/inputmap.cpp
@@ -322,7 +322,7 @@ void menu_input::recompute_metrics(uint32_t width, uint32_t height, float aspect
}
-void menu_input::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+void menu_input::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
if (pollingitem)
{
diff --git a/src/frontend/mame/ui/inputmap.h b/src/frontend/mame/ui/inputmap.h
index e5947a9b649..585d8002a8c 100644
--- a/src/frontend/mame/ui/inputmap.h
+++ b/src/frontend/mame/ui/inputmap.h
@@ -65,7 +65,7 @@ protected:
menu_input(mame_ui_manager &mui, render_container &container);
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) override;
void populate_sorted();
void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq);
diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp
index 71e15631879..a5c82aa90c2 100644
--- a/src/frontend/mame/ui/menu.cpp
+++ b/src/frontend/mame/ui/menu.cpp
@@ -13,7 +13,6 @@
#include "ui/ui.h"
#include "ui/mainmenu.h"
-#include "ui/utils.h"
#include "ui/miscmenu.h"
#include "cheat.h"
@@ -27,9 +26,9 @@
#include "osdepend.h"
-#include <algorithm>
#include <cassert>
-#include <cmath>
+#include <cstdlib>
+#include <limits>
#include <type_traits>
@@ -77,6 +76,12 @@ menu::global_state::global_state(mame_ui_manager &ui)
, m_stack()
, m_free()
, m_hide(false)
+ , m_current_pointer(-1)
+ , m_pointer_type(ui_event::pointer::UNKNOWN)
+ , m_pointer_buttons(0U)
+ , m_pointer_x(-1.0F)
+ , m_pointer_y(-1.0F)
+ , m_pointer_hit(false)
{
render_manager &render(ui.machine().render());
@@ -122,6 +127,21 @@ void menu::global_state::stack_push(std::unique_ptr<menu> &&menu)
}
menu->m_parent = std::move(m_stack);
m_stack = std::move(menu);
+
+ ui_event uievt;
+ while (m_stack->machine().ui_input().pop_event(&uievt))
+ {
+ switch (uievt.event_type)
+ {
+ case ui_event::type::POINTER_UPDATE:
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ use_pointer(m_stack->machine().render().ui_target(), m_stack->container(), uievt);
+ break;
+ default:
+ break;
+ }
+ }
m_stack->machine().ui_input().reset();
}
@@ -142,7 +162,22 @@ void menu::global_state::stack_pop()
m_stack = std::move(menu->m_parent);
menu->m_parent = std::move(m_free);
m_free = std::move(menu);
- m_ui.machine().ui_input().reset();
+
+ ui_event uievt;
+ while (m_free->machine().ui_input().pop_event(&uievt))
+ {
+ switch (uievt.event_type)
+ {
+ case ui_event::type::POINTER_UPDATE:
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ use_pointer(m_free->machine().render().ui_target(), m_free->container(), uievt);
+ break;
+ default:
+ break;
+ }
+ }
+ m_free->machine().ui_input().reset();
}
}
@@ -187,39 +222,155 @@ uint32_t menu::global_state::ui_handler(render_container &container)
if (!m_stack)
stack_push(std::make_unique<menu_main>(m_ui, container));
- // ensure topmost menu is active - need a loop because it could push another menu
- while (m_stack && !m_stack->is_active())
+ while (true)
{
- m_stack->m_active = true;
- m_stack->menu_activated();
- }
+ // ensure topmost menu is active - need a loop because it could push another menu
+ while (m_stack && !m_stack->is_active())
+ {
+ m_stack->m_items_drawn = false;
+ m_stack->m_pointer_state = track_pointer::IDLE;
+ m_stack->m_active = true;
+ m_stack->menu_activated();
+ if (m_stack && m_stack->is_active())
+ {
+ // menu activated - draw it to ensure it's on-screen before it can process input
+ m_stack->check_metrics();
+ m_stack->do_rebuild();
+ m_stack->validate_selection(1);
+ m_stack->do_draw_menu();
+ assert(m_stack);
+ assert(m_stack->is_active());
+
+ // display pointer if appropriate
+ mame_ui_manager::display_pointer pointers[1]{ { m_stack->machine().render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } };
+ if ((0 <= m_current_pointer) && (ui_event::pointer::TOUCH != m_pointer_type))
+ m_ui.set_pointers(std::begin(pointers), std::end(pointers));
+ else
+ m_ui.set_pointers(std::begin(pointers), std::begin(pointers));
+
+ return mame_ui_manager::HANDLER_UPDATE;
+ }
+ }
- // update the menu state
- m_hide = false;
- bool need_update = m_stack && m_stack->do_handle();
+ // update the menu state
+ m_hide = false;
+ bool need_update(m_stack && m_stack->do_handle());
- // clear up anything pending being released
- clear_free_list();
+ // clear up anything pending being released
+ clear_free_list();
- // if the menus are to be hidden, return a cancel here
- if (m_ui.is_menu_active() && (m_hide || !m_stack))
- {
- if (m_stack)
+ // if the menus are to be hidden, return a cancel here
+ if (m_ui.is_menu_active() && (m_hide || !m_stack))
{
- if (m_stack->is_one_shot())
+ if (m_stack)
{
- stack_pop();
+ if (m_stack->is_one_shot())
+ {
+ stack_pop();
+ }
+ else if (m_stack->is_active())
+ {
+ m_stack->m_active = false;
+ m_stack->menu_deactivated();
+ }
}
- else if (m_stack->is_active())
+
+ // forget about pointers while menus aren't handling events
+ m_current_pointer = -1;
+ m_pointer_type = ui_event::pointer::UNKNOWN;
+ m_pointer_buttons = 0U;
+ m_pointer_x = -1.0F;
+ m_pointer_y = -1.0F;
+ m_pointer_hit = false;
+
+ return mame_ui_manager::HANDLER_CANCEL;
+ }
+
+ // if the menu is still active, draw it, otherwise try again
+ if (m_stack->is_active())
+ {
+ m_stack->do_draw_menu();
+
+ // display pointer if appropriate
+ mame_ui_manager::display_pointer pointers[1]{ { m_stack->machine().render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } };
+ if ((0 <= m_current_pointer) && (ui_event::pointer::TOUCH != m_pointer_type))
+ m_ui.set_pointers(std::begin(pointers), std::end(pointers));
+ else
+ m_ui.set_pointers(std::begin(pointers), std::begin(pointers));
+
+ return need_update ? mame_ui_manager::HANDLER_UPDATE : 0;
+ }
+ }
+}
+
+
+std::pair<bool, bool> menu::global_state::use_pointer(render_target &target, render_container &container, ui_event const &uievt)
+{
+ if (&target != uievt.target)
+ return std::make_pair(false, false);
+
+ switch (uievt.event_type)
+ {
+ case ui_event::type::POINTER_UPDATE:
+ // if it's our current pointer, just update it
+ if (uievt.pointer_id == m_current_pointer)
+ {
+ assert(uievt.pointer_type == m_pointer_type);
+ assert(uievt.pointer_buttons == ((m_pointer_buttons & ~uievt.pointer_released) | uievt.pointer_pressed));
+
+ m_pointer_buttons = uievt.pointer_buttons;
+ m_pointer_hit = target.map_point_container(
+ uievt.pointer_x,
+ uievt.pointer_y,
+ container,
+ m_pointer_x,
+ m_pointer_y);
+ return std::make_pair(true, false);
+ }
+
+ // don't change if the current pointer has buttons pressed and this one doesn't
+ if ((0 > m_current_pointer) || (!m_pointer_buttons && (!m_pointer_hit || uievt.pointer_pressed)))
+ {
+ float x, y;
+ bool const hit(target.map_point_container(uievt.pointer_x, uievt.pointer_y, container, x, y));
+ if ((0 > m_current_pointer) || uievt.pointer_pressed || (!m_pointer_hit && hit))
{
- m_stack->m_active = false;
- m_stack->menu_deactivated();
+ m_current_pointer = uievt.pointer_id;
+ m_pointer_type = uievt.pointer_type;
+ m_pointer_buttons = uievt.pointer_buttons;
+ m_pointer_x = x;
+ m_pointer_y = y;
+ m_pointer_hit = hit;
+ return std::make_pair(true, true);
}
}
- return mame_ui_manager::HANDLER_CANCEL;
- }
- return need_update ? mame_ui_manager::HANDLER_UPDATE : 0;
+ // keep current pointer
+ return std::make_pair(false, false);
+
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ // irrelevant if it isn't our current pointer
+ if (uievt.pointer_id != m_current_pointer)
+ return std::make_pair(false, false);
+
+ assert(uievt.pointer_type == m_pointer_type);
+ assert(uievt.pointer_released == m_pointer_buttons);
+
+ // keep the coordinates where we lost the pointer
+ m_current_pointer = -1;
+ m_pointer_buttons = 0U;
+ m_pointer_hit = target.map_point_container(
+ uievt.pointer_x,
+ uievt.pointer_y,
+ container,
+ m_pointer_x,
+ m_pointer_y);
+ return std::make_pair(true, false);
+
+ default:
+ std::abort();
+ }
}
@@ -248,22 +399,31 @@ menu::menu(mame_ui_manager &mui, render_container &container)
, m_lr_border(0.0F)
, m_lr_arrow_width(0.0F)
, m_ud_arrow_width(0.0F)
+ , m_items_left(0.0F)
+ , m_items_right(0.0F)
+ , m_items_top(0.0F)
+ , m_adjust_top(0.0F)
+ , m_adjust_bottom(0.0F)
+ , m_decrease_left(0.0F)
+ , m_increase_left(0.0F)
+ , m_show_up_arrow(false)
+ , m_show_down_arrow(false)
+ , m_items_drawn(false)
+ , m_pointer_state(track_pointer::IDLE)
+ , m_pointer_down(0.0F, 0.0F)
+ , m_pointer_updated(0.0F, 0.0F)
+ , m_pointer_line(0)
+ , m_pointer_repeat(std::chrono::steady_clock::time_point::min())
, m_process_flags(0)
, m_selected(0)
- , m_hover(1)
, m_special_main_menu(false)
, m_one_shot(false)
, m_needs_prev_menu_item(true)
, m_active(false)
- , m_event()
, m_customtop(0.0F)
, m_custombottom(0.0F)
, m_resetpos(0)
, m_resetref(nullptr)
- , m_mouse_hit(false)
- , m_mouse_button(false)
- , m_mouse_x(-1.0F)
- , m_mouse_y(-1.0F)
{
reset(reset_options::SELECT_FIRST);
@@ -288,6 +448,10 @@ menu::~menu()
void menu::reset(reset_options options)
{
+ // don't accept pointer input until the menu has been redrawn
+ m_items_drawn = false;
+ m_pointer_state = track_pointer::IDLE;
+
// based on the reset option, set the reset info
m_resetpos = 0;
m_resetref = nullptr;
@@ -393,62 +557,6 @@ void menu::set_custom_space(float top, float bottom)
//-------------------------------------------------
-// process - process a menu, drawing it
-// and returning any interesting events
-//-------------------------------------------------
-
-std::pair<const menu::event *, bool> menu::process()
-{
- // reset the event
- m_event.iptkey = IPT_INVALID;
-
- // first make sure our selection is valid
- validate_selection(1);
-
- // if we're not running the emulation, draw parent menus in the background
- auto const draw_parent =
- [] (auto &self, menu *parent) -> bool
- {
- if (!parent || !(parent->is_special_main_menu() || self(self, parent->m_parent.get())))
- return false;
- else
- parent->draw(PROCESS_NOINPUT);
- return true;
- };
- if (draw_parent(draw_parent, m_parent.get()))
- container().add_rect(0.0F, 0.0F, 1.0F, 1.0F, rgb_t(114, 0, 0, 0), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-
- // draw the menu proper
- // FIXME: this should happen after processing events to fix the egregious latency
- // The main thing preventing this is that mouse handling is mixed up with drawing
- draw(m_process_flags);
-
- // process input
- if (!(m_process_flags & (PROCESS_NOKEYS | PROCESS_NOINPUT)))
- {
- // read events
- handle_events(m_process_flags, m_event);
-
- // handle the keys if we don't already have an event
- if (m_event.iptkey == IPT_INVALID)
- handle_keys(m_process_flags, m_event.iptkey);
- }
-
- // update the selected item in the event
- if ((m_event.iptkey != IPT_INVALID) && selection_valid())
- {
- m_event.itemref = get_selection_ref();
- m_event.item = &m_items[m_selected];
- return std::make_pair(&m_event, false);
- }
- else
- {
- return std::make_pair(nullptr, false);
- }
-}
-
-
-//-------------------------------------------------
// set_selection - changes the index
// of the currently selected menu item
//-------------------------------------------------
@@ -473,27 +581,37 @@ void menu::set_selection(void *selected_itemref)
***************************************************************************/
//-------------------------------------------------
-// draw - draw a menu
+// do_draw_menu - draw a menu
//-------------------------------------------------
-void menu::draw(uint32_t flags)
+void menu::do_draw_menu()
{
- // first draw the FPS counter
- // FIXME: provide a way to do this in the UI manager itself while menus are on-screen
- if (ui().show_fps_counter())
- {
- ui().draw_text_full(
- container(),
- machine().video().speed_text(),
- 0.0F, 0.0F, 1.0F,
- text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD,
- mame_ui_manager::OPAQUE_, rgb_t::white(), rgb_t::black(),
- nullptr, nullptr);
- }
+ // if we're not running the emulation, draw parent menus in the background
+ auto const draw_parent =
+ [] (auto &self, menu *parent) -> bool
+ {
+ if (!parent || !(parent->is_special_main_menu() || self(self, parent->m_parent.get())))
+ return false;
+ else
+ parent->draw(PROCESS_NOINPUT);
+ return true;
+ };
+ if (draw_parent(draw_parent, m_parent.get()))
+ container().add_rect(0.0F, 0.0F, 1.0F, 1.0F, rgb_t(114, 0, 0, 0), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ // draw the menu proper
+ draw(m_process_flags);
+}
+
+
+//-------------------------------------------------
+// draw - draw the menu itself
+//-------------------------------------------------
+void menu::draw(uint32_t flags)
+{
bool const customonly = (flags & PROCESS_CUSTOM_ONLY);
- bool const noinput = (flags & PROCESS_NOINPUT);
- float const max_width = 1.0F - ((lr_border() + (machine().render().ui_aspect(&container()) * UI_LINE_WIDTH)) * 2.0F);
+ float const max_width = 1.0F - ((lr_border() + (x_aspect() * UI_LINE_WIDTH)) * 2.0F);
if (is_special_main_menu())
draw_background();
@@ -550,13 +668,13 @@ void menu::draw(uint32_t flags)
// compute top/left of inner menu area by centering
float const visible_left = (1.0F - visible_width) * 0.5F;
- float const visible_top = std::round((((1.0F - visible_main_menu_height - visible_extra_menu_height) * 0.5F) + top_extra_menu_height) * float(m_last_size.second)) / float(m_last_size.second);
+ m_items_top = std::round((((1.0F - visible_main_menu_height - visible_extra_menu_height) * 0.5F) + top_extra_menu_height) * float(m_last_size.second)) / float(m_last_size.second);
// first add us a box
float const x1 = visible_left - lr_border();
- float const y1 = visible_top - tb_border();
+ float const y1 = m_items_top - tb_border();
float const x2 = visible_left + visible_width + lr_border();
- float const y2 = visible_top + visible_main_menu_height + tb_border();
+ float const y2 = m_items_top + visible_main_menu_height + tb_border();
if (!customonly)
{
if (heading_layout)
@@ -586,29 +704,43 @@ void menu::draw(uint32_t flags)
top_line = m_selected - m_visible_lines + ((m_selected == (m_items.size() - 1)) ? 1: 2);
// if scrolling, show arrows
- bool const show_top_arrow((m_items.size() > m_visible_lines) && !first_item_visible());
- bool const show_bottom_arrow((m_items.size() > m_visible_lines) && !last_item_visible());
+ m_show_up_arrow = (m_items.size() > m_visible_lines) && !first_item_visible();
+ m_show_down_arrow = (m_items.size() > m_visible_lines) && !last_item_visible();
// set the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
- m_visible_items = m_visible_lines - (show_top_arrow ? 1 : 0) - (show_bottom_arrow ? 1 : 0);
+ m_visible_items = m_visible_lines - (m_show_up_arrow ? 1 : 0) - (m_show_down_arrow ? 1 : 0);
// determine effective positions taking into account the hilighting arrows
float const effective_width = visible_width - 2.0F * gutter_width();
float const effective_left = visible_left + gutter_width();
- // locate mouse
- if (!customonly && !noinput)
- map_mouse();
- else
- ignore_mouse();
-
// loop over visible lines
- m_hover = m_items.size() + 1;
bool selected_subitem_too_big = false;
- float const line_x0 = x1 + 0.5F * UI_LINE_WIDTH;
- float const line_x1 = x2 - 0.5F * UI_LINE_WIDTH;
- if (!customonly)
+ m_items_left = x1 + 0.5F * UI_LINE_WIDTH;
+ m_items_right = x2 - 0.5F * UI_LINE_WIDTH;
+ if (customonly)
+ {
+ m_items_drawn = false;
+ switch (m_pointer_state)
+ {
+ case track_pointer::IDLE:
+ case track_pointer::IGNORED:
+ case track_pointer::COMPLETED:
+ case track_pointer::CUSTOM:
+ break;
+ case track_pointer::TRACK_LINE:
+ case track_pointer::SCROLL:
+ case track_pointer::ADJUST:
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ }
+ else
{
+ m_adjust_top = 1.0F;
+ m_adjust_bottom = 0.0F;
+ m_decrease_left = -1.0F;
+ m_increase_left = -1.0F;
+ m_items_drawn = true;
for (int linenum = 0; linenum < m_visible_lines; linenum++)
{
auto const itemnum = top_line + linenum;
@@ -618,63 +750,66 @@ void menu::draw(uint32_t flags)
rgb_t bgcolor = ui().colors().text_bg_color();
rgb_t fgcolor2 = ui().colors().subitem_color();
rgb_t fgcolor3 = ui().colors().clone_color();
- float const line_y0 = visible_top + (float)linenum * line_height();
+ float const line_y0 = m_items_top + (float(linenum) * line_height());
float const line_y1 = line_y0 + line_height();
// work out what we're dealing with
- bool const uparrow = !linenum && show_top_arrow;
- bool const downarrow = (linenum == (m_visible_lines - 1)) && show_bottom_arrow;
-
- // set the hover if this is our item
- bool const hovered = mouse_in_rect(line_x0, line_y0, line_x1, line_y1);
- if (hovered)
- {
- if (uparrow)
- m_hover = HOVER_ARROW_UP;
- else if (downarrow)
- m_hover = HOVER_ARROW_DOWN;
- else if (is_selectable(pitem))
- m_hover = itemnum;
- }
+ bool const uparrow = !linenum && m_show_up_arrow;
+ bool const downarrow = (linenum == (m_visible_lines - 1)) && m_show_down_arrow;
+ // highlight if necessary
if (is_selected(itemnum))
{
// if we're selected, draw with a different background
fgcolor = fgcolor2 = fgcolor3 = ui().colors().selected_color();
bgcolor = ui().colors().selected_bg_color();
}
- else if (hovered && (uparrow || downarrow || is_selectable(pitem)))
+ else if (uparrow || downarrow || is_selectable(pitem))
{
- // else if the mouse is over this item, draw with a different background
- fgcolor = fgcolor2 = fgcolor3 = ui().colors().mouseover_color();
- bgcolor = ui().colors().mouseover_bg_color();
+ bool pointerline(linenum == m_pointer_line);
+ if ((track_pointer::ADJUST == m_pointer_state) && pointerline)
+ {
+ // use the hover background if an adjust gesture is attempted on an item that isn't selected
+ fgcolor = fgcolor2 = fgcolor3 = ui().colors().mouseover_color();
+ bgcolor = ui().colors().mouseover_bg_color();
+ }
+ else if (have_pointer() && pointer_in_rect(m_items_left, line_y0, m_items_right, line_y1))
+ {
+ if ((track_pointer::TRACK_LINE == m_pointer_state) && pointerline)
+ {
+ // use the selected background for an item being selected
+ fgcolor = fgcolor2 = fgcolor3 = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ }
+ else if (track_pointer::IDLE == m_pointer_state)
+ {
+ // else if the pointer is over this item, draw with a different background
+ fgcolor = fgcolor2 = fgcolor3 = ui().colors().mouseover_color();
+ bgcolor = ui().colors().mouseover_bg_color();
+ }
+ }
+ else if ((track_pointer::TRACK_LINE == m_pointer_state) && pointerline)
+ {
+ // use the hover background if the pointer moved out of the tracked item
+ fgcolor = fgcolor2 = fgcolor3 = ui().colors().mouseover_color();
+ bgcolor = ui().colors().mouseover_bg_color();
+ }
}
// if we have some background hilighting to do, add a quad behind everything else
if (bgcolor != ui().colors().text_bg_color())
- highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
+ highlight(m_items_left, line_y0, m_items_right, line_y1, bgcolor);
- if (uparrow)
- {
- // if we're on the top line, display the up arrow
- draw_arrow(
- 0.5F * (x1 + x2) - 0.5F * ud_arrow_width(),
- line_y0 + 0.25F * line_height(),
- 0.5F * (x1 + x2) + 0.5F * ud_arrow_width(),
- line_y0 + 0.75F * line_height(),
- fgcolor,
- ROT0);
- }
- else if (downarrow)
+ if (uparrow || downarrow)
{
- // if we're on the bottom line, display the down arrow
+ // if we're on the top or bottom line, display the up or down arrow
draw_arrow(
- 0.5F * (x1 + x2) - 0.5F * ud_arrow_width(),
- line_y0 + 0.25F * line_height(),
- 0.5F * (x1 + x2) + 0.5F * ud_arrow_width(),
- line_y0 + 0.75F * line_height(),
+ 0.5F * (x1 + x2 - ud_arrow_width()),
+ line_y0 + (0.25F * line_height()),
+ 0.5F * (x1 + x2 + ud_arrow_width()),
+ line_y0 + (0.75F * line_height()),
fgcolor,
- ROT0 ^ ORIENTATION_FLIP_Y);
+ downarrow ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
}
else if (pitem.type() == menu_item_type::SEPARATOR)
{
@@ -765,27 +900,22 @@ void menu::draw(uint32_t flags)
}
// apply arrows
- if (is_selected(itemnum) && (pitem.flags() & FLAG_LEFT_ARROW))
+ if (is_selected(itemnum) && (pitem.flags() & (FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW)))
{
- float const l = effective_left + effective_width - subitem_width - gutter_width();
- float const r = l + lr_arrow_width();
- draw_arrow(
- l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height(),
- fgcolor,
- ROT90 ^ ORIENTATION_FLIP_X);
- if (mouse_in_rect(l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height()))
- m_hover = HOVER_UI_LEFT;
- }
- if (is_selected(itemnum) && (pitem.flags() & FLAG_RIGHT_ARROW))
- {
- float const r = effective_left + effective_width + gutter_width();
- float const l = r - lr_arrow_width();
- draw_arrow(
- l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height(),
- fgcolor,
- ROT90);
- if (mouse_in_rect(l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height()))
- m_hover = HOVER_UI_RIGHT;
+ m_adjust_top = line_y0 + (0.1F * line_height());
+ m_adjust_bottom = line_y0 + (0.9F * line_height());
+ if (pitem.flags() & FLAG_LEFT_ARROW)
+ {
+ m_decrease_left = effective_left + effective_width - subitem_width - gutter_width();
+ float const r = m_decrease_left + lr_arrow_width();
+ draw_arrow(m_decrease_left, m_adjust_top, r, m_adjust_bottom, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
+ }
+ if (pitem.flags() & FLAG_RIGHT_ARROW)
+ {
+ float const r = effective_left + effective_width + gutter_width();
+ m_increase_left = r - lr_arrow_width();
+ draw_arrow(m_increase_left, m_adjust_top, r, m_adjust_bottom, fgcolor, ROT90);
+ }
}
}
}
@@ -797,7 +927,7 @@ void menu::draw(uint32_t flags)
menu_item const &pitem = selected_item();
bool const subitem_invert(pitem.flags() & FLAG_INVERT);
auto const linenum = m_selected - top_line;
- float const line_y = visible_top + float(linenum) * line_height();
+ float const line_y = m_items_top + float(linenum) * line_height();
// compute the multi-line target width/height
auto const [target_width, target_height] = get_text_dimensions(
@@ -828,7 +958,7 @@ void menu::draw(uint32_t flags)
}
// if there is something special to add, do it by calling the virtual method
- custom_render(get_selection_ref(), m_customtop, m_custombottom, x1, y1, x2, y2);
+ custom_render(flags, get_selection_ref(), m_customtop, m_custombottom, x1, y1, x2, y2);
}
void menu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
@@ -843,42 +973,15 @@ void menu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
m_lr_arrow_width = 0.4F * m_line_height * aspect;
m_ud_arrow_width = m_line_height * aspect;
-}
-
-void menu::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
-{
-}
+ // don't accept pointer input until the menu has been redrawn
+ m_items_drawn = false;
+ m_pointer_state = track_pointer::IDLE;
-//-------------------------------------------------
-// map_mouse - map mouse pointer location to menu
-// coordinates
-//-------------------------------------------------
-
-void menu::map_mouse()
-{
- ignore_mouse();
- int32_t mouse_target_x, mouse_target_y;
- render_target *const mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &m_mouse_button);
- if (mouse_target)
- {
- if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, container(), m_mouse_x, m_mouse_y))
- m_mouse_hit = true;
- }
}
-
-//-------------------------------------------------
-// ignore_mouse - set members to ignore mouse
-// input
-//-------------------------------------------------
-
-void menu::ignore_mouse()
+void menu::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
- m_mouse_hit = false;
- m_mouse_button = false;
- m_mouse_x = -1.0F;
- m_mouse_y = -1.0F;
}
@@ -887,8 +990,9 @@ void menu::ignore_mouse()
// input events for a menu
//-------------------------------------------------
-void menu::handle_events(uint32_t flags, event &ev)
+bool menu::handle_events(uint32_t flags, event &ev)
{
+ bool need_update = false;
bool stop = false;
ui_event local_menu_event;
@@ -897,119 +1001,93 @@ void menu::handle_events(uint32_t flags, event &ev)
{
switch (local_menu_event.event_type)
{
- // if we are hovering over a valid item, select it with a single click
- case ui_event::type::MOUSE_DOWN:
- if (custom_mouse_down())
- return;
-
- if (!(flags & PROCESS_ONLYCHAR))
+ // deal with pointer-like input (mouse, pen, touch, etc.)
+ case ui_event::type::POINTER_UPDATE:
{
- if (m_hover >= 0 && m_hover < m_items.size())
- {
- m_selected = m_hover;
- }
- else if (m_hover == HOVER_ARROW_UP)
- {
- if (flags & PROCESS_CUSTOM_NAV)
- {
- ev.iptkey = IPT_UI_PAGE_UP;
- stop = true;
- }
- else
- {
- m_selected -= m_visible_items;
- if (m_selected < 0)
- m_selected = 0;
- top_line -= m_visible_items - (last_item_visible() ? 1 : 0);
- }
- }
- else if (m_hover == HOVER_ARROW_DOWN)
- {
- if (flags & PROCESS_CUSTOM_NAV)
- {
- ev.iptkey = IPT_UI_PAGE_DOWN;
- stop = true;
- }
- else
- {
- m_selected += m_visible_lines - 2 + is_first_selected();
- if (m_selected > m_items.size() - 1)
- m_selected = m_items.size() - 1;
- top_line += m_visible_lines - 2;
- }
- }
- else if (m_hover == HOVER_UI_LEFT)
+ auto const [key, redraw] = handle_pointer_update(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_LEFT;
+ ev.iptkey = key;
stop = true;
}
- else if (m_hover == HOVER_UI_RIGHT)
+ }
+ break;
+
+ // pointer left the normal way, possibly releasing buttons
+ case ui_event::type::POINTER_LEAVE:
+ {
+ auto const [key, redraw] = handle_pointer_leave(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_RIGHT;
+ ev.iptkey = key;
stop = true;
}
}
break;
- // if we are hovering over a valid item, fake a UI_SELECT with a double-click
- case ui_event::type::MOUSE_DOUBLE_CLICK:
- if (!(flags & PROCESS_ONLYCHAR) && m_hover >= 0 && m_hover < m_items.size())
+ // pointer left in some abnormal way - cancel any associated actions
+ case ui_event::type::POINTER_ABORT:
{
- m_selected = m_hover;
- ev.iptkey = IPT_UI_SELECT;
- if (is_last_selected() && m_needs_prev_menu_item)
+ auto const [key, redraw] = handle_pointer_abort(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_BACK;
- stack_pop();
- if (is_special_main_menu())
- machine().schedule_exit();
+ ev.iptkey = key;
+ stop = true;
}
- stop = true;
}
break;
// caught scroll event
case ui_event::type::MOUSE_WHEEL:
- if (!custom_mouse_scroll((0 < local_menu_event.zdelta) ? -local_menu_event.num_lines : local_menu_event.num_lines) && !(flags & (PROCESS_ONLYCHAR | PROCESS_CUSTOM_NAV)))
+ if ((track_pointer::IDLE == m_pointer_state) || (track_pointer::IGNORED == m_pointer_state))
{
- if (local_menu_event.zdelta > 0)
+ if (!custom_mouse_scroll((0 < local_menu_event.zdelta) ? -local_menu_event.num_lines : local_menu_event.num_lines) && !(flags & (PROCESS_ONLYCHAR | PROCESS_CUSTOM_NAV)))
{
- if (is_first_selected())
+ if (local_menu_event.zdelta > 0)
{
- select_last_item();
+ if (is_first_selected())
+ {
+ select_last_item();
+ }
+ else
+ {
+ m_selected -= local_menu_event.num_lines;
+ validate_selection(-1);
+ }
+ top_line -= (m_selected <= top_line && top_line != 0);
+ if (m_selected <= top_line && m_visible_items != m_visible_lines)
+ top_line -= local_menu_event.num_lines;
}
else
{
- m_selected -= local_menu_event.num_lines;
- validate_selection(-1);
+ if (is_last_selected())
+ {
+ select_first_item();
+ }
+ else
+ {
+ m_selected += local_menu_event.num_lines;
+ validate_selection(1);
+ }
+ top_line += (m_selected >= top_line + m_visible_items + (top_line != 0));
+ if (m_selected >= (top_line + m_visible_items + (top_line != 0)))
+ top_line += local_menu_event.num_lines;
}
- top_line -= (m_selected <= top_line && top_line != 0);
- if (m_selected <= top_line && m_visible_items != m_visible_lines)
- top_line -= local_menu_event.num_lines;
- }
- else
- {
- if (is_last_selected())
- {
- select_first_item();
- }
- else
- {
- m_selected += local_menu_event.num_lines;
- validate_selection(1);
- }
- top_line += (m_selected >= top_line + m_visible_items + (top_line != 0));
- if (m_selected >= (top_line + m_visible_items + (top_line != 0)))
- top_line += local_menu_event.num_lines;
}
}
break;
// translate CHAR events into specials
case ui_event::type::IME_CHAR:
- ev.iptkey = IPT_SPECIAL;
- ev.unichar = local_menu_event.ch;
- stop = true;
+ if ((track_pointer::IDLE == m_pointer_state) || (track_pointer::IGNORED == m_pointer_state))
+ {
+ ev.iptkey = IPT_SPECIAL;
+ ev.unichar = local_menu_event.ch;
+ stop = true;
+ }
break;
// ignore everything else
@@ -1017,6 +1095,536 @@ void menu::handle_events(uint32_t flags, event &ev)
break;
}
}
+
+ // deal with repeating scroll arrows
+ if ((track_pointer::TRACK_LINE == m_pointer_state) && ((!m_pointer_line && m_show_up_arrow) || ((m_pointer_line == (m_visible_lines - 1)) && m_show_down_arrow)))
+ {
+ float const linetop(m_items_top + (float(m_pointer_line) * line_height()));
+ float const linebottom(m_items_top + (float(m_pointer_line + 1) * line_height()));
+ if (pointer_in_rect(m_items_left, linetop, m_items_right, linebottom))
+ {
+ if (std::chrono::steady_clock::now() >= m_pointer_repeat)
+ {
+ if (!m_pointer_line)
+ {
+ // scroll up
+ assert(0 < top_line);
+ --top_line;
+ if (!top_line)
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ else
+ {
+ // scroll down
+ assert(m_items.size() > (top_line + m_visible_lines));
+ ++top_line;
+ if (m_items.size() == (top_line + m_visible_lines))
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ force_visible_selection();
+ need_update = true;
+ m_pointer_repeat += std::chrono::milliseconds(100);
+ }
+ }
+ }
+
+ return need_update;
+}
+
+
+//-------------------------------------------------
+// handle_pointer_update - handle a regular
+// pointer update
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_pointer_update(uint32_t flags, ui_event const &uievt)
+{
+ // decide whether to make this our current pointer
+ render_target &target(machine().render().ui_target());
+ auto const [ours, changed] = m_global_state.use_pointer(target, container(), uievt);
+ if (!ours)
+ {
+ return std::make_pair(IPT_INVALID, false);
+ }
+ else if (changed)
+ {
+ // if the active pointer changed, ignore if any buttons were already down
+ if (uievt.pointer_buttons != uievt.pointer_pressed)
+ {
+ m_pointer_state = track_pointer::IGNORED;
+ return std::make_pair(IPT_INVALID, false);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::IDLE;
+ }
+ }
+ else if ((track_pointer::IGNORED == m_pointer_state) || (track_pointer::COMPLETED == m_pointer_state))
+ {
+ // stop ignoring the pointer if all buttons were released
+ if (uievt.pointer_buttons == uievt.pointer_pressed)
+ m_pointer_state = track_pointer::IDLE;
+ else
+ return std::make_pair(IPT_INVALID, false);
+ }
+
+ // give derived class a chance to handle it
+ if ((track_pointer::IDLE == m_pointer_state) || (track_pointer::CUSTOM == m_pointer_state))
+ {
+ auto const [key, take, redraw] = custom_pointer_updated(changed, uievt);
+ if (take)
+ {
+ m_pointer_state = track_pointer::CUSTOM;
+ return std::make_pair(key, redraw);
+ }
+ else if (track_pointer::CUSTOM == m_pointer_state)
+ {
+ if (uievt.pointer_buttons)
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ return std::make_pair(key, redraw);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::IDLE;
+ }
+ }
+
+ if (IPT_INVALID != key)
+ return std::make_pair(key, redraw);
+ }
+
+ // ignore altogether if menu hasn't been drawn or flags say so
+ if (!m_items_drawn || (flags & (PROCESS_CUSTOM_ONLY | PROCESS_ONLYCHAR)))
+ {
+ if (uievt.pointer_pressed)
+ {
+ if (track_pointer::IDLE == m_pointer_state)
+ m_pointer_state = track_pointer::IGNORED;
+ }
+ else if (!uievt.pointer_buttons)
+ {
+ if ((track_pointer::IGNORED == m_pointer_state) || (track_pointer::COMPLETED == m_pointer_state))
+ m_pointer_state = track_pointer::IDLE;
+ }
+ return std::make_pair(IPT_INVALID, false);
+ }
+
+ switch (m_pointer_state)
+ {
+ case track_pointer::IDLE:
+ // ignore anything other than left click for now
+ if ((uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x1)))
+ return handle_primary_down(flags, uievt);
+ else if (uievt.pointer_pressed)
+ m_pointer_state = track_pointer::IGNORED;
+ break;
+
+ case track_pointer::IGNORED:
+ case track_pointer::COMPLETED:
+ case track_pointer::CUSTOM:
+ std::abort(); // won't get here - handled earlier
+
+ case track_pointer::TRACK_LINE:
+ {
+ auto const result(update_line_click(uievt));
+
+ // treat anything else being pressed as cancelling the click sequence
+ if (uievt.pointer_buttons & ~u32(0x01))
+ m_pointer_state = track_pointer::COMPLETED;
+ else if (!uievt.pointer_buttons)
+ m_pointer_state = track_pointer::IDLE;
+
+ return result;
+ }
+
+ case track_pointer::SCROLL:
+ {
+ bool const redraw(update_drag_scroll(uievt));
+
+ // treat anything else being pressed as cancelling the drag
+ if (uievt.pointer_buttons & ~u32(0x01))
+ m_pointer_state = track_pointer::COMPLETED;
+ else if (!uievt.pointer_buttons)
+ m_pointer_state = track_pointer::IDLE;
+
+ return std::make_pair(IPT_INVALID, redraw);
+ }
+
+ case track_pointer::ADJUST:
+ {
+ auto const result(update_drag_adjust(uievt));
+
+ // treat anything else being pressed as cancelling the drag
+ if (uievt.pointer_buttons & ~u32(0x01))
+ m_pointer_state = track_pointer::COMPLETED;
+ else if (!uievt.pointer_buttons)
+ m_pointer_state = track_pointer::IDLE;
+
+ return result;
+ }
+ }
+
+ return std::make_pair(IPT_INVALID, false);
+}
+
+
+//-------------------------------------------------
+// handle_pointer_leave - handle a pointer
+// leaving the window
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_pointer_leave(uint32_t flags, ui_event const &uievt)
+{
+ // ignore pointer input in windows other than the one that displays the UI
+ render_target &target(machine().render().ui_target());
+ auto const [ours, changed] = m_global_state.use_pointer(target, container(), uievt);
+ assert(!changed);
+ if (!ours)
+ return std::make_pair(IPT_INVALID, false);
+
+ int key(IPT_INVALID);
+ bool redraw(false);
+ switch (m_pointer_state)
+ {
+ case track_pointer::IDLE:
+ case track_pointer::CUSTOM:
+ std::tie(key, std::ignore, redraw) = custom_pointer_updated(changed, uievt);
+ break;
+
+ case track_pointer::IGNORED:
+ case track_pointer::COMPLETED:
+ break; // nothing to do
+
+ case track_pointer::TRACK_LINE:
+ std::tie(key, redraw) = update_line_click(uievt);
+ break;
+
+ case track_pointer::SCROLL:
+ redraw = update_drag_scroll(uievt);
+ break;
+
+ case track_pointer::ADJUST:
+ std::tie(key, redraw) = update_drag_adjust(uievt);
+ break;
+ }
+
+ m_pointer_state = track_pointer::IDLE;
+ return std::make_pair(key, redraw);
+}
+
+
+//-------------------------------------------------
+// handle_pointer_abort - handle a pointer
+// leaving in an abnormal way
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_pointer_abort(uint32_t flags, ui_event const &uievt)
+{
+ // ignore pointer input in windows other than the one that displays the UI
+ render_target &target(machine().render().ui_target());
+ auto const [ours, changed] = m_global_state.use_pointer(target, container(), uievt);
+ assert(!changed);
+ if (!ours)
+ return std::make_pair(IPT_INVALID, false);
+
+ int key(IPT_INVALID);
+ bool redraw(false);
+ if (track_pointer::CUSTOM == m_pointer_state)
+ std::tie(key, std::ignore, redraw) = custom_pointer_updated(false, uievt);
+ else if (track_pointer::TRACK_LINE == m_pointer_state)
+ redraw = true;
+ m_pointer_state = track_pointer::IDLE;
+ return std::make_pair(key, redraw);
+}
+
+
+//-------------------------------------------------
+// handle_primary_down - handle the primary
+// action for a pointer device
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_primary_down(uint32_t flags, ui_event const &uievt)
+{
+ // we handle touch differently to mouse or pen
+ bool const is_touch(ui_event::pointer::TOUCH == uievt.pointer_type);
+ auto const [x, y] = pointer_location(); // FIXME: need starting location for multi-click actions
+
+ // check increase/decrease arrows first
+ // FIXME: should repeat if appropriate
+ if (!is_touch && (y >= m_adjust_top) && (y < m_adjust_bottom))
+ {
+ if ((x >= m_decrease_left) && (x < (m_decrease_left + lr_arrow_width())))
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ return std::make_pair(IPT_UI_LEFT, false);
+ }
+ else if ((x >= m_increase_left) && (x < (m_increase_left + lr_arrow_width())))
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ return std::make_pair(IPT_UI_RIGHT, false);
+ }
+ }
+
+ // work out if we’re pointing at an item
+ if ((x < m_items_left) || (x >= m_items_right) || (y < m_items_top) || (y >= (m_items_top + (float(m_visible_lines) * line_height()))))
+ {
+ m_pointer_state = track_pointer::IGNORED;
+ return std::make_pair(IPT_INVALID, false);
+ }
+ auto const lineno(int((y - m_items_top) / line_height()));
+ assert(lineno >= 0);
+ assert(lineno < m_visible_lines);
+
+ // map to an action
+ if (!lineno && m_show_up_arrow)
+ {
+ // scroll up
+ assert(0 < top_line);
+ --top_line;
+ force_visible_selection();
+ if (top_line)
+ {
+ m_pointer_state = track_pointer::TRACK_LINE;
+ m_pointer_down = std::make_pair(x, y);
+ m_pointer_updated = m_pointer_down;
+ m_pointer_line = lineno;
+ m_pointer_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ return std::make_pair(IPT_INVALID, true);
+ }
+ else if ((lineno == (m_visible_lines - 1)) && m_show_down_arrow)
+ {
+ // scroll down
+ assert(m_items.size() > (top_line + m_visible_lines));
+ ++top_line;
+ force_visible_selection();
+ if (m_items.size() > (top_line + m_visible_lines))
+ {
+ m_pointer_state = track_pointer::TRACK_LINE;
+ m_pointer_down = std::make_pair(x, y);
+ m_pointer_updated = m_pointer_down;
+ m_pointer_line = lineno;
+ m_pointer_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ return std::make_pair(IPT_INVALID, true);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::TRACK_LINE;
+ m_pointer_down = std::make_pair(x, y);
+ m_pointer_updated = m_pointer_down;
+ m_pointer_line = lineno;
+
+ int const itemno(lineno + top_line);
+ assert(itemno >= 0);
+ assert(itemno < m_items.size());
+ return std::make_pair(IPT_INVALID, is_selectable(m_items[itemno]));
+ }
+
+ // nothing to do
+ m_pointer_state = track_pointer::IGNORED;
+ return std::make_pair(IPT_INVALID, false);
+}
+
+
+//-------------------------------------------------
+// update_line_click - track pointer after
+// clicking a menu line
+//-------------------------------------------------
+
+std::pair<int, bool> menu::update_line_click(ui_event const &uievt)
+{
+ assert(track_pointer::TRACK_LINE == m_pointer_state);
+ assert((uievt.pointer_buttons | uievt.pointer_released) & 0x01);
+
+ // arrows should scroll while held
+ if ((!m_pointer_line && m_show_up_arrow) || ((m_pointer_line == (m_visible_lines - 1)) && m_show_down_arrow))
+ {
+ // check for re-entry
+ bool redraw(false);
+ auto const [x, y] = pointer_location();
+ float const linetop(m_items_top + (float(m_pointer_line) * line_height()));
+ float const linebottom(m_items_top + (float(m_pointer_line + 1) * line_height()));
+ bool const reentered(reentered_rect(m_pointer_updated.first, m_pointer_updated.second, x, y, m_items_left, linetop, m_items_right, linebottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (now >= m_pointer_repeat)
+ {
+ if (!m_pointer_line)
+ {
+ // scroll up
+ assert(0 < top_line);
+ --top_line;
+ if (!top_line)
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ else
+ {
+ // scroll down
+ assert(m_items.size() > (top_line + m_visible_lines));
+ ++top_line;
+ if (m_items.size() == (top_line + m_visible_lines))
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ force_visible_selection();
+ redraw = true;
+ m_pointer_repeat = now + std::chrono::milliseconds(100);
+ }
+ }
+
+ // keep the pointer location where we updated
+ m_pointer_updated = std::make_pair(x, y);
+ return std::make_pair(IPT_INVALID, redraw);
+ }
+
+ // check for conversion of a tap to a finger drag
+ auto const drag_result(check_touch_drag(uievt));
+ if (track_pointer::TRACK_LINE != m_pointer_state)
+ return drag_result;
+
+ // only take action if the primary button was released
+ if (!(uievt.pointer_released & 0x01))
+ return std::make_pair(IPT_INVALID, false);
+
+ // nothing to do if the item isn't selectable
+ int const itemno = m_pointer_line + top_line;
+ assert(itemno >= 0);
+ assert(itemno < m_items.size());
+ if (!is_selectable(m_items[itemno]))
+ return std::make_pair(IPT_INVALID, false);
+
+ // treat multi-click actions as not moving, otherwise check that pointer is still over the line
+ if (0 >= uievt.pointer_clicks)
+ {
+ auto const [x, y] = pointer_location();
+ if ((x < m_items_left) || (x >= m_items_right) || (int((y - m_items_top) / line_height()) != m_pointer_line))
+ return std::make_pair(IPT_INVALID, true);
+ }
+
+ // anything other than a double-click just selects the item
+ m_selected = itemno;
+ if (2 != uievt.pointer_clicks)
+ return std::make_pair(IPT_INVALID, true);
+
+ // activate regular items by simulating UI Select
+ if (!is_last_selected() || !m_needs_prev_menu_item)
+ return std::make_pair(IPT_UI_SELECT, true);
+
+ // handle the magic final item that dismisses the menu
+ stack_pop();
+ if (is_special_main_menu())
+ machine().schedule_exit();
+ return std::make_pair(IPT_UI_BACK, true);
+}
+
+
+//-------------------------------------------------
+// update_drag_scroll - update menu position in
+// response to a touch drag
+//-------------------------------------------------
+
+bool menu::update_drag_scroll(ui_event const &uievt)
+{
+ assert(track_pointer::SCROLL == m_pointer_state);
+ assert((uievt.pointer_buttons | uievt.pointer_released) & 0x01);
+
+ // get target location
+ int const newtop(drag_scroll(
+ pointer_location().second, m_pointer_down.second, m_pointer_updated.second, -line_height(),
+ m_pointer_line, 0, int(m_items.size() - m_visible_lines)));
+ if (newtop == top_line)
+ return false;
+
+ // scroll and move the selection if necessary to keep it in the visible range
+ top_line = newtop;
+ force_visible_selection();
+ return true;
+}
+
+
+//-------------------------------------------------
+// update_drag_adjust - adjust value on
+// horizontal drag
+//-------------------------------------------------
+
+std::pair<int, bool> menu::update_drag_adjust(ui_event const &uievt)
+{
+ assert(track_pointer::ADJUST == m_pointer_state);
+ assert((uievt.pointer_buttons | uievt.pointer_released) & 0x01);
+
+ // this is ugly because adjustment is implemented by faking keystrokes - can't give a count/distance
+
+ // set thresholds depending on the direction for hysteresis
+ int const target(drag_scroll(
+ pointer_location().first, m_pointer_updated.first, m_pointer_updated.first, line_height() * x_aspect(),
+ 0, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
+
+ // ensure the item under the pointer is selected and adjustable
+ if ((top_line + m_pointer_line) == m_selected)
+ {
+ if (0 < target)
+ {
+ if (m_items[m_selected].flags() & FLAG_RIGHT_ARROW)
+ return std::make_pair(IPT_UI_RIGHT, true);
+ }
+ else if (0 > target)
+ {
+ if (m_items[m_selected].flags() & FLAG_LEFT_ARROW)
+ return std::make_pair(IPT_UI_LEFT, true);
+ }
+ }
+
+ // looks like it wasn't to be
+ return std::make_pair(IPT_INVALID, false);
+}
+
+
+//-------------------------------------------------
+// check_touch_drag - check for conversion of a
+// touch to a scroll or adjust slide
+//-------------------------------------------------
+
+std::pair<int, bool> menu::check_touch_drag(ui_event const &uievt)
+{
+ // we handle touch differently to mouse or pen
+ if (ui_event::pointer::TOUCH != uievt.pointer_type)
+ return std::make_pair(IPT_INVALID, false);
+
+ // check distances
+ auto const [x, y] = pointer_location();
+ auto const [h, v] = check_drag_conversion(x, y, m_pointer_down.first, m_pointer_down.second, line_height());
+ if (h)
+ {
+ // only the selected line can be adjusted
+ if ((top_line + m_pointer_line) == m_selected)
+ {
+ m_pointer_state = track_pointer::ADJUST;
+ return update_drag_adjust(uievt);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ }
+ else if (v)
+ {
+ m_pointer_state = track_pointer::SCROLL;
+ m_pointer_line = top_line;
+ return std::make_pair(IPT_INVALID, update_drag_scroll(uievt));
+ }
+
+ // no update needed
+ return std::make_pair(IPT_INVALID, false);
}
@@ -1025,13 +1633,13 @@ void menu::handle_events(uint32_t flags, event &ev)
// keys for a menu
//-------------------------------------------------
-void menu::handle_keys(uint32_t flags, int &iptkey)
+bool menu::handle_keys(uint32_t flags, int &iptkey)
{
- bool const ignorepause = (flags & PROCESS_IGNOREPAUSE) || stack_has_special_main_menu();
-
- // bail if no items
+ // bail if no items (happens if event handling triggered an item reset)
if (m_items.empty())
- return;
+ return false;
+
+ bool const ignorepause = (flags & PROCESS_IGNOREPAUSE) || stack_has_special_main_menu();
// if we hit select, return true or pop the stack, depending on the item
if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0))
@@ -1043,7 +1651,7 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
if (is_special_main_menu())
machine().schedule_exit();
}
- return;
+ return false;
}
// UI configure hides the menus
@@ -1053,12 +1661,12 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
stack_pop();
else
m_global_state.hide_menu();
- return;
+ return true;
}
// bail out
if (flags & PROCESS_ONLYCHAR)
- return;
+ return false;
// hitting back also pops the stack
if (exclusive_input_pressed(iptkey, IPT_UI_BACK, 0))
@@ -1070,7 +1678,7 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
if (is_special_main_menu())
machine().schedule_exit();
}
- return;
+ return false;
}
// validate the current selection
@@ -1082,16 +1690,19 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// accept left/right/prev/next keys as-is with repeat if appropriate
if (!ignoreleft && exclusive_input_pressed(iptkey, IPT_UI_LEFT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
- return;
+ return false;
if (!ignoreright && exclusive_input_pressed(iptkey, IPT_UI_RIGHT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
- return;
+ return false;
+
+ // keep track of whether we changed anything
+ bool updated(false);
// up backs up by one item
if (exclusive_input_pressed(iptkey, IPT_UI_UP, 6))
{
if (flags & PROCESS_CUSTOM_NAV)
{
- return;
+ return updated;
}
else if (is_first_selected())
{
@@ -1105,6 +1716,7 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
top_line -= (m_selected <= top_line && top_line != 0);
if (m_selected <= top_line && m_visible_items != m_visible_lines)
top_line--;
+ updated = true;
}
// down advances by one item
@@ -1112,7 +1724,7 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
{
if (flags & PROCESS_CUSTOM_NAV)
{
- return;
+ return updated;
}
else if (is_last_selected())
{
@@ -1126,47 +1738,52 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
top_line += (m_selected >= top_line + m_visible_items + (top_line != 0));
if (m_selected >= (top_line + m_visible_items + (top_line != 0)))
top_line++;
+ updated = true;
}
// page up backs up by m_visible_items
if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_UP, 6))
{
if (flags & PROCESS_CUSTOM_NAV)
- return;
+ return updated;
m_selected -= m_visible_items;
top_line -= m_visible_items - (last_item_visible() ? 1 : 0);
if (m_selected < 0)
m_selected = 0;
validate_selection(1);
+ updated = true;
}
// page down advances by m_visible_items
if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_DOWN, 6))
{
if (flags & PROCESS_CUSTOM_NAV)
- return;
+ return updated;
m_selected += m_visible_lines - 2 + is_first_selected();
top_line += m_visible_lines - 2;
if (m_selected > m_items.size() - 1)
m_selected = m_items.size() - 1;
validate_selection(-1);
+ updated = true;
}
// home goes to the start
if (exclusive_input_pressed(iptkey, IPT_UI_HOME, 0))
{
if (flags & PROCESS_CUSTOM_NAV)
- return;
+ return updated;
select_first_item();
+ updated = true;
}
// end goes to the last
if (exclusive_input_pressed(iptkey, IPT_UI_END, 0))
{
if (flags & PROCESS_CUSTOM_NAV)
- return;
+ return updated;
select_last_item();
+ updated = true;
}
// pause enables/disables pause
@@ -1202,6 +1819,39 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
break;
}
}
+ return updated;
+}
+
+
+//-------------------------------------------------
+// default handler implementations
+//-------------------------------------------------
+
+bool menu::custom_ui_back()
+{
+ return false;
+}
+
+std::tuple<int, bool, bool> menu::custom_pointer_updated(bool changed, ui_event const &uievt)
+{
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+bool menu::custom_mouse_scroll(int lines)
+{
+ return false;
+}
+
+void menu::menu_activated()
+{
+}
+
+void menu::menu_deactivated()
+{
+}
+
+void menu::menu_dismissed()
+{
}
@@ -1249,6 +1899,85 @@ void menu::validate_selection(int scandir)
}
+//-------------------------------------------------
+// check_metrics - recompute metrics if target
+// geometry has changed
+//-------------------------------------------------
+
+bool menu::check_metrics()
+{
+ render_manager &render(machine().render());
+ render_target &target(render.ui_target());
+ std::pair<uint32_t, uint32_t> const uisize(target.width(), target.height());
+ float const aspect = render.ui_aspect(&container());
+ if ((uisize == m_last_size) && (aspect == m_last_aspect))
+ return false;
+
+ m_last_size = uisize;
+ m_last_aspect = aspect;
+ recompute_metrics(uisize.first, uisize.second, aspect);
+ return true;
+}
+
+
+//-------------------------------------------------
+// do_rebuild - get the subclass to populate
+// the menu items
+//-------------------------------------------------
+
+bool menu::do_rebuild()
+{
+ if (!m_items.empty())
+ return false;
+
+ m_rebuilding = true;
+ try
+ {
+ // add an item to return - this is a really hacky way of doing this
+ if (m_needs_prev_menu_item)
+ item_append(_("Return to Previous Menu"), 0, nullptr);
+
+ // let implementation add other items
+ populate();
+ }
+ catch (...)
+ {
+ m_items.clear();
+ m_rebuilding = false;
+ throw;
+ }
+ m_rebuilding = false;
+ return true;
+}
+
+
+//-------------------------------------------------
+// force_visible_selection - if the selected item
+// is not visible, move the selection it it's
+// within the visible portion of the menu
+//-------------------------------------------------
+
+void menu::force_visible_selection()
+{
+ int const first(top_line ? (top_line + 1) : 0);
+ int const last(top_line + m_visible_lines - ((m_items.size() > (top_line + m_visible_lines)) ? 1 : 0));
+ if (first > m_selected)
+ {
+ m_selected = first;
+ while (!is_selectable(m_items[m_selected]))
+ ++m_selected;
+ assert(last > m_selected);
+ }
+ else if (last <= m_selected)
+ {
+ m_selected = last - 1;
+ while (!is_selectable(m_items[m_selected]))
+ --m_selected;
+ assert(first <= m_selected);
+ }
+}
+
+
/***************************************************************************
MENU STACK MANAGEMENT
@@ -1262,43 +1991,74 @@ bool menu::do_handle()
machine().osd().check_osd_inputs();
// recompute metrics if necessary
- render_manager &render(machine().render());
- render_target &target(render.ui_target());
- std::pair<uint32_t, uint32_t> const uisize(target.width(), target.height());
- float const aspect = render.ui_aspect(&container());
- if ((uisize != m_last_size) || (aspect != m_last_aspect))
- {
- m_last_size = uisize;
- m_last_aspect = aspect;
- recompute_metrics(uisize.first, uisize.second, aspect);
+ if (check_metrics())
need_update = true;
- }
- if (m_items.empty())
- {
- m_rebuilding = true;
- try
- {
- // add an item to return - this is a really hacky way of doing this
- if (m_needs_prev_menu_item)
- item_append(_("Return to Previous Menu"), 0, nullptr);
+ // get the implementation to rebuild the list of items if necessary
+ if (do_rebuild())
+ need_update = true;
+ validate_selection(1);
- // let implementation add other items
- populate();
+ // reset the event
+ std::optional<event> result;
+ result.emplace();
+ result->itemref = nullptr;
+ result->item = nullptr;
+ result->iptkey = IPT_INVALID;
+
+ // process input
+ uint32_t flags(m_process_flags);
+ if (!(flags & (PROCESS_NOKEYS | PROCESS_NOINPUT)))
+ {
+ // read events
+ if (handle_events(flags, *result))
need_update = true;
- }
- catch (...)
+
+ switch (m_pointer_state)
{
- m_items.clear();
- m_rebuilding = false;
- throw;
+ case track_pointer::IDLE:
+ case track_pointer::IGNORED:
+ // handle keys if we don't already have an event and we aren't tracking a pointer action
+ if ((IPT_INVALID == result->iptkey) && handle_keys(flags, result->iptkey))
+ need_update = true;
+ break;
+ default:
+ // ignore keys pressed while tracking a pointer action
+ for (int code = IPT_UI_FIRST + 1; IPT_UI_LAST > code; ++code)
+ machine().ui_input().pressed(code);
+ break;
}
- m_rebuilding = false;
}
- auto [eventptr, changed] = process();
- need_update = need_update || changed;
- need_update = handle(eventptr) || need_update;
+ // deal with stack push/pop and rebuild
+ if (!is_active())
+ return false;
+ if (do_rebuild())
+ {
+ validate_selection(1);
+ need_update = true;
+ }
+
+ // update the selected item in the event and let the implementation handle it
+ if ((result->iptkey != IPT_INVALID) && selection_valid())
+ {
+ result->itemref = get_selection_ref();
+ result->item = &m_items[m_selected];
+ }
+ else
+ {
+ result.reset();
+ }
+ need_update = handle(result ? &*result : nullptr) || need_update;
+
+ // the implementation had another chance to push/pop or rebuild
+ if (!is_active())
+ return false;
+ if (do_rebuild())
+ {
+ validate_selection(1);
+ return true;
+ }
return need_update;
}
@@ -1328,7 +2088,7 @@ delegate<uint32_t (render_container &)> menu::get_ui_handler(mame_ui_manager &mu
text_layout menu::create_layout(float width, text_layout::text_justify justify, text_layout::word_wrapping wrap)
{
- return text_layout(*ui().get_font(), line_height() * m_last_aspect, line_height(), width, justify, wrap);
+ return text_layout(*ui().get_font(), line_height() * x_aspect(), line_height(), width, justify, wrap);
}
diff --git a/src/frontend/mame/ui/menu.h b/src/frontend/mame/ui/menu.h
index d383e31ab52..791432482b5 100644
--- a/src/frontend/mame/ui/menu.h
+++ b/src/frontend/mame/ui/menu.h
@@ -19,16 +19,29 @@
#include "language.h"
#include "render.h"
+#include "interface/uievents.h"
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string_view>
+#include <tuple>
#include <utility>
#include <vector>
+/***************************************************************************
+ FORWARD DECLARATIONS
+***************************************************************************/
+
+struct ui_event;
+
+
namespace ui {
/***************************************************************************
@@ -132,11 +145,10 @@ protected:
// menu-related events
struct event
{
- void *itemref; // reference for the selected item or nullptr
- menu_item *item; // selected item or nullptr
- int iptkey; // one of the IPT_* values from inptport.h
- char32_t unichar; // unicode character if iptkey == IPT_SPECIAL
- render_bounds mouse; // mouse position if iptkey == IPT_CUSTOM
+ void *itemref; // reference for the selected item or nullptr
+ menu_item *item; // selected item or nullptr
+ int iptkey; // one of the IPT_* values from inpttype.h
+ char32_t unichar; // unicode character if iptkey == IPT_SPECIAL
};
menu(mame_ui_manager &mui, render_container &container);
@@ -181,10 +193,6 @@ protected:
void select_first_item();
void select_last_item();
- int hover() const { return m_hover; }
- void set_hover(int index) { m_hover = index; }
- void clear_hover() { m_hover = m_items.size() + 1; }
-
// scroll position control
void set_top_line(int index) { top_line = (0 < index) ? (index - 1) : index; }
void centre_selection() { top_line = m_selected - (m_visible_lines / 2); }
@@ -196,6 +204,8 @@ protected:
float get_customtop() const { return m_customtop; }
float get_custombottom() const { return m_custombottom; }
+ std::pair<uint32_t, uint32_t> target_size() const { return m_last_size; }
+ float x_aspect() const { return m_last_aspect; }
float line_height() const { return m_line_height; }
float gutter_width() const { return m_gutter_width; }
float tb_border() const { return m_tb_border; }
@@ -281,7 +291,7 @@ protected:
std::string_view const &line(*it);
if (!line.empty())
{
- text_layout layout(*ui().get_font(), text_size * m_last_aspect, text_size, 1.0, justify, wrap);
+ text_layout layout(*ui().get_font(), text_size * x_aspect(), text_size, 1.0, justify, wrap);
layout.add_text(line, rgb_t::white(), rgb_t::black());
maxwidth = (std::max)(layout.actual_width(), maxwidth);
}
@@ -328,36 +338,32 @@ protected:
// draw additional menu content
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect);
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2);
-
- // map mouse to menu coordinates
- void map_mouse();
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2);
- // clear the mouse position
- void ignore_mouse();
+ // access to pointer state
+ bool have_pointer() const noexcept { return m_global_state.have_pointer(); }
+ bool pointer_idle() const noexcept { return (track_pointer::IDLE == m_pointer_state) && have_pointer(); }
+ bool pointer_in_rect(float x0, float y0, float x1, float y1) const noexcept { return m_global_state.pointer_in_rect(x0, y0, x1, y1); }
+ std::pair<float, float> pointer_location() const noexcept { return m_global_state.pointer_location(); }
+ osd::ui_event_handler::pointer pointer_type() const noexcept { return m_global_state.pointer_type(); }
- bool is_mouse_hit() const { return m_mouse_hit; } // is mouse pointer inside menu's render container?
- float get_mouse_x() const { return m_mouse_x; } // mouse x location in menu coordinates
- float get_mouse_y() const { return m_mouse_y; } // mouse y location in menu coordinates
-
- // mouse hit test - checks whether mouse_x is in [x0, x1) and mouse_y is in [y0, y1)
- bool mouse_in_rect(float x0, float y0, float x1, float y1) const
- {
- return m_mouse_hit && (m_mouse_x >= x0) && (m_mouse_x < x1) && (m_mouse_y >= y0) && (m_mouse_y < y1);
- }
+ // derived classes that override handle_events need to call these for pointer events
+ std::pair<int, bool> handle_pointer_update(uint32_t flags, ui_event const &uievt);
+ std::pair<int, bool> handle_pointer_leave(uint32_t flags, ui_event const &uievt);
+ std::pair<int, bool> handle_pointer_abort(uint32_t flags, ui_event const &uievt);
// overridable event handling
void set_process_flags(uint32_t flags) { m_process_flags = flags; }
- virtual void handle_events(uint32_t flags, event &ev);
- virtual void handle_keys(uint32_t flags, int &iptkey);
- virtual bool custom_ui_back() { return false; }
- virtual bool custom_mouse_down() { return false; }
- virtual bool custom_mouse_scroll(int lines) { return false; }
+ virtual bool handle_events(uint32_t flags, event &ev);
+ virtual bool handle_keys(uint32_t flags, int &iptkey);
+ virtual bool custom_ui_back();
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt);
+ virtual bool custom_mouse_scroll(int lines);
// event notifications
- virtual void menu_activated() { }
- virtual void menu_deactivated() { }
- virtual void menu_dismissed() { }
+ virtual void menu_activated();
+ virtual void menu_deactivated();
+ virtual void menu_dismissed();
static bool is_selectable(menu_item const &item)
{
@@ -371,7 +377,47 @@ protected:
return ((actual > min) ? FLAG_LEFT_ARROW : 0) | ((actual < max) ? FLAG_RIGHT_ARROW : 0);
}
+ static bool reentered_rect(float x0, float y0, float x1, float y1, float l, float t, float r, float b)
+ {
+ return
+ ((x0 < l) || (x0 >= r) || (y0 < t) || (y0 >= b)) &&
+ ((x1 >= l) && (x1 < r) && (y1 >= t) && (y1 < b));
+ }
+
+ std::pair<bool, bool> check_drag_conversion(float x, float y, float x_base, float y_base, float threshold) const
+ {
+ float const dx(std::abs((x - x_base) / x_aspect()));
+ float const dy(std::abs(y - y_base));
+ if ((dx > dy) && (dx >= threshold))
+ return std::make_pair(true, false);
+ else if ((dy >= dx) && (dy > threshold))
+ return std::make_pair(false, true);
+ else
+ return std::make_pair(false, false);
+ }
+
+ template <typename T>
+ static T drag_scroll(float location, float base, float &last, float unit, T start, T min, T max)
+ {
+ // set thresholds depending on the direction for hysteresis and clamp to valid range
+ T const target((location - (std::abs(unit) * ((location > last) ? 0.3F : -0.3F)) - base) / unit);
+ last = base + (float(target) * unit);
+ return std::clamp(start + target, min, max);
+ }
+
private:
+ // pointer tracking state
+ enum class track_pointer
+ {
+ IDLE,
+ IGNORED,
+ COMPLETED,
+ CUSTOM,
+ TRACK_LINE,
+ SCROLL,
+ ADJUST
+ };
+
class global_state : public widgets_manager
{
public:
@@ -396,17 +442,43 @@ private:
uint32_t ui_handler(render_container &container);
+ bool have_pointer() const noexcept
+ {
+ return 0 <= m_current_pointer;
+ }
+ bool pointer_in_rect(float x0, float y0, float x1, float y1) const noexcept
+ {
+ return (m_pointer_x >= x0) && (m_pointer_x < x1) && (m_pointer_y >= y0) && (m_pointer_y < y1);
+ }
+ std::pair<float, float> pointer_location() const noexcept
+ {
+ return std::make_pair(m_pointer_x, m_pointer_y);
+ }
+ osd::ui_event_handler::pointer pointer_type() const noexcept
+ {
+ return m_pointer_type;
+ }
+
+ std::pair<bool, bool> use_pointer(render_target &target, render_container &container, ui_event const &event);
+
protected:
- mame_ui_manager &m_ui;
+ mame_ui_manager &m_ui;
private:
- bitmap_ptr m_bgrnd_bitmap;
- texture_ptr m_bgrnd_texture;
+ bitmap_ptr m_bgrnd_bitmap;
+ texture_ptr m_bgrnd_texture;
- std::unique_ptr<menu> m_stack;
- std::unique_ptr<menu> m_free;
+ std::unique_ptr<menu> m_stack;
+ std::unique_ptr<menu> m_free;
- bool m_hide;
+ bool m_hide;
+
+ s32 m_current_pointer; // current active pointer ID or -1 if none
+ osd::ui_event_handler::pointer m_pointer_type; // current pointer type
+ u32 m_pointer_buttons; // depressed buttons for current pointer
+ float m_pointer_x;
+ float m_pointer_y;
+ bool m_pointer_hit;
};
// this is to satisfy the std::any requirement that objects be copyable
@@ -417,8 +489,15 @@ private:
global_state_wrapper(global_state_wrapper const &that) : global_state(that.m_ui) { }
};
- // process a menu, drawing it and returning any interesting events
- std::pair<const event *, bool> process();
+ // process a menu, returning any interesting events
+ std::pair<int, bool> handle_primary_down(uint32_t flags, ui_event const &uievt);
+ std::pair<int, bool> update_line_click(ui_event const &uievt);
+ bool update_drag_scroll(ui_event const &uievt);
+ std::pair<int, bool> update_drag_adjust(ui_event const &uievt);
+ std::pair<int, bool> check_touch_drag(ui_event const &uievt);
+
+ // drawing the menu
+ void do_draw_menu();
virtual void draw(uint32_t flags);
// request the specific handling of the game selection main menu
@@ -432,8 +511,11 @@ private:
void extra_text_draw_box(float origx1, float origx2, float origy, float yspan, std::string_view text, int direction);
+ bool check_metrics();
+ bool do_rebuild();
bool first_item_visible() const { return top_line <= 0; }
bool last_item_visible() const { return (top_line + m_visible_lines) >= m_items.size(); }
+ void force_visible_selection();
// push a new menu onto the stack
static void stack_push(std::unique_ptr<menu> &&menu) { menu->m_global_state.stack_push(std::move(menu)); }
@@ -464,26 +546,35 @@ private:
float m_lr_arrow_width;
float m_ud_arrow_width;
+ float m_items_left; // left of the area where the items are drawn
+ float m_items_right; // right of the area where the items are drawn
+ float m_items_top; // top of the area where the items are drawn
+ float m_adjust_top; // top of the "increase"/"decrease" arrows
+ float m_adjust_bottom; // bottom of the "increase"/"decrease" arrows
+ float m_decrease_left; // left of the "decrease" arrow
+ float m_increase_left; // left of the "increase" arrow
+ bool m_show_up_arrow; // are we showing the "scroll up" arrow?
+ bool m_show_down_arrow; // are we showing the "scroll down" arrow?
+ bool m_items_drawn; // have we drawn the items at least once?
+
+ track_pointer m_pointer_state; // tracking state for currently active pointer
+ std::pair<float, float> m_pointer_down; // start location of tracked pointer action
+ std::pair<float, float> m_pointer_updated; // location where pointer tracking was updated
+ int m_pointer_line; // the line we're tracking pointer motion in
+ std::chrono::steady_clock::time_point m_pointer_repeat;
+
uint32_t m_process_flags; // event processing options
int m_selected; // which item is selected
- int m_hover; // which item is being hovered over
bool m_special_main_menu; // true if no real emulation running under the menu
bool m_one_shot; // true for menus outside the normal stack
bool m_needs_prev_menu_item; // true to automatically create item to dismiss menu
bool m_active; // whether the menu is currently visible and topmost
- event m_event; // the UI event that occurred
-
float m_customtop; // amount of extra height to add at the top
float m_custombottom; // amount of extra height to add at the bottom
int m_resetpos; // item index to select after repopulating
void *m_resetref; // item reference value to select after repopulating
-
- bool m_mouse_hit;
- bool m_mouse_button;
- float m_mouse_x;
- float m_mouse_y;
};
diff --git a/src/frontend/mame/ui/miscmenu.cpp b/src/frontend/mame/ui/miscmenu.cpp
index 8ed4e64fac3..df5754a380e 100644
--- a/src/frontend/mame/ui/miscmenu.cpp
+++ b/src/frontend/mame/ui/miscmenu.cpp
@@ -311,7 +311,7 @@ bool menu_bookkeeping::handle(event const *ev)
}
else
{
- return ev && handle_key(ev->iptkey);
+ return menu_textbox::handle(ev);
}
}
diff --git a/src/frontend/mame/ui/selector.cpp b/src/frontend/mame/ui/selector.cpp
index e21026f6d44..53f68006c04 100644
--- a/src/frontend/mame/ui/selector.cpp
+++ b/src/frontend/mame/ui/selector.cpp
@@ -143,7 +143,7 @@ void menu_selector::recompute_metrics(uint32_t width, uint32_t height, float asp
// perform our special rendering
//-------------------------------------------------
-void menu_selector::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_selector::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
// get the text for 'UI Select'
std::string const tempbuf[] = { util::string_format(_("menu-selector", "Double-click or press %1$s to select"), ui().get_general_input_setting(IPT_UI_SELECT)) };
diff --git a/src/frontend/mame/ui/selector.h b/src/frontend/mame/ui/selector.h
index 4b8043a990a..58b93c5185a 100644
--- a/src/frontend/mame/ui/selector.h
+++ b/src/frontend/mame/ui/selector.h
@@ -39,7 +39,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual bool custom_ui_back() override { return !m_search.empty(); }
private:
diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp
index f9b2c702503..e9cc85e9a43 100644
--- a/src/frontend/mame/ui/selgame.cpp
+++ b/src/frontend/mame/ui/selgame.cpp
@@ -223,38 +223,6 @@ bool menu_select_game::handle(event const *ev)
}
else switch (ev->iptkey)
{
- case IPT_UI_UP:
- if ((get_focus() == focused_menu::LEFT) && (machine_filter::FIRST < m_filter_highlight))
- {
- --m_filter_highlight;
- changed = true;
- }
- break;
-
- case IPT_UI_DOWN:
- if ((get_focus() == focused_menu::LEFT) && (machine_filter::LAST > m_filter_highlight))
- {
- m_filter_highlight++;
- changed = true;
- }
- break;
-
- case IPT_UI_HOME:
- if (get_focus() == focused_menu::LEFT)
- {
- m_filter_highlight = machine_filter::FIRST;
- changed = true;
- }
- break;
-
- case IPT_UI_END:
- if (get_focus() == focused_menu::LEFT)
- {
- m_filter_highlight = machine_filter::LAST;
- changed = true;
- }
- break;
-
case IPT_UI_EXPORT:
inkey_export();
break;
@@ -278,46 +246,6 @@ bool menu_select_game::handle(event const *ev)
}
break;
- case IPT_CUSTOM:
- // handle IPT_CUSTOM (mouse right click)
- if (!m_populated_favorites)
- {
- menu::stack_push<menu_machine_configure>(
- ui(),
- container(),
- *reinterpret_cast<ui_system_info const *>(m_prev_selected),
- nullptr);
- }
- else
- {
- ui_software_info *sw = reinterpret_cast<ui_software_info *>(m_prev_selected);
- ui_system_info const &sys = m_persistent_data.systems()[driver_list::find(sw->driver->name)];
- menu::stack_push<menu_machine_configure>(
- ui(),
- container(),
- sys,
- [this, empty = sw->startempty] (bool fav, bool changed)
- {
- if (changed)
- reset(empty ? reset_options::SELECT_FIRST : reset_options::REMEMBER_REF);
- });
- }
- break;
-
- case IPT_UI_LEFT:
- if (right_panel() == RP_IMAGES)
- changed = previous_image_view(); // Images
- else if (right_panel() == RP_INFOS)
- changed = change_info_pane(-1); // Infos
- break;
-
- case IPT_UI_RIGHT:
- if (right_panel() == RP_IMAGES)
- changed = next_image_view(); // Images
- else if (right_panel() == RP_INFOS)
- changed = change_info_pane(1); // Infos
- break;
-
case IPT_UI_FAVORITES:
if (uintptr_t(ev->itemref) > m_skip_main_items)
{
@@ -356,8 +284,6 @@ bool menu_select_game::handle(event const *ev)
}
}
- // if we're in an error state, overlay an error message
- draw_error_text();
return changed;
}
@@ -801,53 +727,6 @@ bool menu_select_game::isfavorite() const
//-------------------------------------------------
-// change what's displayed in the info box
-//-------------------------------------------------
-
-bool menu_select_game::change_info_pane(int delta)
-{
- auto const cap_delta =
- [this, &delta] (uint8_t &current, uint8_t &total) -> bool
- {
- if ((0 > delta) && (-delta > current))
- delta = -int(unsigned(current));
- else if ((0 < delta) && ((current + unsigned(delta)) >= total))
- delta = int(unsigned(total - current - 1));
- if (delta)
- {
- current += delta;
- m_topline_datsview = 0;
- return true;
- }
- else
- {
- return false;
- }
- };
- ui_system_info const *sys;
- ui_software_info const *soft;
- get_selection(soft, sys);
- if (!m_populated_favorites)
- {
- if (uintptr_t(sys) > m_skip_main_items)
- return cap_delta(ui_globals::curdats_view, ui_globals::curdats_total);
- else
- return false;
- }
- else if (uintptr_t(soft) > m_skip_main_items)
- {
- if (soft->startempty)
- return cap_delta(ui_globals::curdats_view, ui_globals::curdats_total);
- else
- return cap_delta(ui_globals::cur_sw_dats_view, ui_globals::cur_sw_dats_total);
- }
- else
- {
- return false;
- }
-}
-
-//-------------------------------------------------
// populate search list
//-------------------------------------------------
@@ -1083,10 +962,10 @@ void menu_select_game::load_custom_filters()
// draw left box
//-------------------------------------------------
-float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2)
+void menu_select_game::draw_left_panel(u32 flags)
{
machine_filter_data &filter_data(m_persistent_data.filter_data());
- return menu_select_launch::draw_left_panel<machine_filter>(filter_data.get_current_filter_type(), filter_data.get_filters(), x1, y1, x2, y2);
+ menu_select_launch::draw_left_panel<machine_filter>(flags, filter_data.get_current_filter_type(), filter_data.get_filters());
}
@@ -1108,6 +987,32 @@ void menu_select_game::get_selection(ui_software_info const *&software, ui_syste
}
}
+void menu_select_game::show_config_menu(int index)
+{
+ if (!m_populated_favorites)
+ {
+ menu::stack_push<menu_machine_configure>(
+ ui(),
+ container(),
+ *reinterpret_cast<ui_system_info const *>(item(index).ref()),
+ nullptr);
+ }
+ else
+ {
+ ui_software_info *sw = reinterpret_cast<ui_software_info *>(item(index).ref());
+ ui_system_info const &sys = m_persistent_data.systems()[driver_list::find(sw->driver->name)];
+ menu::stack_push<menu_machine_configure>(
+ ui(),
+ container(),
+ sys,
+ [this, empty = sw->startempty] (bool fav, bool changed)
+ {
+ if (changed)
+ reset(empty ? reset_options::SELECT_FIRST : reset_options::REMEMBER_REF);
+ });
+ }
+}
+
void menu_select_game::make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const
{
line0 = string_format(_("%1$s %2$s ( %3$d / %4$d systems (%5$d BIOS) )"),
@@ -1142,30 +1047,29 @@ std::string menu_select_game::make_software_description(ui_software_info const &
}
-void menu_select_game::filter_selected()
+void menu_select_game::filter_selected(int index)
{
- if ((machine_filter::FIRST <= m_filter_highlight) && (machine_filter::LAST >= m_filter_highlight))
- {
- m_persistent_data.filter_data().get_filter(machine_filter::type(m_filter_highlight)).show_ui(
- ui(),
- container(),
- [this] (machine_filter &filter)
+ assert((machine_filter::FIRST <= index) && (machine_filter::LAST >= index));
+
+ m_persistent_data.filter_data().get_filter(machine_filter::type(index)).show_ui(
+ ui(),
+ container(),
+ [this] (machine_filter &filter)
+ {
+ set_switch_image();
+ machine_filter::type const new_type(filter.get_type());
+ if (machine_filter::CUSTOM == new_type)
{
- set_switch_image();
- machine_filter::type const new_type(filter.get_type());
- if (machine_filter::CUSTOM == new_type)
+ emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ if (!file.open(util::string_format("custom_%s_filter.ini", emulator_info::get_configname())))
{
- emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- if (!file.open(util::string_format("custom_%s_filter.ini", emulator_info::get_configname())))
- {
- filter.save_ini(file, 0);
- file.close();
- }
+ filter.save_ini(file, 0);
+ file.close();
}
- m_persistent_data.filter_data().set_current_filter_type(new_type);
- reset(reset_options::REMEMBER_REF);
- });
- }
+ }
+ m_persistent_data.filter_data().set_current_filter_type(new_type);
+ reset(reset_options::REMEMBER_REF);
+ });
}
} // namespace ui
diff --git a/src/frontend/mame/ui/selgame.h b/src/frontend/mame/ui/selgame.h
index 4aae7261687..8a9037a8864 100644
--- a/src/frontend/mame/ui/selgame.h
+++ b/src/frontend/mame/ui/selgame.h
@@ -62,11 +62,12 @@ private:
virtual bool handle(event const *ev) override;
// drawing
- virtual float draw_left_panel(float x1, float y1, float x2, float y2) override;
+ virtual void draw_left_panel(u32 flags) override;
virtual render_texture *get_icon_texture(int linenum, void *selectedref) override;
// get selected software and/or driver
virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const override;
+ virtual void show_config_menu(int index) override;
virtual bool accept_search() const override { return !isfavorite(); }
// text for main top/bottom panels
@@ -74,14 +75,11 @@ private:
virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const override;
// filter navigation
- virtual void filter_selected() override;
+ virtual void filter_selected(int index) override;
// toolbar
virtual void inkey_export() override;
- // internal methods
- bool change_info_pane(int delta);
-
void build_available_list();
bool isfavorite() const;
diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp
index 23176a0168c..7791aaff8d1 100644
--- a/src/frontend/mame/ui/selmenu.cpp
+++ b/src/frontend/mame/ui/selmenu.cpp
@@ -39,8 +39,6 @@
#include <algorithm>
#include <cmath>
#include <cstring>
-#include <tuple>
-#include <utility>
// these hold static bitmap images
@@ -107,6 +105,18 @@ char const *const hover_msg[] = {
N_("Show DATs view"),
};
+std::tuple<unsigned, int, bool> SYS_TOOLBAR_BITMAPS[] = {
+ { TOOLBAR_BITMAP_FAVORITE, IPT_UI_FAVORITES, true },
+ { TOOLBAR_BITMAP_SAVE, IPT_UI_EXPORT, false },
+ { TOOLBAR_BITMAP_AUDIT, IPT_UI_AUDIT, false },
+ { TOOLBAR_BITMAP_INFO, IPT_UI_DATS, true }
+};
+
+std::tuple<unsigned, int, bool> SW_TOOLBAR_BITMAPS[] = {
+ { TOOLBAR_BITMAP_FAVORITE, IPT_UI_FAVORITES, true },
+ { TOOLBAR_BITMAP_INFO, IPT_UI_DATS, true }
+};
+
void load_image(bitmap_argb32 &bitmap, emu_file &file, std::string const &base)
{
@@ -206,8 +216,8 @@ bool menu_select_launch::reselect_last::s_reselect = false;
// instantiate possible variants of these so derived classes don't get link errors
template bool menu_select_launch::select_bios(game_driver const &, bool);
template bool menu_select_launch::select_bios(ui_software_info const &, bool);
-template float menu_select_launch::draw_left_panel<machine_filter>(machine_filter::type current, std::map<machine_filter::type, machine_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
-template float menu_select_launch::draw_left_panel<software_filter>(software_filter::type current, std::map<software_filter::type, software_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
+template void menu_select_launch::draw_left_panel<machine_filter>(u32 flags, machine_filter::type current, std::map<machine_filter::type, machine_filter::ptr> const &filters);
+template void menu_select_launch::draw_left_panel<software_filter>(u32 flags, software_filter::type current, std::map<software_filter::type, software_filter::ptr> const &filters);
menu_select_launch::system_flags::system_flags(machine_static_info const &info)
@@ -253,10 +263,6 @@ void menu_select_launch::reselect_last::set_software(game_driver const &driver,
}
-inline bool menu_select_launch::show_left_panel() const { return !(m_panels_status & HIDE_LEFT_PANEL); }
-inline bool menu_select_launch::show_right_panel() const { return !(m_panels_status & HIDE_RIGHT_PANEL); }
-
-
//-------------------------------------------------
// ctor
//-------------------------------------------------
@@ -424,8 +430,8 @@ menu_select_launch::cache::cache(running_machine &machine)
std::memcpy(&m_no_avail_bitmap.pix(0), no_avail_bmp, 256 * 256 * sizeof(u32));
- m_toolbar_bitmaps.resize(UI_TOOLBAR_BUTTONS);
- m_toolbar_textures.reserve(UI_TOOLBAR_BUTTONS);
+ m_toolbar_bitmaps.resize(UI_TOOLBAR_BUTTONS * 2);
+ m_toolbar_textures.reserve(UI_TOOLBAR_BUTTONS * 2);
}
@@ -455,7 +461,7 @@ void menu_select_launch::cache::cache_toolbar(running_machine &machine, float wi
float const drawscale((std::max)(xscale, yscale));
// rasterise the SVG and clear it out of memory
- bitmap_argb32 &bitmap(m_toolbar_bitmaps[i]);
+ bitmap_argb32 &bitmap(m_toolbar_bitmaps[2 * i]);
bitmap.resize(pix_size, pix_size);
nsvgRasterize(
rasterizer.get(),
@@ -467,20 +473,28 @@ void menu_select_launch::cache::cache_toolbar(running_machine &machine, float wi
svg.reset();
// correct colour format
+ bitmap_argb32 &disabled_bitmap(m_toolbar_bitmaps[(2 * i) + 1]);
+ disabled_bitmap.resize(pix_size, pix_size);
for (s32 y = 0; bitmap.height() > y; ++y)
{
- u32 *dst(&bitmap.pix(y));
- for (s32 x = 0; bitmap.width() > x; ++x, ++dst)
+ u32 *cdst(&bitmap.pix(y));
+ u32 *mdst(&disabled_bitmap.pix(y));
+ for (s32 x = 0; bitmap.width() > x; ++x, ++cdst, ++mdst)
{
- u8 const *const src(reinterpret_cast<u8 const *>(dst));
- rgb_t const d(src[3], src[0], src[1], src[2]);
- *dst = d;
+ u8 const *const src(reinterpret_cast<u8 const *>(cdst));
+ rgb_t const c(src[3], src[0], src[1], src[2]);
+ u8 const l(std::clamp(std::lround((0.2126 * src[0]) + (0.7152 * src[1]) + (0.0722 * src[2])), 0L, 255L));
+ rgb_t const m(src[3], l, l, l);
+ *cdst = c;
+ *mdst = m;
}
}
- // make a texture
+ // make textures
render_texture &texture(*m_toolbar_textures.emplace_back(render.texture_alloc(), render));
+ render_texture &disabled_texture(*m_toolbar_textures.emplace_back(render.texture_alloc(), render));
texture.set_bitmap(bitmap, bitmap.cliprect(), TEXFORMAT_ARGB32);
+ disabled_texture.set_bitmap(disabled_bitmap, disabled_bitmap.cliprect(), TEXFORMAT_ARGB32);
}
}
}
@@ -514,9 +528,32 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c
, m_cache(mui.get_session_data<menu_select_launch, cache_wrapper>(machine()))
, m_is_swlist(is_swlist)
, m_focus(focused_menu::MAIN)
- , m_pressed(false)
- , m_repeat(0)
+ , m_pointer_action(pointer_action::NONE)
+ , m_scroll_repeat(std::chrono::steady_clock::time_point::min())
+ , m_base_pointer(0.0F, 0.0F)
+ , m_last_pointer(0.0F, 0.0F)
+ , m_clicked_line(0)
+ , m_primary_vbounds(0.0F, -1.0F)
+ , m_primary_items_top(-1.0F)
+ , m_primary_items_hbounds(0.0F, -1.0F)
+ , m_primary_lines(0)
+ , m_left_panel_width(-1.0F)
+ , m_left_items_hbounds(0.0F, -1.0F)
+ , m_left_items_top(1.0F)
+ , m_left_item_count(0)
+ , m_left_visible_lines(0)
+ , m_left_visible_top(0)
+ , m_right_panel_width(-1.0F)
+ , m_right_tabs_bottom(-1.0F)
+ , m_right_heading_top(-1.0F)
+ , m_right_content_vbounds(0.0F, -1.0F)
+ , m_right_content_hbounds(0.0F, -1.0F)
, m_right_visible_lines(0)
+ , m_toolbar_button_vbounds(0.0F, -1.0F)
+ , m_toolbar_button_width(-1.0)
+ , m_toolbar_button_spacing(-1.0)
+ , m_toolbar_backtrack_left(-1.0)
+ , m_toolbar_main_left(-1.0)
, m_panels_status(SHOW_PANELS)
, m_right_panel(RP_FIRST)
, m_has_icons(false)
@@ -529,33 +566,109 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c
}
-bool menu_select_launch::next_image_view()
+std::pair<bool, bool> menu_select_launch::next_right_panel_view()
+{
+ if (right_panel() == RP_IMAGES)
+ return next_image_view();
+ else if (right_panel() == RP_INFOS)
+ return next_info_view();
+ else
+ return std::make_pair(false, false);
+}
+
+
+std::pair<bool, bool> menu_select_launch::previous_right_panel_view()
+{
+ if (right_panel() == RP_IMAGES)
+ return previous_image_view();
+ else if (right_panel() == RP_INFOS)
+ return previous_info_view();
+ else
+ return std::make_pair(false, false);
+}
+
+
+std::pair<bool, bool> menu_select_launch::next_image_view()
{
if (LAST_VIEW > m_image_view)
{
++m_image_view;
set_switch_image();
- return true;
+ return std::make_pair(true, (LAST_VIEW > m_image_view));
}
else
{
- return false;
+ return std::make_pair(false, false);
}
}
-bool menu_select_launch::previous_image_view()
+std::pair<bool, bool> menu_select_launch::previous_image_view()
{
if (FIRST_VIEW < m_image_view)
{
--m_image_view;
set_switch_image();
- return true;
+ return std::make_pair(true, (FIRST_VIEW < m_image_view));
}
else
{
- return false;
+ return std::make_pair(false, false);
+ }
+}
+
+
+std::pair<bool, bool> menu_select_launch::next_info_view()
+{
+ ui_software_info const *software;
+ ui_system_info const *system;
+ get_selection(software, system);
+ if (software && !software->startempty)
+ {
+ if ((ui_globals::cur_sw_dats_total - 1) > ui_globals::cur_sw_dats_view)
+ {
+ ++ui_globals::cur_sw_dats_view;
+ m_topline_datsview = 0;
+ return std::make_pair(true, (ui_globals::cur_sw_dats_total - 1) > ui_globals::cur_sw_dats_view);
+ }
+ }
+ else if (system || (software && software->driver))
+ {
+ if ((ui_globals::curdats_total - 1) > ui_globals::curdats_view)
+ {
+ ++ui_globals::curdats_view;
+ m_topline_datsview = 0;
+ return std::make_pair(true, (ui_globals::curdats_total - 1) > ui_globals::curdats_view);
+ }
}
+ return std::make_pair(false, false);
+}
+
+
+std::pair<bool, bool> menu_select_launch::previous_info_view()
+{
+ ui_software_info const *software;
+ ui_system_info const *system;
+ get_selection(software, system);
+ if (software && !software->startempty)
+ {
+ if (0 < ui_globals::cur_sw_dats_view)
+ {
+ --ui_globals::cur_sw_dats_view;
+ m_topline_datsview = 0;
+ return std::make_pair(true, 0 < ui_globals::cur_sw_dats_view);
+ }
+ }
+ else if (system || (software && software->driver))
+ {
+ if (0 < ui_globals::curdats_view)
+ {
+ --ui_globals::curdats_view;
+ m_topline_datsview = 0;
+ return std::make_pair(true, 0 < ui_globals::curdats_view);
+ }
+ }
+ return std::make_pair(false, false);
}
@@ -651,13 +764,27 @@ void menu_select_launch::recompute_metrics(uint32_t width, uint32_t height, floa
m_info_layout = std::nullopt;
// calculate size of dividers between panes
- m_divider_width = 2.0F * lr_border();
- m_divider_arrow_width = 0.4F * m_divider_width;
- m_divider_arrow_height = 2.0F * tb_border();
+ m_divider_width = 0.8F * line_height() * x_aspect();
+ m_divider_arrow_width = 0.32F * line_height() * x_aspect();
+ m_divider_arrow_height = 0.64F * line_height();
// calculate info text size
m_info_line_height = ui().get_line_height(ui().options().infos_size());
+ // invalidate panel metrics
+ m_primary_vbounds = std::make_pair(0.0F, -1.0F);
+ m_primary_items_hbounds = std::make_pair(0.0F, -1.0F);
+ m_left_panel_width = -1.0F;
+ m_left_items_hbounds = std::make_pair(0.0F, -1.0F);
+ m_right_panel_width = -1.0F;
+ m_right_heading_top = -1.0F;
+ m_right_content_vbounds = std::make_pair(0.0F, -1.0F);
+ m_right_content_hbounds = std::make_pair(0.0F, -1.0F);
+ m_toolbar_button_vbounds = std::make_pair(0.0F, -1.0F);
+
+ // abandon pointer input
+ m_pointer_action = pointer_action::NONE;
+
// force right panel images to be redrawn
m_cache.set_snapx_driver(nullptr);
m_cache.set_snapx_software(nullptr);
@@ -668,7 +795,7 @@ void menu_select_launch::recompute_metrics(uint32_t width, uint32_t height, floa
// perform our special rendering
//-------------------------------------------------
-void menu_select_launch::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::custom_render(u32 flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
std::string tempbuf[4];
@@ -682,7 +809,7 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto
ui().colors().text_color(), ui().colors().background_color());
// draw toolbar
- draw_toolbar(origx1, y1, origx2, origy1 - tb_border());
+ draw_toolbar(flags, origx1, y1, origx2, origy1 - tb_border());
// determine the text to render below
ui_software_info const *swinfo;
@@ -772,14 +899,27 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto
}
else
{
- std::string copyright(emulator_info::get_copyright());
- size_t found1 = copyright.find_first_of('\n');
- size_t found2 = copyright.find_last_of('\n');
+ std::string_view copyright(emulator_info::get_copyright());
+ unsigned line(0);
+
+ // first line is version string
+ tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version);
- tempbuf[0] = string_format(_("%1$s %2$s"), emulator_info::get_appname(), build_version);
- tempbuf[1] = copyright.substr(0, found1);
- tempbuf[2] = copyright.substr(found1 + 1, found2 - (found1 + 1));
- tempbuf[3] = copyright.substr(found2 + 1);
+ // output message
+ while (line < std::size(tempbuf))
+ {
+ auto const found = copyright.find('\n');
+ if (std::string::npos != found)
+ {
+ tempbuf[line++] = copyright.substr(0, found);
+ copyright.remove_prefix(found + 1);
+ }
+ else
+ {
+ tempbuf[line++] = copyright;
+ copyright = std::string_view();
+ }
+ }
}
// draw the footer
@@ -812,7 +952,7 @@ void menu_select_launch::rotate_focus(int dir)
switch (get_focus())
{
case focused_menu::MAIN:
- if (selected_index() > m_available_items)
+ if (selected_index() >= m_available_items)
{
if ((0 > dir) || (m_panels_status == HIDE_BOTH))
select_prev();
@@ -915,204 +1055,211 @@ void menu_select_launch::inkey_dats()
//-------------------------------------------------
-// draw common arrows
+// draw info arrow
//-------------------------------------------------
-void menu_select_launch::draw_common_arrow(float origx1, float origy1, float origx2, float origy2, int current, int dmin, int dmax, float title_size)
+void menu_select_launch::draw_info_arrow(u32 flags, int line)
{
- // set left-right arrows dimension
- float const ar_x0 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width() - lr_arrow_width();
- float const ar_y0 = origy1 + 0.1f * line_height();
- float const ar_x1 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width();
- float const ar_y1 = origy1 + 0.9f * line_height();
-
- float const al_x0 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width();
- float const al_y0 = origy1 + 0.1f * line_height();
- float const al_x1 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width() + lr_arrow_width();
- float const al_y1 = origy1 + 0.9f * line_height();
+ float const linetop(m_right_content_vbounds.first + (float(line) * m_info_line_height));
+ float const linebottom(m_right_content_vbounds.first + (float(line + 1) * m_info_line_height));
+ bool const hovered(pointer_in_rect(m_right_content_hbounds.first, linetop, m_right_content_hbounds.second, linebottom));
+ bool const clicked((pointer_action::RIGHT_TRACK_LINE == m_pointer_action) && (line == m_clicked_line));
- rgb_t fgcolor_right, fgcolor_left;
- fgcolor_right = fgcolor_left = ui().colors().text_color();
-
- // set hover
- if (mouse_in_rect(ar_x0, ar_y0, ar_x1, ar_y1) && current != dmax)
+ rgb_t bgcolor = ui().colors().text_bg_color();
+ rgb_t fgcolor = ui().colors().text_color();
+ if (clicked && hovered)
{
- ui().draw_textured_box(container(), ar_x0 + 0.01f, ar_y0, ar_x1 - 0.01f, ar_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43),
- hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
- set_hover(HOVER_UI_RIGHT);
- fgcolor_right = ui().colors().mouseover_color();
+ // draw selected highlight for tracked item
+ bgcolor = ui().colors().selected_bg_color();
+ fgcolor = ui().colors().selected_color();
+ highlight(m_right_content_hbounds.first, linetop, m_right_content_hbounds.second, linebottom, bgcolor);
}
- else if (mouse_in_rect(al_x0, al_y0, al_x1, al_y1) && current != dmin)
+ else if (clicked || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered && pointer_idle()))
{
- ui().draw_textured_box(container(), al_x0 + 0.01f, al_y0, al_x1 - 0.01f, al_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43),
- hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
- set_hover(HOVER_UI_LEFT);
- fgcolor_left = ui().colors().mouseover_color();
+ // draw hover highlight when hovered over or dragged off
+ bgcolor = ui().colors().mouseover_bg_color();
+ fgcolor = ui().colors().mouseover_color();
+ highlight(m_right_content_hbounds.first, linetop, m_right_content_hbounds.second, linebottom, bgcolor);
}
- // apply arrow
- if (dmax == dmin)
- return;
- else if (current == dmin)
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90);
- else if (current == dmax)
- draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X);
- else
- {
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90);
- draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X);
- }
+ draw_arrow(
+ 0.5F * (m_right_content_hbounds.first + m_right_content_hbounds.second - (x_aspect() * m_info_line_height)),
+ linetop + (0.25F * m_info_line_height),
+ 0.5F * (m_right_content_hbounds.first + m_right_content_hbounds.second + (x_aspect() * m_info_line_height)),
+ linetop + (0.75F * m_info_line_height),
+ fgcolor,
+ line ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
}
//-------------------------------------------------
-// draw info arrow
+// draw vertical divider
//-------------------------------------------------
-void menu_select_launch::draw_info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float ud_arrow_width)
+void menu_select_launch::draw_divider(u32 flags, float x1, bool right)
{
+ // work out colours
rgb_t fgcolor = ui().colors().text_color();
-
- if (mouse_in_rect(origx1, oy1, origx2, oy1 + line_height))
+ rgb_t bgcolor(0xef, 0x12, 0x47, 0x7b); // FIXME: magic numbers in colour?
+ bool const hovered(pointer_in_rect(x1, m_primary_vbounds.first, x1 + m_divider_width, m_primary_vbounds.second));
+ bool const clicked((pointer_action::DIVIDER_TRACK == m_pointer_action) && ((right ? 1 : 0) == m_clicked_line));
+ if (hovered && clicked)
+ {
+ fgcolor = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ }
+ else if (clicked || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered))
{
- ui().draw_textured_box(
- container(),
- origx1 + 0.01f, oy1, origx2 - 0.01f, oy1 + line_height,
- ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43),
- hilight_main_texture(),
- PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
- set_hover(!ub ? HOVER_DAT_UP : HOVER_DAT_DOWN);
fgcolor = ui().colors().mouseover_color();
}
- u32 const orientation = !ub ? ROT0 : (ROT0 ^ ORIENTATION_FLIP_Y);
- draw_arrow(
- 0.5f * (origx1 + origx2) - 0.5f * ud_arrow_width, oy1 + 0.25f * line_height,
- 0.5f * (origx1 + origx2) + 0.5f * ud_arrow_width, oy1 + 0.75f * line_height,
- fgcolor,
- orientation);
+ // draw the divider pane
+ ui().draw_outlined_box(container(), x1, m_primary_vbounds.first, x1 + m_divider_width, m_primary_vbounds.second, bgcolor);
+
+ // draw the arrow
+ uint32_t orientation(ROT90);
+ if (right ? !show_right_panel() : show_left_panel())
+ orientation ^= ORIENTATION_FLIP_X;
+ float const ar_x0 = x1 + (0.5F * (m_divider_width - m_divider_arrow_width));
+ float const ar_y0 = 0.5F * (m_primary_vbounds.first + m_primary_vbounds.second - m_divider_arrow_height);
+ draw_arrow(ar_x0, ar_y0, ar_x0 + m_divider_arrow_width, ar_y0 + m_divider_arrow_height, fgcolor, orientation);
}
-bool menu_select_launch::draw_error_text()
-{
- if (m_ui_error)
- ui().draw_text_box(container(), m_error_text, text_layout::text_justify::CENTER, 0.5f, 0.5f, UI_RED_COLOR);
- return m_ui_error;
-}
+//-------------------------------------------------
+// draw left panel (filter list)
+//-------------------------------------------------
template <typename Filter>
-float menu_select_launch::draw_left_panel(
- typename Filter::type current,
- std::map<typename Filter::type, typename Filter::ptr> const &filters,
- float x1, float y1, float x2, float y2)
+void menu_select_launch::draw_left_panel(u32 flags, typename Filter::type current, std::map<typename Filter::type, typename Filter::ptr> const &filters)
{
if (!show_left_panel())
- return draw_collapsed_left_panel(x1, y1, x2, y2);
-
- // calculate line height
- float const sc(y2 - y1 - (2.0F * tb_border()));
- float line_height_max(m_info_line_height);
- if ((Filter::COUNT * line_height_max) > sc)
{
- float const lm(sc / Filter::COUNT);
- line_height_max = line_height() * (lm / line_height());
+ // left panel hidden, but no need to recompute metrics if target isn't resized
+ m_left_panel_width = 0.0F;
+
+ draw_divider(flags, lr_border(), false);
+ return;
}
- // calculate horizontal offset for unadorned names
- std::string tmp(convert_command_glyph("_# "));
- float const text_sign = ui().get_string_width(tmp, m_info_line_height);
+ // get the width of the selection indicator glyphs
+ float const checkmark_width = ui().get_string_width(convert_command_glyph("_# "), m_info_line_height);
+
+ if (m_left_items_hbounds.first >= m_left_items_hbounds.second)
+ {
+ // calculate number of lines that will fit - centre vertically if we need scroll arrows
+ float const height(m_primary_vbounds.second - m_primary_vbounds.first);
+ int const lines((height - (tb_border() * 2.0F)) / m_info_line_height);
+ if (Filter::COUNT <= lines)
+ m_left_items_top = m_primary_vbounds.first + tb_border();
+ else
+ m_left_items_top = m_primary_vbounds.first + ((height - (float(lines) * m_info_line_height)) * 0.5F);
+ float const pixelheight(target_size().second);
+ m_left_items_top = std::round(m_left_items_top * pixelheight) / pixelheight;
+ m_left_item_count = Filter::COUNT;
+ m_left_visible_lines = std::min<int>(Filter::COUNT, lines);
+
+ // get the maximum filter name width, restricted to a quarter of the target width
+ float line_width(0.0F);
+ for (typename Filter::type x = Filter::FIRST; Filter::COUNT > x; ++x)
+ line_width = std::max(ui().get_string_width(Filter::display_name(x), m_info_line_height) + checkmark_width, line_width);
+ line_width = std::min(line_width + (lr_border() * 2.0F), 0.25F);
+ m_left_items_hbounds = std::make_pair(2.0F * lr_border(), (2.0F * lr_border()) + line_width);
+
+ // make sure the scroll position makes sense
+ m_left_visible_top = (std::min)(m_left_visible_top, m_left_item_count - m_left_visible_lines);
+ }
+ m_left_panel_width = (m_left_items_hbounds.second - m_left_items_hbounds.first) + (lr_border() * 2.0F);
- // get the maximum width of a filter name
- float left_width(0.0F);
- for (typename Filter::type x = Filter::FIRST; Filter::COUNT > x; ++x)
- left_width = std::max(ui().get_string_width(Filter::display_name(x), m_info_line_height) + text_sign, left_width);
- left_width = std::min(left_width, 0.25F);
+ // ensure the highlighted item is visible
+ if ((m_filter_highlight - Filter::FIRST) < (m_left_visible_top + 1))
+ m_left_visible_top = (Filter::FIRST == m_filter_highlight) ? 0 : (m_filter_highlight - 1);
+ else if ((m_filter_highlight - Filter::FIRST) > (m_left_visible_top + m_left_visible_lines - 2))
+ m_left_visible_top = (std::min)(m_filter_highlight - Filter::FIRST + 2 - m_left_visible_lines, m_left_item_count - m_left_visible_lines);
// outline the box and inset by the border width
- float const origy1(y1);
- float const origy2(y2);
- x2 = x1 + left_width + 2.0F * lr_border();
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
- x1 += lr_border();
- x2 -= lr_border();
- y1 += tb_border();
- y2 -= tb_border();
+ ui().draw_outlined_box(
+ container(),
+ lr_border(), m_primary_vbounds.first, lr_border() + m_left_panel_width, m_primary_vbounds.second,
+ ui().colors().background_color());
// now draw the rows
+ typename Filter::type filter(Filter::FIRST);
+ for (int i = 0; i < m_left_visible_top; ++i)
+ ++filter;
auto const active_filter(filters.find(current));
- for (typename Filter::type filter = Filter::FIRST; Filter::COUNT > filter; ++filter)
+ std::string str;
+ bool const atbottom((m_left_visible_top + m_left_visible_lines) == m_left_item_count);
+ for (int line = 0; line < m_left_visible_lines; ++line, ++filter)
{
- std::string str;
- if (filters.end() != active_filter)
- {
- str = active_filter->second->adorned_display_name(filter);
- }
- else
- {
- if (current == filter)
- str = convert_command_glyph("_> ");
- str.append(Filter::display_name(filter));
- }
+ float const line_top(m_left_items_top + (float(line) * m_info_line_height));
+ bool const uparrow(!line && m_left_visible_top);
+ bool const downarrow(!atbottom && ((m_left_visible_lines - 1) == line));
- // handle mouse hover in passing
+ // work out the colours for this item
rgb_t bgcolor = ui().colors().text_bg_color();
rgb_t fgcolor = ui().colors().text_color();
- if (mouse_in_rect(x1, y1, x2, y1 + line_height_max))
+ bool const hovered(pointer_in_rect(m_left_items_hbounds.first, line_top, m_left_items_hbounds.second, line_top + m_info_line_height));
+ bool const pointerline((pointer_action::LEFT_TRACK_LINE == m_pointer_action) && (line == m_clicked_line));
+ if (pointerline && hovered)
{
- bgcolor = ui().colors().mouseover_bg_color();
- fgcolor = ui().colors().mouseover_color();
- set_hover(HOVER_FILTER_FIRST + filter);
- highlight(x1, y1, x2, y1 + line_height_max, bgcolor);
+ // draw selected highlight for tracked item
+ bgcolor = ui().colors().selected_bg_color();
+ fgcolor = ui().colors().selected_color();
+ highlight(m_left_items_hbounds.first, line_top, m_left_items_hbounds.second, line_top + m_info_line_height, bgcolor);
}
-
- // draw primary highlight if keyboard focus is here
- if ((m_filter_highlight == filter) && (get_focus() == focused_menu::LEFT))
+ else if ((m_filter_highlight == filter) && (get_focus() == focused_menu::LEFT))
{
+ // draw primary highlight if keyboard focus is here
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
ui().draw_textured_box(
container(),
- x1, y1, x2, y1 + line_height_max,
+ m_left_items_hbounds.first, line_top, m_left_items_hbounds.second, line_top + m_info_line_height,
bgcolor, rgb_t(255, 43, 43, 43),
hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
+ else if (pointerline || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered && pointer_idle()))
+ {
+ // draw hover highlight when hovered over or dragged off
+ bgcolor = ui().colors().mouseover_bg_color();
+ fgcolor = ui().colors().mouseover_color();
+ highlight(m_left_items_hbounds.first, line_top, m_left_items_hbounds.second, line_top + m_info_line_height, bgcolor);
+ }
- // finally draw the text itself and move to the next line
- float const x1t = x1 + ((str == Filter::display_name(filter)) ? text_sign : 0.0F);
- ui().draw_text_full(
- container(), str,
- x1t, y1, x2 - x1t,
- text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
- mame_ui_manager::NORMAL, fgcolor, bgcolor,
- nullptr, nullptr,
- m_info_line_height);
- y1 += line_height_max;
- }
-
- x1 = x2 + lr_border();
- x2 = x1 + m_divider_width;
- y1 = origy1;
- y2 = origy2;
-
- // set left-right arrows dimension
- float const ar_x0 = 0.5F * (x2 + x1 - m_divider_arrow_width);
- float const ar_y0 = 0.5F * (y2 + y1 - m_divider_arrow_height);
- float const ar_x1 = ar_x0 + m_divider_arrow_width;
- float const ar_y1 = ar_y0 + m_divider_arrow_height;
-
- ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b));
-
- rgb_t fgcolor = ui().colors().text_color();
- if (mouse_in_rect(x1, y1, x2, y2))
- {
- fgcolor = ui().colors().mouseover_color();
- set_hover(HOVER_LPANEL_ARROW);
+ // finally draw the text itself
+ if (uparrow || downarrow)
+ {
+ draw_arrow(
+ 0.5F * (m_left_items_hbounds.first + m_left_items_hbounds.second + (x_aspect() * m_info_line_height)),
+ line_top + (0.25F * m_info_line_height),
+ 0.5F * (m_left_items_hbounds.first + m_left_items_hbounds.second - (x_aspect() * m_info_line_height)),
+ line_top + (0.75F * m_info_line_height),
+ fgcolor,
+ downarrow ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
+ }
+ else
+ {
+ if (filters.end() != active_filter)
+ str = active_filter->second->adorned_display_name(filter);
+ else if (current == filter)
+ (str = convert_command_glyph("_> ")).append(Filter::display_name(filter));
+ else
+ str = Filter::display_name(filter);
+ float const x1t = m_left_items_hbounds.first + lr_border() + ((str == Filter::display_name(filter)) ? checkmark_width : 0.0F);
+ ui().draw_text_full(
+ container(), str,
+ x1t, line_top, m_left_items_hbounds.second - x1t - lr_border() + (1.0F / float(target_size().second)),
+ text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, fgcolor, bgcolor,
+ nullptr, nullptr,
+ m_info_line_height);
+ }
}
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
- return x2 + lr_border();
+ // draw the divider
+ draw_divider(flags, lr_border() + m_left_panel_width, false);
}
@@ -1282,77 +1429,86 @@ bool menu_select_launch::select_part(mame_ui_manager &mui, render_container &con
// draw toolbar
//-------------------------------------------------
-void menu_select_launch::draw_toolbar(float x1, float y1, float x2, float y2)
+void menu_select_launch::draw_toolbar(u32 flags, float x1, float y1, float x2, float y2)
{
+ // work out which buttons we're going to draw
+ bool const have_parent(m_is_swlist || !stack_has_special_main_menu());
+ auto const *const toolbar_bitmaps(m_is_swlist ? SW_TOOLBAR_BITMAPS : SYS_TOOLBAR_BITMAPS);
+ unsigned const toolbar_count(m_is_swlist ? std::size(SW_TOOLBAR_BITMAPS) : std::size(SYS_TOOLBAR_BITMAPS));
+
// draw a box
ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b));
- // take off the borders
- float const aspect(machine().render().ui_aspect(&container()));
- x1 += lr_border();
- x2 -= lr_border();
- y1 += tb_border();
- y2 -= tb_border();
+ // cache metrics and bitmaps if necessary
+ if (m_toolbar_button_vbounds.first >= m_toolbar_button_vbounds.second)
+ {
+ m_toolbar_button_vbounds.first = y1 + tb_border();
+ m_toolbar_button_vbounds.second = y2 - tb_border();
+ float const button_height(m_toolbar_button_vbounds.second - m_toolbar_button_vbounds.first);
+ m_toolbar_button_width = button_height * float(x_aspect());
+ m_toolbar_button_spacing = m_toolbar_button_width * 1.5F;
+ float const total_width((float(toolbar_count) + (float(toolbar_count - 1) * 0.5F)) * m_toolbar_button_width);
+ m_toolbar_backtrack_left = x2 - lr_border() - m_toolbar_button_width;
+ m_toolbar_main_left = (std::min)(m_toolbar_backtrack_left - (float(toolbar_count) * m_toolbar_button_spacing), (x1 + x2 - total_width) * 0.5F);
+ m_cache.cache_toolbar(machine(), m_toolbar_button_width, button_height);
+ }
- // work out which buttons we're going to draw
- constexpr unsigned SYS_TOOLBAR_BITMAPS[] = { TOOLBAR_BITMAP_FAVORITE, TOOLBAR_BITMAP_SAVE, TOOLBAR_BITMAP_AUDIT, TOOLBAR_BITMAP_INFO };
- constexpr unsigned SW_TOOLBAR_BITMAPS[] = { TOOLBAR_BITMAP_FAVORITE, TOOLBAR_BITMAP_INFO };
- bool const have_parent = m_is_swlist || !stack_has_special_main_menu();
- unsigned const *const toolbar_bitmaps = m_is_swlist ? SW_TOOLBAR_BITMAPS : SYS_TOOLBAR_BITMAPS;
- unsigned const toolbar_count = m_is_swlist ? std::size(SW_TOOLBAR_BITMAPS) : std::size(SYS_TOOLBAR_BITMAPS);
-
- // calculate metrics
- float const x_size = (y2 - y1) * aspect;
- float const x_spacing = x_size * 1.5f;
- float const backtrack_pos = x2 - x_size;
- float const total_width = (float(toolbar_count) + (float(toolbar_count - 1) * 0.5f)) * x_size;
- m_cache.cache_toolbar(machine(), x_size, y2 - y1);
-
- // add backtrack button
- rgb_t color(0xffcccccc);
- if (mouse_in_rect(backtrack_pos, y1, x2, y2))
- {
- set_hover(HOVER_BACKTRACK);
- color = rgb_t::white();
- float const ypos = y2 + line_height() + 2.0f * tb_border();
- ui().draw_text_box(
- container(),
- have_parent ? _("Return to Previous Menu") : _("Exit"),
- text_layout::text_justify::RIGHT, 1.0f - lr_border(), ypos,
- ui().colors().background_color());
+ // tooltip needs to be above for pen/touch to avoid being obscured
+ float tooltip_pos;
+ switch (pointer_type())
+ {
+ case ui_event::pointer::PEN:
+ case ui_event::pointer::TOUCH:
+ tooltip_pos = m_toolbar_button_vbounds.first - line_height() - (2.0F * tb_border());
+ break;
+ default:
+ tooltip_pos = m_toolbar_button_vbounds.second + line_height() + tb_border();
+ break;
+ }
+
+
+ {
+ // add backtrack button
+ bool const hovered(pointer_in_rect(m_toolbar_backtrack_left, m_toolbar_button_vbounds.first, m_toolbar_backtrack_left + m_toolbar_button_width, m_toolbar_button_vbounds.second));
+ bool const tracked((pointer_action::TOOLBAR_TRACK == m_pointer_action) && (0 > m_clicked_line));
+ rgb_t const color((hovered && tracked) ? rgb_t::white() : rgb_t(0xffcccccc));
+ if (tracked || (hovered && !(flags & PROCESS_NOINPUT) && pointer_idle()))
+ {
+ ui().draw_text_box(
+ container(),
+ have_parent ? _("Return to Previous Menu") : _("Exit"),
+ text_layout::text_justify::RIGHT, 1.0F - lr_border(), tooltip_pos,
+ ui().colors().background_color());
+ }
+ container().add_quad(
+ m_toolbar_backtrack_left, m_toolbar_button_vbounds.first, m_toolbar_backtrack_left + m_toolbar_button_width, m_toolbar_button_vbounds.second,
+ color,
+ m_cache.toolbar_textures()[2 * (have_parent ? TOOLBAR_BITMAP_PREVMENU : TOOLBAR_BITMAP_EXIT)].get(),
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
- container().add_quad(
- backtrack_pos, y1, x2, y2,
- color,
- m_cache.toolbar_textures()[have_parent ? TOOLBAR_BITMAP_PREVMENU : TOOLBAR_BITMAP_EXIT].get(),
- PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
// now add the other buttons
- x1 = (std::min)(backtrack_pos - (float(toolbar_count) * x_spacing), x1 + ((x2 - x1 - total_width) * 0.5f));
- for (int z = 0; toolbar_count > z; ++z, x1 += x_spacing)
+ for (int z = 0; toolbar_count > z; ++z)
{
- auto const bitmap = toolbar_bitmaps[z];
- x2 = x1 + x_size;
- color = rgb_t (0xffcccccc);
- if (mouse_in_rect(x1, y1, x2, y2))
+ auto const [bitmap, action, need_selection] = toolbar_bitmaps[z];
+ float const button_left(m_toolbar_main_left + (float(z) * m_toolbar_button_spacing));
+ float const button_right(button_left + m_toolbar_button_width);
+ bool const enabled(!need_selection || get_selection_ptr());
+ bool const hovered(pointer_in_rect(button_left, m_toolbar_button_vbounds.first, button_right, m_toolbar_button_vbounds.second));
+ bool const tracked((pointer_action::TOOLBAR_TRACK == m_pointer_action) && (z == m_clicked_line));
+ rgb_t color((hovered && tracked && enabled) ? rgb_t::white() : rgb_t(0xffcccccc));
+ if (tracked || (hovered && !(flags & PROCESS_NOINPUT) && pointer_idle()))
{
- bool const need_selection = (TOOLBAR_BITMAP_FAVORITE == bitmap) || (TOOLBAR_BITMAP_INFO == bitmap);
- if (!need_selection || get_selection_ptr())
- {
- set_hover(HOVER_B_FAV + bitmap);
- color = rgb_t::white();
- }
- float ypos = y2 + line_height() + 2.0f * tb_border();
ui().draw_text_box(
container(),
_(hover_msg[bitmap]),
- text_layout::text_justify::CENTER, (x1 + x2) * 0.5f, ypos,
+ text_layout::text_justify::CENTER, (button_left + button_right) * 0.5F, tooltip_pos,
ui().colors().background_color());
}
container().add_quad(
- x1, y1, x2, y2,
+ button_left, m_toolbar_button_vbounds.first, button_right, m_toolbar_button_vbounds.second,
color,
- m_cache.toolbar_textures()[bitmap].get(),
+ m_cache.toolbar_textures()[(2 * bitmap) + (enabled ? 0 : 1)].get(),
PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
}
@@ -1377,13 +1533,6 @@ void menu_select_launch::draw_star(float x0, float y0)
}
-void menu_select_launch::set_pressed()
-{
- (m_repeat == 0) ? m_repeat = osd_ticks() + osd_ticks_per_second() / 2 : m_repeat = osd_ticks() + osd_ticks_per_second() / 4;
- m_pressed = true;
-}
-
-
//-------------------------------------------------
// draw icons
//-------------------------------------------------
@@ -1404,10 +1553,9 @@ void menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, flo
// get title and search path for right panel
//-------------------------------------------------
-void menu_select_launch::get_title_search(std::string &snaptext, std::string &searchstr)
+std::string menu_select_launch::get_arts_searchpath()
{
- // get arts title text
- snaptext.assign(_("selmenu-artwork", std::get<1>(ARTS_INFO[m_image_view])));
+ std::string searchstr;
// get search path
std::string addpath;
@@ -1436,6 +1584,251 @@ void menu_select_launch::get_title_search(std::string &snaptext, std::string &se
while (path_iter.next(c_path))
searchstr.append(";").append(curpath).append(PATH_SEPARATOR).append(c_path);
}
+
+ return searchstr;
+}
+
+
+//-------------------------------------------------
+// handle UI input events for main menu
+//-------------------------------------------------
+
+bool menu_select_launch::handle_events(u32 flags, event &ev)
+{
+ // loop while we have interesting events
+ bool stop(false), need_update(false), search_changed(false);
+ ui_event local_menu_event;
+ while (!stop && machine().ui_input().pop_event(&local_menu_event))
+ {
+ switch (local_menu_event.event_type)
+ {
+ // deal with pointer-like input (mouse, pen, touch, etc.)
+ case ui_event::type::POINTER_UPDATE:
+ {
+ auto const [key, redraw] = handle_pointer_update(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
+ {
+ ev.iptkey = key;
+ stop = true;
+ }
+ }
+ break;
+
+ // pointer left the normal way, possibly releasing buttons
+ case ui_event::type::POINTER_LEAVE:
+ {
+ auto const [key, redraw] = handle_pointer_leave(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
+ {
+ ev.iptkey = key;
+ stop = true;
+ }
+ }
+ break;
+
+ // pointer left in some abnormal way - cancel any associated actions
+ case ui_event::type::POINTER_ABORT:
+ {
+ auto const [key, redraw] = handle_pointer_abort(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
+ {
+ ev.iptkey = key;
+ stop = true;
+ }
+ }
+ break;
+
+ // caught scroll event
+ case ui_event::type::MOUSE_WHEEL:
+ if ((&machine().render().ui_target() == local_menu_event.target) && pointer_idle() && !m_ui_error)
+ {
+ float x, y;
+ bool const hit(local_menu_event.target->map_point_container(local_menu_event.mouse_x, local_menu_event.mouse_y, container(), x, y));
+ if (!hit)
+ break;
+
+ if ((x >= m_primary_items_hbounds.first) && (x < m_primary_items_hbounds.second) && (y >= m_primary_items_top) && (y < (m_primary_items_top + (float(m_primary_lines) * line_height()))))
+ {
+ if (local_menu_event.zdelta > 0)
+ {
+ if ((selected_index() >= m_available_items) || is_first_selected())
+ break;
+ stop = true;
+ ev.iptkey = IPT_CUSTOM; // stop processing events so info can be rebuilt
+ set_selected_index(selected_index() - local_menu_event.num_lines);
+ if (selected_index() < top_line + (top_line != 0))
+ top_line -= local_menu_event.num_lines;
+ }
+ else
+ {
+ if (selected_index() >= (m_available_items - 1))
+ break;
+ stop = true;
+ ev.iptkey = IPT_CUSTOM; // stop processing events so info can be rebuilt
+ set_selected_index(std::min(selected_index() + local_menu_event.num_lines, m_available_items - 1));
+ if (selected_index() >= top_line + m_visible_items + (top_line != 0))
+ top_line += local_menu_event.num_lines;
+ }
+ }
+ else if ((x >= m_right_content_hbounds.first) && (x < m_right_content_hbounds.second) && (y >= m_right_content_vbounds.first) && (y < m_right_content_vbounds.second))
+ {
+ if (show_right_panel() && (RP_INFOS == m_right_panel))
+ {
+ if (local_menu_event.zdelta > 0)
+ m_topline_datsview -= local_menu_event.num_lines;
+ else
+ m_topline_datsview += local_menu_event.num_lines;
+ }
+ }
+ }
+ break;
+
+ // text input goes to the search field unless there's an error message displayed
+ case ui_event::type::IME_CHAR:
+ if (!pointer_idle())
+ break;
+
+ if (exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_NEXT, 0) || exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_PREV, 0))
+ {
+ stop = true;
+ }
+ else if (m_ui_error)
+ {
+ ev.iptkey = IPT_CUSTOM;
+ stop = true;
+ }
+ else if (accept_search())
+ {
+ if (input_character(m_search, local_menu_event.ch, uchar_is_printable))
+ search_changed = true;
+ }
+ break;
+
+ // ignore everything else
+ default:
+ break;
+ }
+
+ // need to update search before processing certain kinds of events, but others don't matter
+ if (search_changed)
+ {
+ switch (machine().ui_input().peek_event_type())
+ {
+ case ui_event::type::MOUSE_WHEEL:
+ case ui_event::type::POINTER_UPDATE:
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ stop = true;
+ break;
+ case ui_event::type::NONE:
+ case ui_event::type::WINDOW_FOCUS:
+ case ui_event::type::WINDOW_DEFOCUS:
+ case ui_event::type::IME_CHAR:
+ break;
+ }
+ }
+ }
+
+ // deal with repeating main scroll arrows
+ if ((pointer_action::MAIN_TRACK_LINE == m_pointer_action) && (is_main_up_arrow(m_clicked_line) || (is_main_down_arrow(m_clicked_line))))
+ {
+ if (check_scroll_repeat(m_primary_items_top, m_primary_items_hbounds, line_height()))
+ {
+ if (!m_clicked_line)
+ {
+ // scroll up
+ --top_line;
+ if (main_at_top())
+ m_pointer_action = pointer_action::NONE;
+ }
+ else
+ {
+ // scroll down
+ ++top_line;
+ if (main_at_bottom())
+ m_pointer_action = pointer_action::NONE;
+ }
+ if (main_force_visible_selection())
+ {
+ if (IPT_INVALID == ev.iptkey)
+ ev.iptkey = IPT_CUSTOM; // stop processing events so the info pane can be rebuilt
+ }
+ need_update = true;
+ }
+ }
+
+ // deal with repeating info view arrows
+ if (pointer_action::RIGHT_TRACK_ARROW == m_pointer_action)
+ {
+ float const left(m_clicked_line ? (m_right_content_hbounds.second - lr_border() - lr_arrow_width()) : (m_right_content_hbounds.first + lr_border()));
+ float const right(m_clicked_line ? (m_right_content_hbounds.second - lr_border()) : (m_right_content_hbounds.first + lr_border() + lr_arrow_width()));
+ if (pointer_in_rect(left, right_arrows_top(), right, right_arrows_bottom()))
+ {
+ if (std::chrono::steady_clock::now() >= m_scroll_repeat)
+ {
+ m_scroll_repeat += std::chrono::milliseconds(200);
+ if (!(m_clicked_line ? next_right_panel_view() : previous_right_panel_view()).second)
+ m_pointer_action = pointer_action::NONE;
+ need_update = true;
+ }
+ }
+ }
+
+ // deal with repeating filter scroll arrows
+ if ((pointer_action::LEFT_TRACK_LINE == m_pointer_action) && (is_left_up_arrow(m_clicked_line) || (is_left_down_arrow(m_clicked_line))))
+ {
+ if (check_scroll_repeat(m_left_items_top, m_left_items_hbounds, m_info_line_height))
+ {
+ if (!m_clicked_line)
+ {
+ // scroll up
+ --m_left_visible_top;
+ m_filter_highlight = std::min(m_left_visible_top + m_left_visible_lines - 2, m_filter_highlight);
+ if (left_at_top())
+ m_pointer_action = pointer_action::NONE;
+ }
+ else
+ {
+ // scroll down
+ ++m_left_visible_top;
+ m_filter_highlight = std::max(m_left_visible_top + 1, m_filter_highlight);
+ if (left_at_bottom())
+ m_pointer_action = pointer_action::NONE;
+ }
+ need_update = true;
+ }
+ }
+
+ // deal with repeating filter scroll arrows
+ if (pointer_action::RIGHT_TRACK_LINE == m_pointer_action)
+ {
+ if (check_scroll_repeat(m_right_content_vbounds.first, m_right_content_hbounds, m_info_line_height))
+ {
+ if (!m_clicked_line)
+ {
+ // scroll up
+ --m_topline_datsview;
+ if (info_at_top())
+ m_pointer_action = pointer_action::NONE;
+ }
+ else
+ {
+ // scroll down
+ ++m_topline_datsview;
+ if (info_at_bottom())
+ m_pointer_action = pointer_action::NONE;
+ }
+ need_update = true;
+ }
+ }
+
+ if (search_changed)
+ reset(reset_options::SELECT_FIRST);
+
+ return need_update;
}
@@ -1443,11 +1836,11 @@ void menu_select_launch::get_title_search(std::string &snaptext, std::string &se
// handle keys for main menu
//-------------------------------------------------
-void menu_select_launch::handle_keys(u32 flags, int &iptkey)
+bool menu_select_launch::handle_keys(u32 flags, int &iptkey)
{
// bail if no items
if (item_count() == 0)
- return;
+ return false;
// if we hit select, return true or pop the stack, depending on the item
if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0))
@@ -1458,9 +1851,9 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
}
else if (m_focus == focused_menu::LEFT)
{
- filter_selected();
+ filter_selected(m_filter_highlight);
}
- return;
+ return false;
}
if (exclusive_input_pressed(iptkey, IPT_UI_BACK, 0))
@@ -1468,13 +1861,13 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
if (m_ui_error)
{
// dismiss error
- return;
+ return false;
}
else if (!is_special_main_menu() && m_search.empty())
{
// pop the stack if this isn't the root session menu
stack_pop();
- return;
+ return false;
}
}
@@ -1492,11 +1885,12 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
stack_pop();
machine().schedule_exit();
}
- return;
+ return false;
}
// validate the current selection
validate_selection(1);
+ bool updated(false);
// accept left/right keys as-is with repeat
if (exclusive_input_pressed(iptkey, IPT_UI_LEFT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
@@ -1504,12 +1898,28 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
if (m_focus == focused_menu::RIGHTTOP)
{
// Swap the right panel and swallow it
- m_right_panel = RP_IMAGES;
iptkey = IPT_INVALID;
+ if (right_panel() != RP_IMAGES)
+ {
+ m_right_panel = RP_IMAGES;
+ updated = true;
+ }
}
- else
+ else if (show_right_panel())
{
- return;
+ // Swap the right panel page and swallow it
+ if (right_panel() == RP_IMAGES)
+ {
+ iptkey = IPT_INVALID;
+ if (previous_image_view().first)
+ updated = true;
+ }
+ else if (right_panel() == RP_INFOS)
+ {
+ iptkey = IPT_INVALID;
+ if (previous_info_view().first)
+ updated = true;
+ }
}
}
@@ -1519,71 +1929,120 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
if (m_focus == focused_menu::RIGHTTOP)
{
// Swap the right panel and swallow it
- m_right_panel = RP_INFOS;
iptkey = IPT_INVALID;
+ if (right_panel() != RP_INFOS)
+ {
+ m_right_panel = RP_INFOS;
+ updated = true;
+ }
}
- else
+ else if (show_right_panel())
{
- return;
+ // Swap the right panel page and swallow it
+ if (right_panel() == RP_IMAGES)
+ {
+ iptkey = IPT_INVALID;
+ if (next_image_view().first)
+ updated = true;
+ }
+ else if (right_panel() == RP_INFOS)
+ {
+ iptkey = IPT_INVALID;
+ if (next_info_view().first)
+ updated = true;
+ }
}
}
// up backs up by one item
if (exclusive_input_pressed(iptkey, IPT_UI_UP, 6))
{
- if (show_left_panel() && m_focus == focused_menu::LEFT)
+ if (m_focus == focused_menu::LEFT)
{
- return;
+ // swallow it
+ iptkey = IPT_INVALID;
+ if (m_filter_highlight)
+ {
+ --m_filter_highlight;
+ updated = true;
+ }
}
- else if (show_right_panel() && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM)))
+ else if ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))
{
- m_topline_datsview--;
- return;
+ // swallow it
+ iptkey = IPT_INVALID;
+ if (m_topline_datsview)
+ {
+ m_topline_datsview--;
+ updated = true;
+ }
}
else if (selected_index() == m_available_items + 1 || is_first_selected() || m_ui_error)
{
- return;
+ return updated;
+ }
+ else
+ {
+ set_selected_index(selected_index() - 1);
+ if (selected_index() == top_line && top_line != 0)
+ top_line--;
}
-
- set_selected_index(selected_index() - 1);
-
- if (selected_index() == top_line && top_line != 0)
- top_line--;
}
// down advances by one item
if (exclusive_input_pressed(iptkey, IPT_UI_DOWN, 6))
{
- if (show_left_panel() && m_focus == focused_menu::LEFT)
+ if (m_focus == focused_menu::LEFT)
{
- return;
+ // swallow it
+ iptkey = IPT_INVALID;
+ if ((m_left_item_count - 1) > m_filter_highlight)
+ {
+ ++m_filter_highlight;
+ updated = true;
+ }
}
- else if (show_right_panel() && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM)))
+ else if ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))
{
+ // swallow it
+ iptkey = IPT_INVALID;
+ updated = true;
m_topline_datsview++;
- return;
}
else if (is_last_selected() || selected_index() == m_available_items - 1 || m_ui_error)
{
- return;
+ return updated;
+ }
+ else
+ {
+ set_selected_index(selected_index() + 1);
+ if (selected_index() == top_line + m_visible_items + (top_line != 0))
+ top_line++;
}
-
- set_selected_index(selected_index() + 1);
- if (selected_index() == top_line + m_visible_items + (top_line != 0))
- top_line++;
}
// page up backs up by m_visible_items
if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_UP, 6))
{
- // Infos
- if (show_right_panel() && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM)))
+ if (m_focus == focused_menu::LEFT)
+ {
+ // Filters - swallow it
+ iptkey = IPT_INVALID;
+ if (!left_at_top())
+ {
+ updated = true;
+ m_left_visible_top -= std::min(std::max(m_left_visible_lines - 3, 1), m_left_visible_top);
+ m_filter_highlight = std::min(m_left_visible_top + m_left_visible_lines - 2, m_filter_highlight);
+ }
+ }
+ else if ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))
{
+ // Infos - swallow it
+ iptkey = IPT_INVALID;
+ updated = true;
m_topline_datsview -= m_right_visible_lines - 3;
- return;
}
-
- if (selected_index() < m_available_items && !m_ui_error)
+ else if (selected_index() < m_available_items && !m_ui_error)
{
set_selected_index(std::max(selected_index() - m_visible_items, 0));
@@ -1594,14 +2053,25 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
// page down advances by m_visible_items
if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_DOWN, 6))
{
- // Infos
- if (show_right_panel() && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM)))
+ if (m_focus == focused_menu::LEFT)
+ {
+ // Filters - swallow it
+ iptkey = IPT_INVALID;
+ if (!left_at_bottom())
+ {
+ updated = true;
+ m_left_visible_top += std::min(std::max(m_left_visible_lines - 3, 1), m_left_item_count - m_left_visible_lines - m_left_visible_top);
+ m_filter_highlight = std::max(m_left_visible_top + 1, m_filter_highlight);
+ }
+ }
+ else if ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))
{
+ // Infos - swallow it
+ iptkey = IPT_INVALID;
+ updated = true;
m_topline_datsview += m_right_visible_lines - 3;
- return;
}
-
- if (selected_index() < m_available_items && !m_ui_error)
+ else if (selected_index() < m_available_items && !m_ui_error)
{
set_selected_index(std::min(selected_index() + m_visible_lines - 2 + (selected_index() == 0), m_available_items - 1));
@@ -1612,49 +2082,77 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
// home goes to the start
if (exclusive_input_pressed(iptkey, IPT_UI_HOME, 0))
{
- if (show_left_panel() && m_focus == focused_menu::LEFT)
+ if (m_focus == focused_menu::LEFT)
{
- return;
+ // Filters - swallow it
+ iptkey = IPT_INVALID;
+ if (m_filter_highlight)
+ {
+ updated = true;
+ m_left_visible_top = 0;
+ m_filter_highlight = 0;
+ }
}
- else if (show_right_panel() && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM)))
+ else if ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))
{
+ // Infos - swallow it
+ iptkey = IPT_INVALID;
+ if (m_topline_datsview)
+ updated = true;
m_topline_datsview = 0;
- return;
}
-
- if (selected_index() < m_available_items && !m_ui_error)
+ else if (selected_index() < m_available_items && !m_ui_error)
+ {
select_first_item();
+ }
}
// end goes to the last
if (exclusive_input_pressed(iptkey, IPT_UI_END, 0))
{
- if (show_left_panel() && m_focus == focused_menu::LEFT)
+ if (m_focus == focused_menu::LEFT)
{
- return;
+ // Filters - swallow it
+ iptkey = IPT_INVALID;
+ if ((m_left_item_count - 1) != m_filter_highlight)
+ {
+ updated = true;
+ if (!left_at_bottom())
+ m_left_visible_top = m_left_item_count - m_left_visible_lines;
+ m_filter_highlight = m_left_item_count - 1;
+ }
}
- else if (show_right_panel() && ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM)))
+ else if ((m_focus == focused_menu::RIGHTTOP) || (m_focus == focused_menu::RIGHTBOTTOM))
{
+ // Infos - swallow it
+ iptkey = IPT_INVALID;
+ updated = true;
m_topline_datsview = m_total_lines;
- return;
}
-
- if (selected_index() < m_available_items && !m_ui_error)
+ else if (selected_index() < m_available_items && !m_ui_error)
+ {
set_selected_index(top_line = m_available_items - 1);
+ }
}
// focus next rotates throw targets forward
if (exclusive_input_pressed(iptkey, IPT_UI_FOCUS_NEXT, 12))
{
if (!m_ui_error)
+ {
rotate_focus(1);
+ updated = true;
+ }
}
// focus next rotates throw targets forward
if (exclusive_input_pressed(iptkey, IPT_UI_FOCUS_PREV, 12))
{
if (!m_ui_error)
+ {
rotate_focus(-1);
+ updated = true;
+ }
}
// handle a toggle cheats request
@@ -1691,238 +2189,942 @@ void menu_select_launch::handle_keys(u32 flags, int &iptkey)
break;
}
}
+ return updated;
}
//-------------------------------------------------
-// handle input events for main menu
+// handle pointer input for main menu
//-------------------------------------------------
-void menu_select_launch::handle_events(u32 flags, event &ev)
+std::tuple<int, bool, bool> menu_select_launch::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- if (m_pressed)
+ if (ui_event::type::POINTER_ABORT == uievt.event_type)
+ return std::make_tuple(IPT_INVALID, false, false);
+
+ // if nothing's happening, check for clicks
+ if (pointer_idle())
{
- bool const pressed = mouse_pressed();
- s32 target_x, target_y;
- bool button;
- render_target *const mouse_target = machine().ui_input().find_mouse(&target_x, &target_y, &button);
- if (mouse_target && button && (hover() == HOVER_ARROW_DOWN || hover() == HOVER_ARROW_UP))
- {
- if (pressed)
- machine().ui_input().push_mouse_down_event(mouse_target, target_x, target_y);
- }
+ if ((uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
+ return handle_primary_down(changed, uievt);
+ else if ((uievt.pointer_pressed & 0x02) && !(uievt.pointer_buttons & ~u32(0x02)))
+ return handle_right_down(changed, uievt);
+ else if ((uievt.pointer_pressed & 0x04) && !(uievt.pointer_buttons & ~u32(0x04)))
+ return handle_middle_down(changed, uievt);
else
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
+
+ // handle in-progress actions
+ switch (m_pointer_action)
+ {
+ case pointer_action::NONE:
+ break;
+ case pointer_action::MAIN_TRACK_LINE:
+ return update_main_track_line(changed, uievt);
+ case pointer_action::MAIN_TRACK_RBUTTON:
+ return update_main_track_rbutton(changed, uievt);
+ case pointer_action::MAIN_DRAG:
+ return update_main_drag(changed, uievt);
+ case pointer_action::LEFT_TRACK_LINE:
+ return update_left_track_line(changed, uievt);
+ case pointer_action::LEFT_DRAG:
+ return update_left_drag(changed, uievt);
+ case pointer_action::RIGHT_TRACK_TAB:
+ return update_right_track_tab(changed, uievt);
+ case pointer_action::RIGHT_TRACK_ARROW:
+ return update_right_track_arrow(changed, uievt);
+ case pointer_action::RIGHT_TRACK_LINE:
+ return update_right_track_line(changed, uievt);
+ case pointer_action::RIGHT_SWITCH:
+ return update_right_switch(changed, uievt);
+ case pointer_action::RIGHT_DRAG:
+ return update_right_drag(changed, uievt);
+ case pointer_action::TOOLBAR_TRACK:
+ return update_toolbar_track(changed, uievt);
+ case pointer_action::DIVIDER_TRACK:
+ return update_divider_track(changed, uievt);
+ }
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+
+//-------------------------------------------------
+// handle primary click
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::handle_primary_down(bool changed, ui_event const &uievt)
+{
+ if (m_ui_error)
+ {
+ m_ui_error = false;
+ m_error_text.clear();
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+
+ auto const [x, y] = pointer_location();
+
+ // check main item list
+ if ((x >= m_primary_items_hbounds.first) && (x < m_primary_items_hbounds.second) && (y > m_primary_items_top))
+ {
+ int const line((y - m_primary_items_top) / line_height());
+ if (line < (m_primary_lines + m_skip_main_items))
{
- reset_pressed();
+ int key(IPT_INVALID);
+ m_pointer_action = pointer_action::MAIN_TRACK_LINE;
+ m_base_pointer = m_last_pointer = std::make_pair(x, y);
+ m_clicked_line = line;
+ if (is_main_up_arrow(line))
+ {
+ // top line is a scroll arrow
+ --top_line;
+ if (main_force_visible_selection())
+ key = IPT_CUSTOM; // stop processing events so the info pane can be rebuilt
+ if (!main_at_top())
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ else
+ m_pointer_action = pointer_action::NONE;
+ }
+ else if (is_main_down_arrow(line))
+ {
+ // bottom line is a scroll arrow
+ ++top_line;
+ if (main_force_visible_selection())
+ key = IPT_CUSTOM; // stop processing events so the info pane can be rebuilt
+ if (!main_at_bottom())
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ else
+ m_pointer_action = pointer_action::NONE;
+ }
+ return std::make_tuple(key, true, true);
}
}
- // loop while we have interesting events
- bool stop(false), search_changed(false);
- ui_event local_menu_event;
- while (!stop && machine().ui_input().pop_event(&local_menu_event))
+ // check filter list
+ if (show_left_panel() && (x >= m_left_items_hbounds.first) && (x < m_left_items_hbounds.second) && (y >= m_left_items_top))
{
- switch (local_menu_event.event_type)
+ int const line((y - m_left_items_top) / m_info_line_height);
+ if (line < m_left_visible_lines)
{
- // if we are hovering over a valid item, select it with a single click
- case ui_event::type::MOUSE_DOWN:
- if (m_ui_error)
+ m_pointer_action = pointer_action::LEFT_TRACK_LINE;
+ m_base_pointer = m_last_pointer = std::make_pair(x, y);
+ m_clicked_line = line;
+ if (is_left_up_arrow(line))
{
- ev.iptkey = IPT_OTHER;
- stop = true;
+ // top line is a scroll arrow
+ --m_left_visible_top;
+ m_filter_highlight = std::min(m_left_visible_top + m_left_visible_lines - 2, m_filter_highlight);
+ if (!left_at_top())
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ else
+ m_pointer_action = pointer_action::NONE;
+ }
+ else if (is_left_down_arrow(line))
+ {
+ // bottom line is a scroll arrow
+ ++m_left_visible_top;
+ m_filter_highlight = std::max(m_left_visible_top + 1, m_filter_highlight);
+ if (!left_at_bottom())
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ else
+ m_pointer_action = pointer_action::NONE;
}
else
{
- if (hover() >= 0 && hover() < item_count())
- {
- if (hover() >= m_available_items - 1 && selected_index() < m_available_items)
- m_prev_selected = get_selection_ref();
- set_selected_index(hover());
- m_focus = focused_menu::MAIN;
- }
- else if (hover() == HOVER_ARROW_UP)
- {
- set_selected_index(std::max(selected_index() - m_visible_items, 0));
- top_line -= m_visible_items - (top_line + m_visible_lines == m_available_items);
- set_pressed();
- }
- else if (hover() == HOVER_ARROW_DOWN)
- {
- set_selected_index(std::min(selected_index() + m_visible_lines - 2 + (selected_index() == 0), m_available_items - 1));
- top_line += m_visible_lines - 2;
- set_pressed();
- }
- else if (hover() == HOVER_UI_RIGHT)
- ev.iptkey = IPT_UI_RIGHT;
- else if (hover() == HOVER_UI_LEFT)
- ev.iptkey = IPT_UI_LEFT;
- else if (hover() == HOVER_DAT_DOWN)
- m_topline_datsview += m_right_visible_lines - 3;
- else if (hover() == HOVER_DAT_UP)
- m_topline_datsview -= m_right_visible_lines - 3;
- else if (hover() == HOVER_LPANEL_ARROW)
+ // ignore multi-click actions - someone hammered on the scroll arrow and hit the end
+ if (1 < uievt.pointer_clicks)
+ m_pointer_action = pointer_action::NONE;
+ }
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+
+ // check right panel content
+ if (show_right_panel())
+ {
+ // check right tabs
+ if ((x >= right_panel_left()) && (x < right_panel_right()) && (y >= m_primary_vbounds.first) && (y < m_right_tabs_bottom))
+ {
+ int const tab((x - right_panel_left()) / right_tab_width());
+ if (tab != m_right_panel)
+ {
+ m_pointer_action = pointer_action::RIGHT_TRACK_TAB;
+ m_clicked_line = tab;
+ }
+ return std::make_tuple(IPT_INVALID, true, pointer_action::NONE != m_pointer_action);
+ }
+
+ if ((x >= m_right_content_hbounds.first) && (x < m_right_content_hbounds.second))
+ {
+ // check right panel heading arrows
+ if ((ui_event::pointer::TOUCH != uievt.pointer_type) && (y >= right_arrows_top()) && (y < right_arrows_bottom()))
+ {
+ if ((x >= (m_right_content_hbounds.first + lr_border())) && (x < (m_right_content_hbounds.first + lr_border() + lr_arrow_width())))
{
- if (get_focus() == focused_menu::LEFT)
+ auto const [updated, notend] = previous_right_panel_view();
+ if (notend)
{
- set_focus(focused_menu::MAIN);
- select_prev();
+ m_pointer_action = pointer_action::RIGHT_TRACK_ARROW;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(600);
+ m_base_pointer = m_last_pointer = std::make_pair(x, y);
+ m_clicked_line = 0;
}
-
- m_panels_status ^= HIDE_LEFT_PANEL;
+ return std::make_tuple(IPT_INVALID, true, updated);
}
- else if (hover() == HOVER_RPANEL_ARROW)
+ else if ((x >= (m_right_content_hbounds.second - lr_border() - lr_arrow_width())) && (x < (m_right_content_hbounds.second - lr_border())))
{
- if ((get_focus() == focused_menu::RIGHTTOP) || (get_focus() == focused_menu::RIGHTBOTTOM))
+ auto const [updated, notend] = next_right_panel_view();
+ if (notend)
{
- set_focus(focused_menu::MAIN);
- select_prev();
+ m_pointer_action = pointer_action::RIGHT_TRACK_ARROW;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(600);
+ m_base_pointer = m_last_pointer = std::make_pair(x, y);
+ m_clicked_line = 1;
}
+ return std::make_tuple(IPT_INVALID, true, updated);
+ }
+ }
- m_panels_status ^= HIDE_RIGHT_PANEL;
+ // check right panel heading touch swipe
+ if ((ui_event::pointer::TOUCH == uievt.pointer_type) && (y >= m_right_heading_top) && (y < (m_right_heading_top + line_height())))
+ {
+ m_pointer_action = pointer_action::RIGHT_SWITCH;
+ m_base_pointer = m_last_pointer = std::make_pair(x, y);
+ m_clicked_line = 0;
+ if (right_panel() == RP_IMAGES)
+ {
+ m_clicked_line = m_image_view;
}
- else if (hover() == HOVER_B_FAV)
+ else if (right_panel() == RP_INFOS)
{
- ev.iptkey = IPT_UI_FAVORITES;
- stop = true;
+ ui_software_info const *software;
+ ui_system_info const *system;
+ get_selection(software, system);
+ if (software && !software->startempty)
+ m_clicked_line = ui_globals::cur_sw_dats_view;
+ else if (system || (software && software->driver))
+ m_clicked_line = ui_globals::curdats_view;
}
- else if (hover() == HOVER_B_EXPORT)
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+
+ // check info scroll
+ if ((RP_INFOS == m_right_panel) && (y >= m_right_content_vbounds.first) && (y < (m_right_content_vbounds.first + (float(m_right_visible_lines) * m_info_line_height))))
+ {
+ int const line((y - m_right_content_vbounds.first) / m_info_line_height);
+ if (line < m_right_visible_lines)
{
- inkey_export();
- stop = true;
+ bool redraw(false);
+ m_base_pointer = m_last_pointer = std::make_pair(x, y);
+ m_clicked_line = line;
+ if (!line && !info_at_top())
+ {
+ redraw = true;
+ --m_topline_datsview;
+ if (!info_at_top())
+ {
+ m_pointer_action = pointer_action::RIGHT_TRACK_LINE;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ }
+ }
+ else if ((line == (m_right_visible_lines - 1)) && !info_at_bottom())
+ {
+ redraw = true;
+ ++m_topline_datsview;
+ if (!info_at_bottom())
+ {
+ m_pointer_action = pointer_action::RIGHT_TRACK_LINE;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ }
+ }
+ else if (ui_event::pointer::TOUCH == uievt.pointer_type)
+ {
+ m_pointer_action = pointer_action::RIGHT_DRAG;
+ m_clicked_line = m_topline_datsview;
+ }
+ return std::make_tuple(IPT_INVALID, true, redraw);
}
- else if (hover() == HOVER_B_AUDIT)
+ }
+ }
+ }
+
+ // check toolbar
+ if ((y >= m_toolbar_button_vbounds.first) && (y < m_toolbar_button_vbounds.second))
+ {
+ if ((x >= m_toolbar_backtrack_left) && (x < (m_toolbar_backtrack_left + m_toolbar_button_width)))
+ {
+ m_pointer_action = pointer_action::TOOLBAR_TRACK;
+ m_clicked_line = -1;
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else
+ {
+ unsigned const toolbar_count(m_is_swlist ? std::size(SW_TOOLBAR_BITMAPS) : std::size(SYS_TOOLBAR_BITMAPS));
+ float const button(std::floor((x - m_toolbar_main_left) / m_toolbar_button_spacing));
+ int const n(button);
+ if ((n >= 0) && (n < toolbar_count) && (x < (m_toolbar_main_left + (button * m_toolbar_button_spacing) + m_toolbar_button_width)))
+ {
+ m_pointer_action = pointer_action::TOOLBAR_TRACK;
+ m_clicked_line = n;
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+ }
+
+ // check dividers
+ if ((y >= m_primary_vbounds.first) && (y < m_primary_vbounds.second))
+ {
+ if ((x >= left_divider_left()) && (x < left_divider_right()))
+ {
+ m_pointer_action = pointer_action::DIVIDER_TRACK;
+ m_clicked_line = 0;
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if ((x >= right_divider_left()) && (x < right_divider_right()))
+ {
+ m_pointer_action = pointer_action::DIVIDER_TRACK;
+ m_clicked_line = 1;
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+
+//-------------------------------------------------
+// handle right click
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::handle_right_down(bool changed, ui_event const &uievt)
+{
+ if (m_ui_error)
+ return std::make_tuple(IPT_INVALID, false, false);
+
+ // check main item list
+ auto const [x, y] = pointer_location();
+ if ((x >= m_primary_items_hbounds.first) && (x < m_primary_items_hbounds.second) && (y > m_primary_items_top))
+ {
+ int const line((y - m_primary_items_top) / line_height());
+ if ((line < m_primary_lines) && !is_main_up_arrow(line) && (!is_main_down_arrow(line)))
+ {
+ m_pointer_action = pointer_action::MAIN_TRACK_RBUTTON;
+ m_base_pointer = m_last_pointer = std::make_pair(x, y);
+ m_clicked_line = line;
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+
+//-------------------------------------------------
+// handle middle click
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::handle_middle_down(bool changed, ui_event const &uievt)
+{
+ if (m_ui_error)
+ return std::make_tuple(IPT_INVALID, false, false);
+
+ auto const [x, y] = pointer_location();
+ if ((y >= m_primary_vbounds.first) && (y < m_primary_vbounds.second))
+ {
+ if ((x >= left_divider_right()) && (x < right_divider_left()))
+ {
+ // main list
+ if (m_skip_main_items && (y >= (m_primary_items_top + ((float(m_primary_lines) + ((item(m_available_items).type() == menu_item_type::SEPARATOR) ? 0.5F : 0.0F)) * line_height()))))
+ {
+ if (selected_index() < m_available_items)
{
- ev.iptkey = IPT_UI_AUDIT;
- stop = true;
+ m_prev_selected = get_selection_ref();
+ set_selected_index(m_available_items + 1);
}
- else if (hover() == HOVER_B_DATS)
+ }
+ else
+ {
+ if ((get_focus() != focused_menu::MAIN) || (selected_index() > m_available_items))
+ select_prev();
+ }
+ set_focus(focused_menu::MAIN);
+ }
+ else if ((x >= left_panel_left()) && (x < left_panel_right()))
+ {
+ // left panel
+ assert(show_left_panel());
+ if ((get_focus() == focused_menu::MAIN) && (selected_index() <= m_available_items))
+ m_prev_selected = get_selection_ref();
+ set_focus(focused_menu::LEFT);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if ((x >= right_panel_left()) && (x < right_panel_right()))
+ {
+ // right panel
+ assert(show_right_panel());
+ if ((get_focus() == focused_menu::MAIN) && (selected_index() <= m_available_items))
+ m_prev_selected = get_selection_ref();
+ set_focus((y < m_right_tabs_bottom) ? focused_menu::RIGHTTOP : focused_menu::RIGHTBOTTOM);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+
+//-------------------------------------------------
+// track click on main item
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_main_track_line(bool changed, ui_event const &uievt)
+{
+ auto const [x, y] = pointer_location();
+ float const itemtop(m_primary_items_top + (float(m_clicked_line) * line_height()));
+ float const itembottom(m_primary_items_top + (float(m_clicked_line + 1) * line_height()));
+ int key(IPT_INVALID);
+ bool redraw(false);
+
+ if (is_main_up_arrow(m_clicked_line) || is_main_down_arrow(m_clicked_line))
+ {
+ // top or bottom line is a scroll arrow
+ bool const reentered(reentered_rect(m_last_pointer.first, m_last_pointer.second, x, y, m_primary_items_hbounds.first, itemtop, m_primary_items_hbounds.second, itembottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (now >= m_scroll_repeat)
+ {
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
+ if (!m_clicked_line)
{
- inkey_dats();
- stop = true;
+ --top_line;
+ if (main_at_top())
+ m_pointer_action = pointer_action::NONE;
}
- else if (hover() == HOVER_BACKTRACK)
+ else
{
- ev.iptkey = IPT_UI_BACK;
- stack_pop();
- if (is_special_main_menu())
- machine().schedule_exit();
- stop = true;
+ ++top_line;
+ if (main_at_bottom())
+ m_pointer_action = pointer_action::NONE;
}
- else if (hover() >= HOVER_RP_FIRST && hover() <= HOVER_RP_LAST)
+ if (main_force_visible_selection())
+ key = IPT_CUSTOM; // stop processing events so the info pane can be rebuilt
+ redraw = true;
+ }
+ }
+ }
+ else
+ {
+ // check for conversion to touch scroll
+ if (ui_event::pointer::TOUCH == uievt.pointer_type)
+ {
+ auto const [h, v] = check_drag_conversion(x, y, m_base_pointer.first, m_base_pointer.second, line_height());
+ if (h || (v && (m_clicked_line >= m_primary_lines)))
+ {
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, false, true);
+ }
+ else if (v)
+ {
+ m_pointer_action = pointer_action::MAIN_DRAG;
+ m_last_pointer = m_base_pointer;
+ m_clicked_line = top_line;
+ return update_main_drag(changed, uievt);
+ }
+ }
+
+ // check to see if they released over the item
+ if (uievt.pointer_released & 0x01)
+ {
+ m_pointer_action = pointer_action::NONE;
+ if ((0 < uievt.pointer_clicks) || ((x >= m_primary_items_hbounds.first) && (x < m_primary_items_hbounds.second) && (y >= itemtop) && (y < itembottom)))
+ {
+ if (m_clicked_line < m_visible_lines)
{
- m_right_panel = (HOVER_RP_FIRST - hover()) * (-1);
- stop = true;
+ // systems or software items are always selectable
+ if (2 == uievt.pointer_clicks)
+ key = IPT_UI_SELECT;
+ else if (selected_index() != (m_clicked_line + top_line))
+ key = IPT_CUSTOM; // stop processing events so the info pane can be rebuilt
+ set_selected_index(m_clicked_line + top_line);
+ set_focus(focused_menu::MAIN);
}
- else if (hover() >= HOVER_FILTER_FIRST && hover() <= HOVER_FILTER_LAST)
+ else if ((m_clicked_line >= m_primary_lines) && (m_clicked_line < (m_primary_lines + m_skip_main_items)))
{
- m_filter_highlight = hover() - HOVER_FILTER_FIRST;
- filter_selected();
- stop = true;
+ // need to ensure this is a selectable item
+ int const itemnum(m_available_items + m_clicked_line - m_primary_lines);
+ if (is_selectable(item(itemnum)))
+ {
+ if (selected_index() < m_available_items)
+ m_prev_selected = get_selection_ref();
+ set_selected_index(itemnum);
+ set_focus(focused_menu::MAIN);
+ if (2 == uievt.pointer_clicks)
+ key = IPT_UI_SELECT;
+ }
}
}
- break;
+ return std::make_tuple(key, false, true);
+ }
+ }
- // if we are hovering over a valid item, fake a UI_SELECT with a double-click
- case ui_event::type::MOUSE_DOUBLE_CLICK:
- if (hover() >= 0 && hover() < item_count())
- {
- set_selected_index(hover());
- ev.iptkey = IPT_UI_SELECT;
- }
- stop = true;
- break;
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ if (pointer_action::NONE != m_pointer_action)
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(key, pointer_action::NONE != m_pointer_action, redraw);
+}
- // caught scroll event
- case ui_event::type::MOUSE_WHEEL:
- if (hover() >= 0 && hover() < item_count() - m_skip_main_items)
+
+//-------------------------------------------------
+// track right click
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_main_track_rbutton(bool changed, ui_event const &uievt)
+{
+ // see if it was released inside the line
+ if (uievt.pointer_released & 0x02)
+ {
+ m_pointer_action = pointer_action::NONE;
+ auto const [x, y] = pointer_location();
+ float const linetop(m_primary_items_top + (float(m_clicked_line) * line_height()));
+ float const linebottom(m_primary_items_top + (float(m_clicked_line + 1) * line_height()));
+ if ((x >= m_primary_items_hbounds.first) && (x < m_primary_items_hbounds.second) && (y >= linetop) && (y < linebottom))
+ {
+ show_config_menu(m_clicked_line + top_line);
+ return std::make_tuple(IPT_CUSTOM, false, false); // return IPT_CUSTOM to stop processing events
+ }
+ }
+
+ // stop tracking if another button is pressed
+ if (uievt.pointer_pressed & ~u32(0x02))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, pointer_action::DIVIDER_TRACK != m_pointer_action);
+}
+
+
+//-------------------------------------------------
+// track main touch drag scroll
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_main_drag(bool changed, ui_event const &uievt)
+{
+ auto const newtop(drag_scroll(
+ pointer_location().second, m_base_pointer.second, m_last_pointer.second, -line_height(),
+ m_clicked_line, 0, m_available_items - m_primary_lines));
+ bool const moved(newtop != top_line);
+ int key(IPT_INVALID);
+ if (moved)
+ {
+ // scroll and move the selection if necessary to keep it in the visible range
+ top_line = newtop;
+ if (main_force_visible_selection())
+ key = IPT_CUSTOM; // stop processing events so the info pane can be rebuilt
+ }
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(key, pointer_action::NONE != m_pointer_action, moved);
+}
+
+
+//-------------------------------------------------
+// track click on left panel item
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_left_track_line(bool changed, ui_event const &uievt)
+{
+ auto const [x, y] = pointer_location();
+ float const itemtop(m_left_items_top + (float(m_clicked_line) * m_info_line_height));
+ float const itembottom(m_left_items_top + (float(m_clicked_line + 1) * m_info_line_height));
+ bool redraw(false);
+
+ if (is_left_up_arrow(m_clicked_line) || is_left_down_arrow(m_clicked_line))
+ {
+ // top or bottom line is a scroll arrow
+ bool const reentered(reentered_rect(m_last_pointer.first, m_last_pointer.second, x, y, m_left_items_hbounds.first, itemtop, m_left_items_hbounds.second, itembottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (now >= m_scroll_repeat)
{
- if (local_menu_event.zdelta > 0)
+ m_scroll_repeat = now + std::chrono::milliseconds(100);
+ if (!m_clicked_line)
{
- if (selected_index() >= m_available_items || is_first_selected() || m_ui_error)
- break;
- set_selected_index(selected_index() - local_menu_event.num_lines);
- if (selected_index() < top_line + (top_line != 0))
- top_line -= local_menu_event.num_lines;
+ --m_left_visible_top;
+ m_filter_highlight = std::min(m_left_visible_top + m_left_visible_lines - 2, m_filter_highlight);
+ if (left_at_top())
+ m_pointer_action = pointer_action::NONE;
}
else
{
- if (selected_index() >= m_available_items - 1 || m_ui_error)
- break;
- set_selected_index(std::min(selected_index() + local_menu_event.num_lines, m_available_items - 1));
- if (selected_index() >= top_line + m_visible_items + (top_line != 0))
- top_line += local_menu_event.num_lines;
+ ++m_left_visible_top;
+ m_filter_highlight = std::max(m_left_visible_top + 1, m_filter_highlight);
+ if (left_at_bottom())
+ m_pointer_action = pointer_action::NONE;
}
+ redraw = true;
}
- else if (hover() == HOVER_INFO_TEXT)
+ }
+ }
+ else
+ {
+ // check for conversion to touch scroll
+ if (ui_event::pointer::TOUCH == uievt.pointer_type)
+ {
+ auto const [h, v] = check_drag_conversion(x, y, m_base_pointer.first, m_base_pointer.second, m_info_line_height);
+ if (h)
{
- if (local_menu_event.zdelta > 0)
- m_topline_datsview -= local_menu_event.num_lines;
- else
- m_topline_datsview += local_menu_event.num_lines;
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, false, true);
}
- break;
-
- // translate CHAR events into specials
- case ui_event::type::IME_CHAR:
- if (exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_NEXT, 0) || exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_PREV, 0))
+ else if (v)
{
- stop = true;
+ m_pointer_action = pointer_action::LEFT_DRAG;
+ m_last_pointer = m_base_pointer;
+ m_clicked_line = m_left_visible_top;
+ return update_left_drag(changed, uievt);
}
- else if (m_ui_error)
+ }
+
+ // this is a filter - check to see if they released over the item
+ if ((uievt.pointer_released & 0x01) && (x >= m_left_items_hbounds.first) && (x < m_left_items_hbounds.second) && (y >= itemtop) && (y < itembottom))
+ {
+ m_pointer_action = pointer_action::NONE;
+ filter_selected(m_left_visible_top + m_clicked_line);
+ return std::make_tuple(IPT_CUSTOM, false, true); // return IPT_CUSTOM to stop processing events
+ }
+ }
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ if (pointer_action::NONE != m_pointer_action)
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+}
+
+
+//-------------------------------------------------
+// track left panel touch drag scroll
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_left_drag(bool changed, ui_event const &uievt)
+{
+ auto const newtop(drag_scroll(
+ pointer_location().second, m_base_pointer.second, m_last_pointer.second, -m_info_line_height,
+ m_clicked_line, 0, m_left_item_count - m_left_visible_lines));
+ bool const moved(newtop != m_left_visible_top);
+ if (moved)
+ {
+ // scroll and move the selection if necessary to keep it in the visible range
+ m_left_visible_top = newtop;
+ int const first(left_at_top() ? 0 : (newtop + 1));
+ int const last(newtop + m_left_visible_lines - (left_at_bottom() ? 1 : 2));
+ m_filter_highlight = std::clamp(m_filter_highlight, first, last);
+ }
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, moved);
+}
+
+
+//-------------------------------------------------
+// track click on right panel tab
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_right_track_tab(bool changed, ui_event const &uievt)
+{
+ // see if it was released inside the divider
+ if (uievt.pointer_released & 0x01)
+ {
+ m_pointer_action = pointer_action::NONE;
+ auto const [x, y] = pointer_location();
+ float const left(right_panel_left() + (float(m_clicked_line) * right_tab_width()));
+ float const right(right_panel_left() + (float(m_clicked_line + 1) * right_tab_width()));
+ if ((x >= left) && (x < right) && (y >= m_primary_vbounds.first) && (y < m_right_tabs_bottom))
+ {
+ m_right_panel = m_clicked_line;
+ return std::make_tuple(IPT_CUSTOM, false, true); // return IPT_CUSTOM to stop processing events
+ }
+ }
+
+ // stop tracking if another button is pressed
+ if (uievt.pointer_pressed & ~u32(0x01))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, pointer_action::DIVIDER_TRACK != m_pointer_action);
+}
+
+
+//-------------------------------------------------
+// track right panel heading left/right arrows
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_right_track_arrow(bool changed, ui_event const &uievt)
+{
+ auto const [x, y] = pointer_location();
+ float const left(m_clicked_line ? (m_right_content_hbounds.second - lr_border() - lr_arrow_width()) : (m_right_content_hbounds.first + lr_border()));
+ float const right(m_clicked_line ? (m_right_content_hbounds.second - lr_border()) : (m_right_content_hbounds.first + lr_border() + lr_arrow_width()));
+
+ // check for reentry
+ bool redraw(false);
+ bool const reentered(reentered_rect(m_last_pointer.first, m_last_pointer.second, x, y, left, right_arrows_top(), right, right_arrows_bottom()));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (now >= m_scroll_repeat)
+ {
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(200);
+ bool notend;
+ std::tie(redraw, notend) = m_clicked_line ? next_right_panel_view() : previous_right_panel_view();
+ if (!notend)
+ m_pointer_action = pointer_action::NONE;
+ }
+ }
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ if (pointer_action::NONE != m_pointer_action)
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+}
+
+
+//-------------------------------------------------
+// track right scroll arrows
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_right_track_line(bool changed, ui_event const &uievt)
+{
+ auto const [x, y] = pointer_location();
+ float const itemtop(m_right_content_vbounds.first + (float(m_clicked_line) * m_info_line_height));
+ float const itembottom(m_right_content_vbounds.first + (float(m_clicked_line + 1) * m_info_line_height));
+ bool redraw(false);
+
+ bool const reentered(reentered_rect(m_last_pointer.first, m_last_pointer.second, x, y, m_right_content_hbounds.first, itemtop, m_right_content_hbounds.second, itembottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (now >= m_scroll_repeat)
+ {
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
+ if (!m_clicked_line)
{
- ev.iptkey = IPT_SPECIAL;
- stop = true;
+ --m_topline_datsview;
+ if (info_at_top())
+ m_pointer_action = pointer_action::NONE;
}
- else if (accept_search())
+ else
{
- if (input_character(m_search, local_menu_event.ch, uchar_is_printable))
- search_changed = true;
+ ++m_topline_datsview;
+ if (info_at_bottom())
+ m_pointer_action = pointer_action::NONE;
}
- break;
+ redraw = true;
+ }
+ }
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ if (pointer_action::NONE != m_pointer_action)
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+}
+
+
+//-------------------------------------------------
+// track panel heading touch drag switch
+//-------------------------------------------------
- case ui_event::type::MOUSE_RDOWN:
- if (hover() >= 0 && hover() < item_count() - m_skip_main_items)
+std::tuple<int, bool, bool> menu_select_launch::update_right_switch(bool changed, ui_event const &uievt)
+{
+ // get new page
+ ui_software_info const *software;
+ ui_system_info const *system;
+ get_selection(software, system);
+ int const min((RP_IMAGES == m_right_panel) ? FIRST_VIEW : 0);
+ int const max(
+ (RP_IMAGES == m_right_panel) ? LAST_VIEW :
+ (software && !software->startempty) ? (ui_globals::cur_sw_dats_total - 1) :
+ (system || (software && software->driver)) ? (ui_globals::curdats_total - 1) :
+ 0);
+ auto const newpage(drag_scroll(
+ pointer_location().first, m_base_pointer.first, m_last_pointer.first, 0.125F * (m_right_content_hbounds.first - m_right_content_hbounds.second),
+ m_clicked_line, min, max));
+
+ // switch page
+ u8 dummy(newpage);
+ u8 &current(
+ (RP_IMAGES == m_right_panel) ? m_image_view :
+ (software && !software->startempty) ? ui_globals::cur_sw_dats_view :
+ (system || (software && software->driver)) ? ui_globals::curdats_view :
+ dummy);
+ bool const redraw(newpage != current);
+ if (redraw)
+ {
+ current = newpage;
+ if (RP_IMAGES == m_right_panel)
+ set_switch_image();
+ else
+ m_topline_datsview = 0;
+ }
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+}
+
+
+//-------------------------------------------------
+// track right panel touch drag scroll
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_right_drag(bool changed, ui_event const &uievt)
+{
+ auto const newtop(drag_scroll(
+ pointer_location().second, m_base_pointer.second, m_last_pointer.second, -m_info_line_height,
+ m_clicked_line, 0, m_total_lines - m_right_visible_lines));
+ bool const moved(newtop != m_topline_datsview);
+ m_topline_datsview = newtop;
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, moved);
+}
+
+
+//-------------------------------------------------
+// track click on toolbar button
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_toolbar_track(bool changed, ui_event const &uievt)
+{
+ // see if it was released inside the button
+ if (uievt.pointer_released & 0x01)
+ {
+ m_pointer_action = pointer_action::NONE;
+ auto const [x, y] = pointer_location();
+ float const left((0 > m_clicked_line) ? m_toolbar_backtrack_left : (m_toolbar_main_left + (float(m_clicked_line) * m_toolbar_button_spacing)));
+ if ((x >= left) && (x < (left + m_toolbar_button_width)) && (y >= m_toolbar_button_vbounds.first) && (y < m_toolbar_button_vbounds.second))
+ {
+ if (0 > m_clicked_line)
{
- set_selected_index(hover());
- m_prev_selected = get_selection_ref();
- m_focus = focused_menu::MAIN;
- ev.iptkey = IPT_CUSTOM;
- ev.mouse.x0 = local_menu_event.mouse_x;
- ev.mouse.y0 = local_menu_event.mouse_y;
- stop = true;
+ // backtrack button
+ stack_pop();
+ if (is_special_main_menu())
+ machine().schedule_exit();
+ return std::make_tuple(IPT_UI_BACK, false, true);
+ }
+ else
+ {
+ // main buttons
+ auto const *const toolbar_bitmaps(m_is_swlist ? SW_TOOLBAR_BITMAPS : SYS_TOOLBAR_BITMAPS);
+ auto const [bitmap, action, need_selection] = toolbar_bitmaps[m_clicked_line];
+ switch (action)
+ {
+ case IPT_UI_EXPORT:
+ inkey_export();
+ break;
+ case IPT_UI_DATS:
+ inkey_dats();
+ break;
+ default:
+ return std::make_tuple(action, false, true);
+ }
+ return std::make_tuple(IPT_CUSTOM, false, true); // return IPT_CUSTOM to stop processing events
}
- break;
-
- // ignore everything else
- default:
- break;
}
+ }
- // need to update search before processing certain kinds of events, but others don't matter
- if (search_changed)
+ // stop tracking if another button is pressed
+ if (uievt.pointer_pressed & ~u32(0x01))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, pointer_action::TOOLBAR_TRACK != m_pointer_action);
+}
+
+
+//-------------------------------------------------
+// track click on divider
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_divider_track(bool changed, ui_event const &uievt)
+{
+ // see if it was released inside the divider
+ if (uievt.pointer_released & 0x01)
+ {
+ m_pointer_action = pointer_action::NONE;
+ auto const [x, y] = pointer_location();
+ float const left(m_clicked_line ? right_divider_left() : left_divider_left());
+ float const right(m_clicked_line ? right_divider_right() : left_divider_right());
+ if ((x >= left) && (x < right) && (y >= m_primary_vbounds.first) && (y < m_primary_vbounds.second))
{
- switch (machine().ui_input().peek_event_type())
+ if (m_clicked_line)
{
- case ui_event::type::MOUSE_DOWN:
- case ui_event::type::MOUSE_RDOWN:
- case ui_event::type::MOUSE_DOUBLE_CLICK:
- case ui_event::type::MOUSE_WHEEL:
- stop = true;
- break;
- case ui_event::type::NONE:
- case ui_event::type::WINDOW_FOCUS:
- case ui_event::type::WINDOW_DEFOCUS:
- case ui_event::type::MOUSE_MOVE:
- case ui_event::type::MOUSE_LEAVE:
- case ui_event::type::MOUSE_UP:
- case ui_event::type::MOUSE_RUP:
- case ui_event::type::IME_CHAR:
- break;
+ if ((get_focus() == focused_menu::RIGHTTOP) || (get_focus() == focused_menu::RIGHTBOTTOM))
+ {
+ set_focus(focused_menu::MAIN);
+ select_prev();
+ }
+ m_panels_status ^= HIDE_RIGHT_PANEL;
}
+ else
+ {
+ if (get_focus() == focused_menu::LEFT)
+ {
+ set_focus(focused_menu::MAIN);
+ select_prev();
+ }
+ m_panels_status ^= HIDE_LEFT_PANEL;
+ }
+ return std::make_tuple(IPT_CUSTOM, false, true); // return IPT_CUSTOM to stop processing events
}
}
- if (search_changed)
- reset(reset_options::SELECT_FIRST);
+
+ // stop tracking if another button is pressed
+ if (uievt.pointer_pressed & ~u32(0x01))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, pointer_action::DIVIDER_TRACK != m_pointer_action);
+}
+
+
+//-------------------------------------------------
+// move selection to visible range
+//-------------------------------------------------
+
+bool menu_select_launch::main_force_visible_selection()
+{
+ int const first(main_at_top() ? 0 : (top_line + 1));
+ int const last(top_line + m_primary_lines - (main_at_bottom() ? 1 : 2));
+ if (selected_index() < m_available_items)
+ {
+ int const restricted(std::clamp(selected_index(), first, last));
+ if (selected_index() != restricted)
+ {
+ set_selected_index(restricted);
+ return true;
+ }
+ }
+ else
+ {
+ int selection(0);
+ while ((m_available_items > selection) && (item(selection).ref() != m_prev_selected))
+ ++selection;
+ auto const ref(item(std::clamp(selection, first, last)).ref());
+ if (ref != m_prev_selected)
+ {
+ m_prev_selected = ref;
+ return true;
+ }
+ }
+ return false;
}
@@ -1932,57 +3134,22 @@ void menu_select_launch::handle_events(u32 flags, event &ev)
void menu_select_launch::draw(u32 flags)
{
- bool noinput = (flags & PROCESS_NOINPUT);
- float const icon_offset = m_has_icons ? (1.5f * ud_arrow_width()) : 0.0f;
- float right_panel_size = show_right_panel() ? 0.3f : m_divider_width;
- float visible_width = 1.0f - 4.0f * lr_border();
- float primary_left = (1.0f - visible_width) * 0.5f;
- float primary_width = visible_width;
- draw_background();
+ // recompute height of primary menu area if necessary
+ if (m_primary_vbounds.first >= m_primary_vbounds.second)
+ {
+ float const pixelheight(target_size().second);
+ float const lines(std::floor((1.0F - (4.0F * tb_border()) - get_customtop() - get_custombottom()) / line_height()));
+ float const itemsheight(line_height() * lines);
+ float const space(1.0F - itemsheight - get_customtop() - get_custombottom());
+ m_primary_items_top = std::round((get_customtop() + (0.5F * space)) * pixelheight) / pixelheight;
+ m_primary_vbounds = std::make_pair(m_primary_items_top - tb_border(), m_primary_items_top + itemsheight + tb_border());
+ m_primary_lines = int(lines) - m_skip_main_items;
+ }
- clear_hover();
+ // ensure the selection is visible
m_available_items = item_count() - m_skip_main_items;
- float extra_height = m_skip_main_items * line_height();
- float visible_extra_menu_height = get_customtop() + get_custombottom() + extra_height;
-
- // locate mouse
- if (noinput)
- ignore_mouse();
- else
- map_mouse();
-
- // account for extra space at the top and bottom
- float visible_main_menu_height = 1.0f - 2.0f * tb_border() - visible_extra_menu_height;
- m_visible_lines = int(std::trunc(visible_main_menu_height / line_height()));
- visible_main_menu_height = float(m_visible_lines) * line_height();
-
- // compute top/left of inner menu area by centering
- float visible_left = primary_left;
- float visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f;
-
- // if the menu is at the bottom of the extra, adjust
- visible_top += get_customtop();
-
- // compute left box size
- float x1 = visible_left - lr_border();
- float y1 = visible_top - tb_border();
- float x2 = x1 + 2.0f * lr_border();
- float y2 = visible_top + visible_main_menu_height + tb_border() + extra_height;
-
- // add left box
- visible_left = draw_left_panel(x1, y1, x2, y2);
- visible_width -= right_panel_size + visible_left - 2.0f * lr_border();
-
- // compute and add main box
- x1 = visible_left - lr_border();
- x2 = visible_left + visible_width + lr_border();
- float line = visible_top + (float(m_visible_lines) * line_height());
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
-
- // make sure the selection
- if (m_available_items < m_visible_lines)
- m_visible_lines = m_available_items;
+ m_visible_lines = (std::min)(m_primary_lines, m_available_items);
int selection;
if (selected_index() < m_available_items)
{
@@ -2007,35 +3174,41 @@ void menu_select_launch::draw(u32 flags)
else if (selection >= (top_line + m_visible_lines - 2))
top_line = selection - m_visible_lines + ((selection == (m_available_items - 1)) ? 1: 2);
}
-
- // determine effective positions taking into account the hilighting arrows
- float effective_width = visible_width - 2.0f * gutter_width();
- float effective_left = visible_left + gutter_width();
-
if ((m_focus == focused_menu::MAIN) && (selected_index() < m_available_items))
m_prev_selected = nullptr;
- int const n_loop = (std::min)(m_visible_lines, m_available_items);
- float const pixelheight = machine().render().ui_target().height();
- for (int linenum = 0; linenum < n_loop; linenum++)
+ // draw background, left and right panels, and outline of main box
+ draw_background();
+ draw_left_panel(flags);
+ draw_right_panel(flags);
+ ui().draw_outlined_box(
+ container(),
+ left_divider_right(), m_primary_vbounds.first, right_divider_left(), m_primary_vbounds.second,
+ ui().colors().background_color());
+
+ // calculate horizontal geometry of main item list
+ m_primary_items_hbounds = std::make_pair(left_divider_right() + lr_border(), right_divider_left() - lr_border());
+ float const item_text_left(m_primary_items_hbounds.first + gutter_width());
+ float const item_text_width(m_primary_items_hbounds.second - m_primary_items_hbounds.first - (2.0F * gutter_width()));
+ float const icon_offset(m_has_icons ? (1.5F * ud_arrow_width()) : 0.0F);
+
+ // draw main scrolling items
+ for (int linenum = 0; linenum < m_visible_lines; linenum++)
{
- float line_y = (std::round(visible_top * pixelheight) / pixelheight) + (float(linenum) * line_height());
- int itemnum = top_line + linenum;
- const menu_item &pitem = item(itemnum);
- const std::string_view itemtext = pitem.text();
+ int const itemnum(top_line + linenum);
+ menu_item const &pitem(item(itemnum));
+ std::string_view const itemtext(pitem.text());
+ float const linetop(m_primary_items_top + (float(linenum) * line_height()));
+ float const linebottom(linetop + line_height());
+
+ // work out colours
rgb_t fgcolor = ui().colors().text_color();
rgb_t bgcolor = ui().colors().text_bg_color();
rgb_t fgcolor3 = ui().colors().clone_color();
- float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
- float line_y0 = line_y;
- float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
- float line_y1 = line_y + line_height();
-
- // set the hover if this is our item
- if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
- set_hover(itemnum);
-
- if (is_selected(itemnum) && m_focus == focused_menu::MAIN)
+ bool const hovered(is_selectable(pitem) && pointer_in_rect(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom));
+ bool const pointerline((pointer_action::MAIN_TRACK_LINE == m_pointer_action) && (linenum == m_clicked_line));
+ bool const rclickline((pointer_action::MAIN_TRACK_RBUTTON == m_pointer_action) && (linenum == m_clicked_line));
+ if (!rclickline && is_selected(itemnum) && (get_focus() == focused_menu::MAIN))
{
// if we're selected, draw with a different background
fgcolor = rgb_t(0xff, 0xff, 0x00);
@@ -2043,173 +3216,177 @@ void menu_select_launch::draw(u32 flags)
fgcolor3 = rgb_t(0xcc, 0xcc, 0x00);
ui().draw_textured_box(
container(),
- line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1,
+ m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom,
bgcolor, rgb_t(43, 43, 43),
hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
- else if (itemnum == hover())
+ else if ((pointerline || rclickline) && hovered)
+ {
+ // draw selected highlight for tracked item
+ fgcolor = fgcolor3 = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
+ }
+ else if (pointerline || rclickline || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered && pointer_idle()))
{
- // else if the mouse is over this item, draw with a different background
- fgcolor = fgcolor3 = ui().options().mouseover_color();
+ // draw hover highlight when hovered over or dragged off
+ fgcolor = fgcolor3 = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
- highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
}
else if (pitem.ref() == m_prev_selected)
{
- fgcolor = fgcolor3 = ui().options().mouseover_color();
+ fgcolor = fgcolor3 = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
- ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43),
+ ui().draw_textured_box(
+ container(),
+ m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom,
+ bgcolor, rgb_t(43, 43, 43),
hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
- if (linenum == 0 && top_line != 0)
+ if ((!linenum && top_line) || ((linenum == (m_visible_lines - 1)) && (itemnum != m_available_items - 1)))
{
- // if we're on the top line, display the up arrow
- draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width(), line_y + 0.25f * line_height(),
- 0.5f * (x1 + x2) + 0.5f * ud_arrow_width(), line_y + 0.75f * line_height(), fgcolor, ROT0);
-
- if (hover() == itemnum)
- set_hover(HOVER_ARROW_UP);
- }
- else if (linenum == m_visible_lines - 1 && itemnum != m_available_items - 1)
- {
- // if we're on the bottom line, display the down arrow
- draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width(), line_y + 0.25f * line_height(),
- 0.5f * (x1 + x2) + 0.5f * ud_arrow_width(), line_y + 0.75f * line_height(), fgcolor, ROT0 ^ ORIENTATION_FLIP_Y);
-
- if (hover() == itemnum)
- set_hover(HOVER_ARROW_DOWN);
+ // if we're on the top or bottom line, display the up or down arrow
+ draw_arrow(
+ 0.5F * (m_primary_items_hbounds.first + m_primary_items_hbounds.second - ud_arrow_width()), linetop + (0.25F * line_height()),
+ 0.5F * (m_primary_items_hbounds.first + m_primary_items_hbounds.second + ud_arrow_width()), linetop + (0.75F * line_height()),
+ fgcolor,
+ linenum ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
}
else if (pitem.type() == menu_item_type::SEPARATOR)
{
// if we're just a divider, draw a line
- container().add_line(visible_left, line_y + 0.5f * line_height(), visible_left + visible_width, line_y + 0.5f * line_height(),
+ container().add_line(
+ m_primary_items_hbounds.first, linetop + (0.5F * line_height()),
+ m_primary_items_hbounds.second, linetop + (0.5F * line_height()),
UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
- else if (pitem.subtext().empty())
- {
- // draw the item centered
- int const item_invert = pitem.flags() & FLAG_INVERT;
- if (m_has_icons)
- draw_icon(linenum, item(itemnum).ref(), effective_left, line_y);
- ui().draw_text_full(
- container(),
- itemtext,
- effective_left + icon_offset, line_y, effective_width - icon_offset,
- text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
- mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
- nullptr, nullptr,
- line_height());
- }
else
{
- int const item_invert = pitem.flags() & FLAG_INVERT;
- std::string_view const subitem_text = pitem.subtext();
- float item_width, subitem_width;
-
- // compute right space for subitem
- subitem_width = get_string_width(subitem_text);
- subitem_width += gutter_width();
-
- // draw the item left-justified
+ bool const item_invert(pitem.flags() & FLAG_INVERT);
if (m_has_icons)
- draw_icon(linenum, item(itemnum).ref(), effective_left, line_y);
- ui().draw_text_full(
- container(),
- itemtext,
- effective_left + icon_offset, line_y, effective_width - icon_offset - subitem_width,
- text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
- mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
- &item_width, nullptr,
- line_height());
-
- // draw the subitem right-justified
- ui().draw_text_full(
- container(),
- subitem_text,
- effective_left + icon_offset + item_width, line_y, effective_width - icon_offset - item_width,
- text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
- nullptr, nullptr,
- line_height());
+ draw_icon(linenum, pitem.ref(), item_text_left, linetop);
+ if (pitem.subtext().empty())
+ {
+ // draw the item left-aligned
+ ui().draw_text_full(
+ container(),
+ itemtext,
+ item_text_left + icon_offset, linetop, item_text_width - icon_offset,
+ text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
+ nullptr, nullptr,
+ line_height());
+ }
+ else
+ {
+ // compute right space for subitem
+ std::string_view const subitem_text(pitem.subtext());
+ float const subitem_width(get_string_width(subitem_text) + gutter_width());
+
+ // draw the item left-aligned
+ float item_width;
+ ui().draw_text_full(
+ container(),
+ itemtext,
+ item_text_left + icon_offset, linetop, item_text_width - icon_offset - subitem_width,
+ text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
+ &item_width, nullptr,
+ line_height());
+
+ // draw the subitem right-aligned
+ ui().draw_text_full(
+ container(),
+ subitem_text,
+ item_text_left + icon_offset + item_width, linetop, item_text_width - icon_offset - item_width,
+ text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER,
+ mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
+ nullptr, nullptr,
+ line_height());
+ }
}
}
- for (size_t count = m_available_items; count < item_count(); count++)
+ // draw extra fixed items
+ for (size_t linenum = 0; linenum < m_skip_main_items; linenum++)
{
- const menu_item &pitem = item(count);
- const std::string_view itemtext = pitem.text();
- float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
- float line_y0 = line;
- float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
- float line_y1 = line + line_height();
+ int const itemnum(m_available_items + linenum);
+ menu_item const &pitem(item(itemnum));
+ std::string_view const itemtext(pitem.text());
+ float const linetop(m_primary_items_top + (float(m_primary_lines + linenum) * line_height()));
+ float const linebottom(linetop + line_height());
+
+ // work out colours
rgb_t fgcolor = ui().colors().text_color();
rgb_t bgcolor = ui().colors().text_bg_color();
-
- if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
- set_hover(count);
-
- // if we're selected, draw with a different background
- if (is_selected(count) && m_focus == focused_menu::MAIN)
+ bool const hovered(is_selectable(pitem) && pointer_in_rect(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom));
+ bool const pointerline((pointer_action::MAIN_TRACK_LINE == m_pointer_action) && ((m_primary_lines + linenum) == m_clicked_line));
+ if (is_selected(itemnum) && (get_focus() == focused_menu::MAIN))
{
+ // if we're selected, draw with a different background
fgcolor = rgb_t(0xff, 0xff, 0x00);
bgcolor = rgb_t(0xff, 0xff, 0xff);
- ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43),
+ ui().draw_textured_box(
+ container(),
+ m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom,
+ bgcolor, rgb_t(43, 43, 43),
hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
- // else if the mouse is over this item, draw with a different background
- else if (count == hover())
+ else if (pointerline && hovered)
{
- fgcolor = ui().options().mouseover_color();
+ // draw selected highlight for tracked item
+ fgcolor = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
+ }
+ else if (pointerline || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered && pointer_idle()))
+ {
+ // draw hover highlight when hovered over or dragged off
+ fgcolor = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
- highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
}
if (pitem.type() == menu_item_type::SEPARATOR)
{
+ // if we're just a divider, draw a line
container().add_line(
- visible_left, line + 0.5f * line_height(),
- visible_left + visible_width, line + 0.5f * line_height(),
- UI_LINE_WIDTH,
- ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ m_primary_items_hbounds.first, linetop + (0.5F * line_height()),
+ m_primary_items_hbounds.second, linetop + (0.5F * line_height()),
+ UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
else
{
+ // draw the item centred
ui().draw_text_full(
container(),
itemtext,
- effective_left, line, effective_width,
+ item_text_left, linetop, item_text_width,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
mame_ui_manager::NORMAL, fgcolor, bgcolor,
nullptr, nullptr,
line_height());
}
- line += line_height();
}
- x1 = x2;
- x2 += right_panel_size;
-
- draw_right_panel(x1, y1, x2, y2);
-
- x1 = primary_left - lr_border();
- x2 = primary_left + primary_width + lr_border();
-
// if there is something special to add, do it by calling the virtual method
- custom_render(get_selection_ref(), get_customtop(), get_custombottom(), x1, y1, x2, y2);
+ custom_render(
+ flags,
+ get_selection_ref(),
+ get_customtop(), get_custombottom(),
+ lr_border(), m_primary_vbounds.first, 1.0F - lr_border(), m_primary_vbounds.second);
- // return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
- m_visible_items = m_visible_lines - (top_line != 0) - (top_line + m_visible_lines != m_available_items);
-
- // noinput
- if (noinput)
+ // show error text if necessary
+ if (m_ui_error)
{
- int alpha = (1.0f - machine().options().pause_brightness()) * 255.0f;
- if (alpha > 255)
- alpha = 255;
- if (alpha >= 0)
- container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(alpha, 0x00, 0x00, 0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_rect(0.0F, 0.0F, 1.0F, 1.0F, rgb_t(114, 0, 0, 0), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ ui().draw_text_box(container(), m_error_text, text_layout::text_justify::CENTER, 0.5f, 0.5f, UI_RED_COLOR);
}
+
+ // return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
+ m_visible_items = m_visible_lines - (top_line != 0) - (top_line + m_visible_lines != m_available_items);
}
@@ -2217,116 +3394,197 @@ void menu_select_launch::draw(u32 flags)
// draw right panel
//-------------------------------------------------
-void menu_select_launch::draw_right_panel(float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::draw_right_panel(u32 flags)
{
- float const x2(!show_right_panel() ? origx2 : (origx1 + m_divider_width));
-
- // set left-right arrows dimension
- float const ar_x0(0.5F * (x2 + origx1 - m_divider_arrow_width));
- float const ar_y0(0.5F * (origy2 + origy1 - m_divider_arrow_height));
- float const ar_x1(ar_x0 + m_divider_arrow_width);
- float const ar_y1(ar_y0 + m_divider_arrow_height);
+ if (show_right_panel())
+ {
+ m_right_panel_width = 0.3F - m_divider_width;
- ui().draw_outlined_box(container(), origx1, origy1, origx2, origy2, rgb_t(0xef, 0x12, 0x47, 0x7b));
+ ui().draw_outlined_box(
+ container(), right_panel_left(), m_primary_vbounds.first, right_panel_right(), m_primary_vbounds.second,
+ ui().colors().background_color());
+ draw_right_box_tabs(flags);
- rgb_t fgcolor(ui().colors().text_color());
- if (mouse_in_rect(origx1, origy1, x2, origy2))
- {
- fgcolor = ui().options().mouseover_color();
- set_hover(HOVER_RPANEL_ARROW);
- }
+ if (0.0F >= m_right_heading_top)
+ {
+ float const pixelheight(target_size().second);
+ m_right_heading_top = std::round((m_right_tabs_bottom + UI_LINE_WIDTH + tb_border()) * pixelheight) / pixelheight;
+ m_right_content_vbounds = std::make_pair(
+ std::round((m_right_heading_top + line_height() + tb_border()) * pixelheight) / pixelheight,
+ m_primary_vbounds.second - tb_border());
+ m_right_content_hbounds = std::make_pair(right_panel_left() + lr_border(), right_panel_right() - lr_border());
+ }
- if (!show_right_panel())
- {
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
+ if (m_right_panel == RP_IMAGES)
+ arts_render(flags);
+ else
+ infos_render(flags);
}
else
{
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90);
- origy1 = draw_right_box_title(x2, origy1, origx2, origy2);
-
- if (m_right_panel == RP_IMAGES)
- arts_render(x2, origy1, origx2, origy2);
- else
- infos_render(x2, origy1, origx2, origy2);
+ m_right_panel_width = 0.0F;
}
+
+ draw_divider(flags, 1.0F - lr_border() - m_right_panel_width - m_divider_width, true);
}
//-------------------------------------------------
-// draw right box title
+// draw right box tabs
//-------------------------------------------------
-float menu_select_launch::draw_right_box_title(float x1, float y1, float x2, float y2)
+void menu_select_launch::draw_right_box_tabs(u32 flags)
{
- float const midl = (x2 - x1) * 0.5f;
+ m_right_tabs_bottom = m_primary_vbounds.first + line_height();
- // add outlined box for options
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ float const x1(1.0F - lr_border() - m_right_panel_width);
+ float const x2(1.0F - lr_border());
+ float const tabwidth = right_tab_width();
- // add separator line
- container().add_line(x1 + midl, y1, x1 + midl, y1 + line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-
- std::string buffer[RP_LAST + 1] = { _(RIGHT_PANEL_NAMES[0].second), _(RIGHT_PANEL_NAMES[1].second) };
+ std::string const buffer[RP_LAST + 1] = { _(RIGHT_PANEL_NAMES[0].second), _(RIGHT_PANEL_NAMES[1].second) };
// check size
float text_size = 1.0f;
for (auto & elem : buffer)
{
auto textlen = get_string_width(elem) + 0.01f;
- float tmp_size = (textlen > midl) ? (midl / textlen) : 1.0f;
+ float tmp_size = (textlen > tabwidth) ? (tabwidth / textlen) : 1.0f;
text_size = std::min(text_size, tmp_size);
}
for (int cells = RP_FIRST; cells <= RP_LAST; ++cells)
{
- rgb_t bgcolor = ui().colors().text_bg_color();
- rgb_t fgcolor = ui().colors().text_color();
-
- if (mouse_in_rect(x1, y1, x1 + midl, y1 + line_height()))
- {
- if (m_right_panel != cells)
- {
- bgcolor = ui().colors().mouseover_bg_color();
- fgcolor = ui().options().mouseover_color();
- set_hover(HOVER_RP_FIRST + cells);
- }
- }
-
- if (m_right_panel != cells)
- {
- container().add_line(x1, y1 + line_height(), x1 + midl, y1 + line_height(), UI_LINE_WIDTH,
- ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- if (fgcolor != ui().colors().mouseover_color())
- fgcolor = ui().colors().clone_color();
- }
+ float const tableft(x1 + (float(cells - RP_FIRST) * tabwidth));
- if (m_focus == focused_menu::RIGHTTOP && m_right_panel == cells)
+ rgb_t fgcolor = ui().colors().text_color();
+ rgb_t bgcolor = ui().colors().text_bg_color();
+ if ((focused_menu::RIGHTTOP == m_focus) && (cells == m_right_panel))
{
+ // draw primary highlight if keyboard focus is here
fgcolor = rgb_t(0xff, 0xff, 0x00);
bgcolor = rgb_t(0xff, 0xff, 0xff);
ui().draw_textured_box(
container(),
- x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height(),
+ tableft + UI_LINE_WIDTH, m_primary_vbounds.first + UI_LINE_WIDTH, tableft + tabwidth - UI_LINE_WIDTH, m_right_tabs_bottom,
bgcolor, rgb_t(43, 43, 43), hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
- else if (bgcolor == ui().colors().mouseover_bg_color())
+ else if (cells != m_right_panel)
{
- container().add_rect(x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height(),
- bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
+ bool const hovered(pointer_in_rect(tableft, m_primary_vbounds.first, tableft + tabwidth, m_right_tabs_bottom));
+ bool const pointertab((pointer_action::RIGHT_TRACK_TAB == m_pointer_action) && (cells == m_clicked_line));
+ if (pointertab && hovered)
+ {
+ // draw selected highlight for tracked item
+ fgcolor = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ highlight(tableft + UI_LINE_WIDTH, m_primary_vbounds.first + UI_LINE_WIDTH, tableft + tabwidth - UI_LINE_WIDTH, m_right_tabs_bottom, bgcolor);
+ }
+ else if (pointertab || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered && pointer_idle()))
+ {
+ // draw hover highlight when hovered over or dragged off
+ fgcolor = ui().colors().mouseover_color();
+ bgcolor = ui().colors().mouseover_bg_color();
+ highlight(tableft + UI_LINE_WIDTH, m_primary_vbounds.first + UI_LINE_WIDTH, tableft + tabwidth - UI_LINE_WIDTH, m_right_tabs_bottom, bgcolor);
+ }
+ else
+ {
+ // dim unselected tab title
+ fgcolor = ui().colors().clone_color();
+ }
}
ui().draw_text_full(
container(),
buffer[cells],
- x1 + UI_LINE_WIDTH, y1, midl - UI_LINE_WIDTH,
+ tableft + UI_LINE_WIDTH, m_primary_vbounds.first, tabwidth - UI_LINE_WIDTH,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr,
line_height() * text_size);
- x1 += midl;
+
+ // add lines when appropriate
+ if (RP_FIRST < cells)
+ {
+ container().add_line(
+ tableft, m_primary_vbounds.first, tableft, m_right_tabs_bottom,
+ UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ if (m_right_panel == cells)
+ {
+ container().add_line(
+ x1, m_primary_vbounds.first + line_height(), tableft, m_right_tabs_bottom,
+ UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+ }
}
+ if (RP_LAST != m_right_panel)
+ {
+ container().add_line(
+ x1 + (float(m_right_panel + 1) * tabwidth), m_right_tabs_bottom, x2, m_right_tabs_bottom,
+ UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+}
+
+
+//-------------------------------------------------
+// draw right box heading
+//-------------------------------------------------
+
+void menu_select_launch::draw_right_box_heading(u32 flags, bool larrow, bool rarrow, std::string_view text)
+{
+ float const text_left(m_right_content_hbounds.first + (2.0F * lr_border()) + lr_arrow_width());
+ float const text_width(m_right_content_hbounds.second - m_right_content_hbounds.first - (4.0F * lr_border()) - (2.0F * lr_arrow_width()));
- return (y1 + line_height() + UI_LINE_WIDTH);
+ rgb_t fgcolor(ui().colors().text_color());
+ rgb_t bgcolor(ui().colors().text_bg_color());
+ if (pointer_action::RIGHT_SWITCH == m_pointer_action)
+ {
+ // touch swipe to switch
+ fgcolor = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ highlight(m_right_content_hbounds.first, m_right_heading_top, m_right_content_hbounds.second, m_right_heading_top + line_height(), bgcolor);
+ }
+ else if (focused_menu::RIGHTBOTTOM == m_focus)
+ {
+ // keyboard focus
+ fgcolor = rgb_t(0xff, 0xff, 0x00);
+ bgcolor = rgb_t(0xff, 0xff, 0xff);
+ ui().draw_textured_box(
+ container(),
+ m_right_content_hbounds.first, m_right_heading_top, m_right_content_hbounds.second, m_right_heading_top + line_height(),
+ bgcolor, rgb_t(43, 43, 43),
+ hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
+ }
+
+ ui().draw_text_full(container(),
+ text, text_left, m_right_heading_top, text_width,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor,
+ nullptr, nullptr,
+ line_height());
+
+ if (larrow)
+ {
+ // not using the selected colour here because the background isn't changed
+ float const left(m_right_content_hbounds.first + lr_border());
+ float const right(m_right_content_hbounds.first + lr_border() + lr_arrow_width());
+ bool const hovered(pointer_in_rect(left, right_arrows_top(), right, right_arrows_bottom()));
+ bool const tracked((pointer_action::RIGHT_TRACK_ARROW == m_pointer_action) && !m_clicked_line);
+ rgb_t fg(fgcolor);
+ if ((focused_menu::RIGHTBOTTOM != m_focus) && (tracked || (!m_ui_error && !(flags & PROCESS_NOINPUT) && pointer_idle() && hovered)))
+ fg = ui().colors().mouseover_color();
+ draw_arrow(left, right_arrows_top(), right, right_arrows_bottom(), fg, ROT90 ^ ORIENTATION_FLIP_X);
+ }
+
+ if (rarrow)
+ {
+ // not using the selected colour here because the background isn't changed
+ float const left(m_right_content_hbounds.second - lr_border() - lr_arrow_width());
+ float const right(m_right_content_hbounds.second - lr_border());
+ bool const hovered(pointer_in_rect(left, right_arrows_top(), right, right_arrows_bottom()));
+ bool const tracked((pointer_action::RIGHT_TRACK_ARROW == m_pointer_action) && m_clicked_line);
+ rgb_t fg(fgcolor);
+ if ((focused_menu::RIGHTBOTTOM != m_focus) && (tracked || (!m_ui_error && !(flags & PROCESS_NOINPUT) && pointer_idle() && hovered)))
+ fg = ui().colors().mouseover_color();
+ draw_arrow(left, right_arrows_top(), right, right_arrows_bottom(), fg, ROT90);
+ }
}
@@ -2334,8 +3592,11 @@ float menu_select_launch::draw_right_box_title(float x1, float y1, float x2, flo
// perform our special rendering
//-------------------------------------------------
-void menu_select_launch::arts_render(float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::arts_render(u32 flags)
{
+ // draw the heading
+ draw_right_box_heading(flags, FIRST_VIEW < m_image_view, LAST_VIEW > m_image_view, _("selmenu-artwork", std::get<1>(ARTS_INFO[m_image_view])));
+
ui_software_info const *software;
ui_system_info const *system;
get_selection(software, system);
@@ -2344,13 +3605,10 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f
{
m_cache.set_snapx_driver(nullptr);
- // arts title and searchpath
- std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2);
-
// loads the image if necessary
if (!m_cache.snapx_software_is(software) || !snapx_valid() || m_switch_image)
{
- emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ);
+ emu_file snapfile(get_arts_searchpath(), OPEN_FLAG_READ);
bitmap_argb32 tmp_bitmap;
if (software->startempty == 1)
@@ -2370,84 +3628,31 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f
m_cache.set_snapx_software(software);
m_switch_image = false;
- arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2);
+ arts_render_images(std::move(tmp_bitmap));
}
// if the image is available, loaded and valid, display it
- draw_snapx(origx1, origy1, origx2, origy2);
+ draw_snapx();
}
else if (system)
{
m_cache.set_snapx_software(nullptr);
- std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2);
-
// loads the image if necessary
if (!m_cache.snapx_driver_is(system->driver) || !snapx_valid() || m_switch_image)
{
- emu_file snapfile(searchstr, OPEN_FLAG_READ);
+ emu_file snapfile(get_arts_searchpath(), OPEN_FLAG_READ);
bitmap_argb32 tmp_bitmap;
load_driver_image(tmp_bitmap, snapfile, *system->driver);
m_cache.set_snapx_driver(system->driver);
m_switch_image = false;
- arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2);
+ arts_render_images(std::move(tmp_bitmap));
}
// if the image is available, loaded and valid, display it
- draw_snapx(origx1, origy1, origx2, origy2);
- }
-}
-
-
-//-------------------------------------------------
-// common function for images render
-//-------------------------------------------------
-
-std::string menu_select_launch::arts_render_common(float origx1, float origy1, float origx2, float origy2)
-{
- std::string snaptext, searchstr;
- get_title_search(snaptext, searchstr);
-
- // apply title to right panel
- float title_size = 0.0f;
- for (int x = FIRST_VIEW; x < LAST_VIEW; x++)
- {
- float const text_length = get_text_width(
- _("selmenu-artwork", std::get<1>(ARTS_INFO[m_image_view])),
- origx1, origy1, origx2 - origx1,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE);
- title_size = (std::max)(text_length + 0.01f, title_size);
+ draw_snapx();
}
-
- rgb_t const fgcolor = (m_focus == focused_menu::RIGHTBOTTOM) ? rgb_t(0xff, 0xff, 0x00) : ui().colors().text_color();
- rgb_t const bgcolor = (m_focus == focused_menu::RIGHTBOTTOM) ? rgb_t(0xff, 0xff, 0xff) : ui().colors().text_bg_color();
- float const middle = origx2 - origx1;
-
- // check size
- float const sc = title_size + 2.0f * gutter_width();
- float const tmp_size = (sc > middle) ? ((middle - 2.0f * gutter_width()) / sc) : 1.0f;
- title_size *= tmp_size;
-
- if (bgcolor != ui().colors().text_bg_color())
- {
- ui().draw_textured_box(
- container(),
- origx1 + ((middle - title_size) * 0.5f), origy1 + tb_border(),
- origx1 + ((middle + title_size) * 0.5f), origy1 + tb_border() + line_height(),
- bgcolor, rgb_t(43, 43, 43),
- hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
- }
-
- ui().draw_text_full(container(),
- snaptext, origx1, origy1 + tb_border(), origx2 - origx1,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor,
- nullptr, nullptr,
- line_height() * tmp_size);
-
- draw_common_arrow(origx1, origy1 + tb_border(), origx2, origy2, m_image_view, FIRST_VIEW, LAST_VIEW, title_size);
-
- return searchstr;
}
@@ -2455,12 +3660,11 @@ std::string menu_select_launch::arts_render_common(float origx1, float origy1, f
// perform rendering of image
//-------------------------------------------------
-void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap)
{
- bool no_available = false;
-
// if it fails, use the default image
- if (!tmp_bitmap.valid())
+ bool no_available(!tmp_bitmap.valid());
+ if (no_available)
{
tmp_bitmap.allocate(256, 256);
const bitmap_argb32 &src(m_cache.no_avail_bitmap());
@@ -2469,76 +3673,74 @@ void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float or
for (int y = 0; y < 256; y++)
tmp_bitmap.pix(y, x) = src.pix(y, x);
}
- no_available = true;
}
bitmap_argb32 &snapx_bitmap(m_cache.snapx_bitmap());
- if (tmp_bitmap.valid())
+ if (!tmp_bitmap.valid())
{
- float panel_width = origx2 - origx1 - 0.02f;
- float panel_height = origy2 - origy1 - 0.02f - (3.0f * tb_border()) - (2.0f * line_height());
- int screen_width = machine().render().ui_target().width();
- int screen_height = machine().render().ui_target().height();
+ snapx_bitmap.reset();
+ return;
+ }
+
+ float const panel_width(m_right_content_hbounds.second - m_right_content_hbounds.first);
+ float const panel_height(m_right_content_vbounds.second - m_right_content_vbounds.first);
- if (machine().render().ui_target().orientation() & ORIENTATION_SWAP_XY)
- std::swap(screen_height, screen_width);
+ auto [screen_width, screen_height] = target_size();
+ if (machine().render().ui_target().orientation() & ORIENTATION_SWAP_XY)
+ {
+ using std::swap;
+ swap(screen_height, screen_width);
+ }
- int panel_width_pixel = panel_width * screen_width;
- int panel_height_pixel = panel_height * screen_height;
+ int const panel_width_pixel(panel_width * screen_width);
+ int const panel_height_pixel(panel_height * screen_height);
- // Calculate resize ratios for resizing
- auto ratioW = (float)panel_width_pixel / tmp_bitmap.width();
- auto ratioH = (float)panel_height_pixel / tmp_bitmap.height();
- auto ratioI = (float)tmp_bitmap.height() / tmp_bitmap.width();
- auto dest_xPixel = tmp_bitmap.width();
- auto dest_yPixel = tmp_bitmap.height();
+ // Calculate resize ratios for resizing
+ auto const ratioW(float(panel_width_pixel) / float(tmp_bitmap.width()));
+ auto const ratioH(float(panel_height_pixel) / float(tmp_bitmap.height()));
+ auto const ratioI(float(tmp_bitmap.height()) / float(tmp_bitmap.width()));
+ auto dest_xPixel(tmp_bitmap.width());
+ auto dest_yPixel(tmp_bitmap.height());
+ if (ui().options().forced_4x3_snapshot() && (ratioI < 0.75F) && (m_image_view == SNAPSHOT_VIEW))
+ {
// force 4:3 ratio min
- if (ui().options().forced_4x3_snapshot() && ratioI < 0.75f && m_image_view == SNAPSHOT_VIEW)
- {
- // smaller ratio will ensure that the image fits in the view
- dest_yPixel = tmp_bitmap.width() * 0.75f;
- ratioH = (float)panel_height_pixel / dest_yPixel;
- float ratio = std::min(ratioW, ratioH);
- dest_xPixel = tmp_bitmap.width() * ratio;
- dest_yPixel *= ratio;
- }
+ dest_yPixel = tmp_bitmap.width() * 0.75F;
+ float const ratio = std::min(ratioW, float(panel_height_pixel) / float(dest_yPixel));
+ dest_xPixel = tmp_bitmap.width() * ratio;
+ dest_yPixel *= ratio;
+ }
+ else if ((ratioW < 1.0F) || (ratioH < 1.0F) || (ui().options().enlarge_snaps() && !no_available))
+ {
// resize the bitmap if necessary
- else if (ratioW < 1 || ratioH < 1 || (ui().options().enlarge_snaps() && !no_available))
- {
- // smaller ratio will ensure that the image fits in the view
- float ratio = std::min(ratioW, ratioH);
- dest_xPixel = tmp_bitmap.width() * ratio;
- dest_yPixel = tmp_bitmap.height() * ratio;
- }
-
- bitmap_argb32 dest_bitmap;
-
- // resample if necessary
- if (dest_xPixel != tmp_bitmap.width() || dest_yPixel != tmp_bitmap.height())
- {
- dest_bitmap.allocate(dest_xPixel, dest_yPixel);
- render_color color = { 1.0f, 1.0f, 1.0f, 1.0f };
- render_resample_argb_bitmap_hq(dest_bitmap, tmp_bitmap, color, true);
- }
- else
- dest_bitmap = std::move(tmp_bitmap);
-
- snapx_bitmap.allocate(panel_width_pixel, panel_height_pixel);
- int x1 = (0.5f * panel_width_pixel) - (0.5f * dest_xPixel);
- int y1 = (0.5f * panel_height_pixel) - (0.5f * dest_yPixel);
+ float const ratio(std::min(ratioW, ratioH));
+ dest_xPixel = tmp_bitmap.width() * ratio;
+ dest_yPixel = tmp_bitmap.height() * ratio;
+ }
- for (int x = 0; x < dest_xPixel; x++)
- for (int y = 0; y < dest_yPixel; y++)
- snapx_bitmap.pix(y + y1, x + x1) = dest_bitmap.pix(y, x);
- // apply bitmap
- m_cache.snapx_texture()->set_bitmap(snapx_bitmap, snapx_bitmap.cliprect(), TEXFORMAT_ARGB32);
+ // resample if necessary
+ bitmap_argb32 dest_bitmap;
+ if ((dest_xPixel != tmp_bitmap.width()) || (dest_yPixel != tmp_bitmap.height()))
+ {
+ dest_bitmap.allocate(dest_xPixel, dest_yPixel);
+ render_resample_argb_bitmap_hq(dest_bitmap, tmp_bitmap, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true);
}
else
{
- snapx_bitmap.reset();
+ dest_bitmap = std::move(tmp_bitmap);
}
+
+ snapx_bitmap.allocate(panel_width_pixel, panel_height_pixel);
+ int x1(0.5F * (float(panel_width_pixel) - float(dest_xPixel)));
+ int y1(0.5F * (float(panel_height_pixel) - float(dest_yPixel)));
+
+ for (int x = 0; x < dest_xPixel; x++)
+ for (int y = 0; y < dest_yPixel; y++)
+ snapx_bitmap.pix(y + y1, x + x1) = dest_bitmap.pix(y, x);
+
+ // apply bitmap
+ m_cache.snapx_texture()->set_bitmap(snapx_bitmap, snapx_bitmap.cliprect(), TEXFORMAT_ARGB32);
}
@@ -2546,18 +3748,14 @@ void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float or
// draw snapshot
//-------------------------------------------------
-void menu_select_launch::draw_snapx(float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::draw_snapx()
{
// if the image is available, loaded and valid, display it
if (snapx_valid())
{
- float const x1 = origx1 + 0.01f;
- float const x2 = origx2 - 0.01f;
- float const y1 = origy1 + (2.0f * tb_border()) + line_height();
- float const y2 = origy2 - tb_border() - line_height();
-
- // apply texture
- container().add_quad(x1, y1, x2, y2, rgb_t::white(), m_cache.snapx_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_quad(
+ m_right_content_hbounds.first, m_right_content_vbounds.first, m_right_content_hbounds.second, m_right_content_vbounds.second,
+ rgb_t::white(), m_cache.snapx_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
}
@@ -2721,37 +3919,10 @@ bool menu_select_launch::has_multiple_bios(game_driver const &driver, s_bios &bi
//-------------------------------------------------
-// draw collapsed left panel
-//-------------------------------------------------
-
-float menu_select_launch::draw_collapsed_left_panel(float x1, float y1, float x2, float y2)
-{
- // set left-right arrows dimension
- float const ar_x0 = 0.5F * (x2 + x1 - m_divider_arrow_width);
- float const ar_y0 = 0.5F * (y2 + y1 - m_divider_arrow_height);
- float const ar_x1 = ar_x0 + m_divider_arrow_width;
- float const ar_y1 = ar_y0 + m_divider_arrow_height;
-
- ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b)); // FIXME: magic numbers in colour?
-
- rgb_t fgcolor = ui().colors().text_color();
- if (mouse_in_rect(x1, y1, x2, y2))
- {
- fgcolor = ui().options().mouseover_color();
- set_hover(HOVER_LPANEL_ARROW);
- }
-
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90);
-
- return x2 + lr_border();
-}
-
-
-//-------------------------------------------------
// draw infos
//-------------------------------------------------
-void menu_select_launch::infos_render(float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::infos_render(u32 flags)
{
std::string_view first;
ui_software_info const *software;
@@ -2837,70 +4008,23 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2,
return;
}
- origy1 += tb_border();
- float const aspect(machine().render().ui_aspect(&container()));
- float const gutter_width = 0.4f * line_height() * aspect * 1.3f;
- float oy1 = origy1 + line_height();
-
+ // draw the heading
std::string_view const snaptext(m_info_view ? std::string_view(m_items_list[m_info_view - 1]) : first);
+ draw_right_box_heading(flags, 0 < m_info_view, (total - 1) > m_info_view, snaptext);
- // get width of widest title
- float title_size(0.0f);
- for (std::size_t x = 0; total > x; ++x)
- {
- std::string_view const name(x ? std::string_view(m_items_list[x - 1]) : first);
- float txt_length = get_string_width(name);
- txt_length += 0.01f;
- title_size = (std::max)(txt_length, title_size);
- }
-
- rgb_t fgcolor = ui().colors().text_color();
- rgb_t bgcolor = ui().colors().text_bg_color();
- if (get_focus() == focused_menu::RIGHTBOTTOM)
- {
- fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
- bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
- }
-
- float middle = origx2 - origx1;
-
- // check size
- float sc = title_size + 2.0f * gutter_width;
- float tmp_size = (sc > middle) ? ((middle - 2.0f * gutter_width) / sc) : 1.0f;
- title_size *= tmp_size;
-
- if (bgcolor != ui().colors().text_bg_color())
- {
- ui().draw_textured_box(
- container(),
- origx1 + ((middle - title_size) * 0.5f), origy1, origx1 + ((middle + title_size) * 0.5f),
- origy1 + line_height(), bgcolor, rgb_t(255, 43, 43, 43),
- hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
- }
-
- ui().draw_text_full(
- container(),
- snaptext,
- origx1, origy1, origx2 - origx1,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr,
- line_height() * tmp_size);
-
- sc = origx2 - origx1 - (2.0f * gutter_width);
+ float const sc(m_right_panel_width - (2.0F * gutter_width()));
if (!m_info_layout || (m_info_layout->width() != sc))
{
m_info_layout.emplace(
*ui().get_font(),
- machine().render().ui_aspect(&container()) * m_info_line_height, m_info_line_height,
+ x_aspect() * m_info_line_height, m_info_line_height,
sc,
text_layout::text_justify::LEFT, text_layout::word_wrapping::WORD);
menu_dats_view::add_info_text(*m_info_layout, m_info_buffer, ui().colors().text_color());
m_total_lines = m_info_layout->lines();
}
- draw_common_arrow(origx1, origy1, origx2, origy2, m_info_view, 0, total - 1, title_size);
-
- m_right_visible_lines = floor((origy2 - oy1) / m_info_line_height);
+ m_right_visible_lines = floor((m_right_content_vbounds.second - m_right_content_vbounds.first) / m_info_line_height);
if (m_total_lines < m_right_visible_lines)
m_right_visible_lines = m_total_lines;
if (m_topline_datsview < 0)
@@ -2909,22 +4033,19 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2,
m_topline_datsview = m_total_lines - m_right_visible_lines;
// get the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
- bool const up_arrow = m_topline_datsview > 0;
- bool const down_arrow = (m_topline_datsview + m_right_visible_lines) < m_total_lines;
- int const r_visible_lines = m_right_visible_lines - (up_arrow ? 1 : 0) - (down_arrow ? 1 : 0);
-
- if (mouse_in_rect(origx1 + gutter_width, oy1, origx2 - gutter_width, origy2))
- set_hover(HOVER_INFO_TEXT);
+ bool const up_arrow(!info_at_top());
+ bool const down_arrow(!info_at_bottom());
+ int const r_visible_lines(m_right_visible_lines - (up_arrow ? 1 : 0) - (down_arrow ? 1 : 0));
if (up_arrow)
- draw_info_arrow(0, origx1, origx2, oy1, m_info_line_height, m_info_line_height * aspect);
+ draw_info_arrow(flags, 0);
if (down_arrow)
- draw_info_arrow(1, origx1, origx2, oy1 + (float(m_right_visible_lines - 1) * m_info_line_height), m_info_line_height, m_info_line_height * aspect);
+ draw_info_arrow(flags, m_right_visible_lines - 1);
m_info_layout->emit(
container(),
m_topline_datsview ? (m_topline_datsview + 1) : 0, r_visible_lines,
- origx1 + gutter_width, oy1 + (m_topline_datsview ? m_info_line_height : 0.0f));
+ right_panel_left() + gutter_width(), m_right_content_vbounds.first + (m_topline_datsview ? m_info_line_height : 0.0f));
}
diff --git a/src/frontend/mame/ui/selmenu.h b/src/frontend/mame/ui/selmenu.h
index 872901ff9b6..b6793994a1f 100644
--- a/src/frontend/mame/ui/selmenu.h
+++ b/src/frontend/mame/ui/selmenu.h
@@ -13,14 +13,19 @@
#pragma once
#include "ui/menu.h"
+#include "ui/utils.h"
#include "audit.h"
#include "lrucache.h"
+#include <chrono>
#include <map>
#include <memory>
#include <optional>
+#include <string_view>
+#include <tuple>
+#include <utility>
#include <vector>
@@ -30,6 +35,14 @@ struct ui_software_info;
namespace ui {
+enum
+{
+ RP_FIRST = 0,
+ RP_IMAGES = RP_FIRST,
+ RP_INFOS,
+ RP_LAST = RP_INFOS
+};
+
class machine_static_info;
class menu_select_launch : public menu
@@ -113,9 +126,6 @@ protected:
menu_select_launch(mame_ui_manager &mui, render_container &container, bool is_swlist);
focused_menu get_focus() const { return m_focus; }
- void set_focus(focused_menu focus) { m_focus = focus; }
- bool next_image_view();
- bool previous_image_view();
bool dismiss_error();
void set_error(reset_options ropt, std::string &&message);
@@ -127,7 +137,7 @@ protected:
void launch_system(game_driver const &driver, ui_software_info const &swinfo, std::string const &part) { launch_system(ui(), driver, &swinfo, &part, nullptr); }
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(u32 flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual void menu_activated() override;
virtual void menu_deactivated() override;
@@ -137,15 +147,10 @@ protected:
// draw arrow
void draw_common_arrow(float origx1, float origy1, float origx2, float origy2, int current, int dmin, int dmax, float title);
- void draw_info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float ud_arrow_width);
-
- bool draw_error_text();
+ void draw_info_arrow(u32 flags, int line);
template <typename Filter>
- float draw_left_panel(
- typename Filter::type current,
- std::map<typename Filter::type, typename Filter::ptr> const &filters,
- float x1, float y1, float x2, float y2);
+ void draw_left_panel(u32 flags, typename Filter::type current, std::map<typename Filter::type, typename Filter::ptr> const &filters);
// icon helpers
void check_for_icons(char const *listname);
@@ -164,8 +169,11 @@ protected:
return (uintptr_t(selected_ref) > m_skip_main_items) ? selected_ref : m_prev_selected;
}
+ bool show_left_panel() const { return !(m_panels_status & HIDE_LEFT_PANEL); }
+ bool show_right_panel() const { return !(m_panels_status & HIDE_RIGHT_PANEL); }
u8 right_panel() const { return m_right_panel; }
u8 right_image() const { return m_image_view; }
+
char const *right_panel_config_string() const;
char const *right_image_config_string() const;
void set_right_panel(u8 index);
@@ -189,11 +197,28 @@ protected:
std::string m_search;
private:
+ enum class pointer_action
+ {
+ NONE,
+ MAIN_TRACK_LINE,
+ MAIN_TRACK_RBUTTON,
+ MAIN_DRAG,
+ LEFT_TRACK_LINE,
+ LEFT_DRAG,
+ RIGHT_TRACK_TAB,
+ RIGHT_TRACK_ARROW,
+ RIGHT_TRACK_LINE,
+ RIGHT_SWITCH,
+ RIGHT_DRAG,
+ TOOLBAR_TRACK,
+ DIVIDER_TRACK
+ };
+
using bitmap_vector = std::vector<bitmap_argb32>;
using texture_ptr_vector = std::vector<texture_ptr>;
using s_parts = std::unordered_map<std::string, std::string>;
- using s_bios = std::vector<std::pair<std::string, int>>;
+ using s_bios = std::vector<std::pair<std::string, int> >;
class software_parts;
class bios_selection;
@@ -242,22 +267,48 @@ private:
using flags_cache = util::lru_cache_map<game_driver const *, system_flags>;
- void reset_pressed() { m_pressed = false; m_repeat = 0; }
- bool mouse_pressed() const { return (osd_ticks() >= m_repeat); }
- void set_pressed();
+ // various helpers for common calculations
+ bool main_at_top() const noexcept { return !top_line; }
+ bool main_at_bottom() const noexcept { return (top_line + m_primary_lines) >= m_available_items; }
+ bool is_main_up_arrow(int index) const noexcept { return !index && !main_at_top(); }
+ bool is_main_down_arrow(int index) const noexcept { return ((m_primary_lines - 1) == index) && !main_at_bottom(); }
+ bool left_at_top() const noexcept { return !m_left_visible_top; }
+ bool left_at_bottom() const noexcept { return (m_left_visible_top + m_left_visible_lines) >= m_left_item_count; }
+ bool is_left_up_arrow(int index) const noexcept { return !index && !left_at_top(); }
+ bool is_left_down_arrow(int index) const noexcept { return ((m_left_visible_lines - 1) == index) && !left_at_bottom(); }
+ bool info_at_top() const noexcept { return !m_topline_datsview; }
+ bool info_at_bottom() const noexcept { return (m_topline_datsview + m_right_visible_lines) >= m_total_lines; }
+
+ // getting precalculated geometry
+ float left_panel_left() const noexcept { return lr_border(); }
+ float left_panel_right() const noexcept { return lr_border() + m_left_panel_width; }
+ float right_panel_left() const noexcept { return 1.0F - lr_border() - m_right_panel_width; }
+ float right_panel_right() const noexcept { return 1.0F - lr_border(); }
+ float right_tab_width() const noexcept { return m_right_panel_width / float(RP_LAST - RP_FIRST + 1); }
+ float right_arrows_top() const noexcept { return m_right_heading_top + (0.1F * line_height()); }
+ float right_arrows_bottom() const noexcept { return m_right_heading_top + (0.9F * line_height()); }
+ float left_divider_left() const noexcept { return lr_border() + m_left_panel_width; }
+ float left_divider_right() const noexcept { return lr_border() + m_left_panel_width + m_divider_width; }
+ float right_divider_left() const noexcept { return 1.0F - lr_border() - m_right_panel_width - m_divider_width; }
+ float right_divider_right() const noexcept { return 1.0F - lr_border() - m_right_panel_width; }
bool snapx_valid() const { return m_cache.snapx_bitmap().valid(); }
+ void draw_divider(u32 flags, float x1, bool right);
+
// draw left panel
- virtual float draw_left_panel(float x1, float y1, float x2, float y2) = 0;
- float draw_collapsed_left_panel(float x1, float y1, float x2, float y2);
+ virtual void draw_left_panel(u32 flags) = 0;
// draw infos
- void infos_render(float x1, float y1, float x2, float y2);
+ void infos_render(u32 flags);
void general_info(ui_system_info const *system, game_driver const &driver, std::string &buffer);
// get selected software and/or driver
virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const = 0;
+
+ // show configuration menu
+ virtual void show_config_menu(int index) = 0;
+
virtual bool accept_search() const { return true; }
void select_prev()
{
@@ -277,38 +328,65 @@ private:
}
}
}
+ void set_focus(focused_menu focus) { m_focus = focus; }
void rotate_focus(int dir);
-
- void draw_toolbar(float x1, float y1, float x2, float y2);
+ std::pair<bool, bool> next_right_panel_view();
+ std::pair<bool, bool> previous_right_panel_view();
+ std::pair<bool, bool> next_image_view();
+ std::pair<bool, bool> previous_image_view();
+ std::pair<bool, bool> next_info_view();
+ std::pair<bool, bool> previous_info_view();
+
+ void draw_toolbar(u32 flags, float x1, float y1, float x2, float y2);
void draw_star(float x0, float y0);
void draw_icon(int linenum, void *selectedref, float x1, float y1);
virtual render_texture *get_icon_texture(int linenum, void *selectedref) = 0;
- void get_title_search(std::string &title, std::string &search);
+ std::string get_arts_searchpath();
// event handling
- virtual void handle_keys(u32 flags, int &iptkey) override;
- virtual void handle_events(u32 flags, event &ev) override;
+ virtual bool handle_events(u32 flags, event &ev) override;
+ virtual bool handle_keys(u32 flags, int &iptkey) override;
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
+
+ // pointer handling helpers
+ std::tuple<int, bool, bool> handle_primary_down(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> handle_right_down(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> handle_middle_down(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_main_track_line(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_main_track_rbutton(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_main_drag(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_left_track_line(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_left_drag(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_right_track_tab(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_right_track_arrow(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_right_track_line(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_right_switch(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_right_drag(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_toolbar_track(bool changed, ui_event const &uievt);
+ std::tuple<int, bool, bool> update_divider_track(bool changed, ui_event const &uievt);
+
+ bool main_force_visible_selection();
// draw game list
virtual void draw(u32 flags) override;
// draw right panel
- void draw_right_panel(float origx1, float origy1, float origx2, float origy2);
- float draw_right_box_title(float x1, float y1, float x2, float y2);
+ void draw_right_panel(u32 flags);
+ void draw_right_box_tabs(u32 flags);
+ void draw_right_box_heading(u32 flags, bool larrow, bool rarrow, std::string_view text);
// images render
- void arts_render(float origx1, float origy1, float origx2, float origy2);
- std::string arts_render_common(float origx1, float origy1, float origx2, float origy2);
- void arts_render_images(bitmap_argb32 &&bitmap, float origx1, float origy1, float origx2, float origy2);
- void draw_snapx(float origx1, float origy1, float origx2, float origy2);
+ void arts_render(u32 flags);
+ void arts_render_images(bitmap_argb32 &&bitmap);
+ void draw_snapx();
// text for main top/bottom panels
virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const = 0;
virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const = 0;
// filter navigation
- virtual void filter_selected() = 0;
+ virtual void filter_selected(int index) = 0;
static void make_audit_fail_text(std::ostream &str, media_auditor const &auditor, media_auditor::summary summary);
static void launch_system(mame_ui_manager &mui, game_driver const &driver, ui_software_info const *swinfo, std::string const *part, int const *bios);
@@ -316,8 +394,20 @@ private:
static bool has_multiple_bios(ui_software_info const &swinfo, s_bios &biosname);
static bool has_multiple_bios(game_driver const &driver, s_bios &biosname);
- bool show_left_panel() const;
- bool show_right_panel() const;
+ bool check_scroll_repeat(float top, std::pair<float, float> hbounds, float height)
+ {
+ float const linetop(top + (float(m_clicked_line) * height));
+ float const linebottom(top + (float(m_clicked_line + 1) * height));
+ if (pointer_in_rect(hbounds.first, linetop, hbounds.second, linebottom))
+ {
+ if (std::chrono::steady_clock::now() >= m_scroll_repeat)
+ {
+ m_scroll_repeat += std::chrono::milliseconds(100);
+ return true;
+ }
+ }
+ return false;
+ }
bool m_ui_error;
std::string m_error_text;
@@ -339,11 +429,38 @@ private:
cache &m_cache;
bool m_is_swlist;
focused_menu m_focus;
- bool m_pressed; // mouse button held down
- osd_ticks_t m_repeat;
+ pointer_action m_pointer_action;
+ std::chrono::steady_clock::time_point m_scroll_repeat;
+ std::pair<float, float> m_base_pointer;
+ std::pair<float, float> m_last_pointer;
+ int m_clicked_line;
+
+ std::pair<float, float> m_primary_vbounds;
+ float m_primary_items_top;
+ std::pair<float, float> m_primary_items_hbounds;
+ int m_primary_lines;
+
+ float m_left_panel_width;
+ std::pair<float, float> m_left_items_hbounds;
+ float m_left_items_top;
+ int m_left_item_count;
+ int m_left_visible_lines;
+ int m_left_visible_top;
+
+ float m_right_panel_width;
+ float m_right_tabs_bottom;
+ float m_right_heading_top;
+ std::pair<float, float> m_right_content_vbounds;
+ std::pair<float, float> m_right_content_hbounds;
int m_right_visible_lines; // right box lines
+ std::pair<float, float> m_toolbar_button_vbounds;
+ float m_toolbar_button_width;
+ float m_toolbar_button_spacing;
+ float m_toolbar_backtrack_left;
+ float m_toolbar_main_left;
+
u8 m_panels_status;
u8 m_right_panel;
bool m_has_icons;
diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp
index bc8d3267585..5a0a4f1eb87 100644
--- a/src/frontend/mame/ui/selsoft.cpp
+++ b/src/frontend/mame/ui/selsoft.cpp
@@ -14,6 +14,7 @@
#include "ui/ui.h"
#include "ui/icorender.h"
#include "ui/inifile.h"
+#include "ui/miscmenu.h"
#include "ui/selector.h"
#include "drivenum.h"
@@ -444,68 +445,6 @@ bool menu_select_software::handle(event const *ev)
changed = inkey_select(ev);
break;
- case IPT_UI_LEFT:
- if (right_panel() == RP_IMAGES)
- {
- // Images
- changed = previous_image_view();
- }
- else if (right_panel() == RP_INFOS && ui_globals::cur_sw_dats_view > 0)
- {
- // Infos
- ui_globals::cur_sw_dats_view--;
- m_topline_datsview = 0;
- changed = true;
- }
- break;
-
- case IPT_UI_RIGHT:
- if (right_panel() == RP_IMAGES)
- {
- // Images
- changed = next_image_view();
- }
- else if (right_panel() == RP_INFOS && ui_globals::cur_sw_dats_view < (ui_globals::cur_sw_dats_total - 1))
- {
- // Infos
- ui_globals::cur_sw_dats_view++;
- m_topline_datsview = 0;
- changed = true;
- }
- break;
-
- case IPT_UI_UP:
- if ((get_focus() == focused_menu::LEFT) && (software_filter::FIRST < m_filter_highlight))
- {
- --m_filter_highlight;
- changed = true;
- }
- break;
-
- case IPT_UI_DOWN:
- if ((get_focus() == focused_menu::LEFT) && (software_filter::LAST > m_filter_highlight))
- {
- ++m_filter_highlight;
- changed = true;
- }
- break;
-
- case IPT_UI_HOME:
- if (get_focus() == focused_menu::LEFT)
- {
- m_filter_highlight = software_filter::FIRST;
- changed = true;
- }
- break;
-
- case IPT_UI_END:
- if (get_focus() == focused_menu::LEFT)
- {
- m_filter_highlight = software_filter::LAST;
- changed = true;
- }
- break;
-
case IPT_UI_DATS:
inkey_dats();
break;
@@ -538,8 +477,6 @@ bool menu_select_software::handle(event const *ev)
}
}
- // if we're in an error state, overlay an error message
- draw_error_text();
return changed;
}
@@ -552,7 +489,7 @@ void menu_select_software::recompute_metrics(uint32_t width, uint32_t height, fl
menu_select_launch::recompute_metrics(width, height, aspect);
// configure the custom rendering
- set_custom_space(4.0F * line_height() + 5.0F * tb_border(), 4.0F * line_height() + 4.0F * tb_border());
+ set_custom_space(4.0F * line_height() + 5.0F * tb_border(), 4.0F * line_height() + 3.0F * tb_border());
}
//-------------------------------------------------
@@ -704,9 +641,9 @@ bool menu_select_software::inkey_select(const event *menu_event)
// draw left box
//-------------------------------------------------
-float menu_select_software::draw_left_panel(float x1, float y1, float x2, float y2)
+void menu_select_software::draw_left_panel(u32 flags)
{
- return menu_select_launch::draw_left_panel<software_filter>(m_data->filter_type(), m_data->filters(), x1, y1, x2, y2);
+ return menu_select_launch::draw_left_panel<software_filter>(flags, m_data->filter_type(), m_data->filters());
}
@@ -771,6 +708,12 @@ void menu_select_software::get_selection(ui_software_info const *&software, ui_s
}
+void menu_select_software::show_config_menu(int index)
+{
+ menu::stack_push<menu_machine_configure>(ui(), container(), m_system, nullptr);
+}
+
+
void menu_select_software::make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const
{
// determine the text for the header
@@ -794,29 +737,28 @@ std::string menu_select_software::make_software_description(ui_software_info con
}
-void menu_select_software::filter_selected()
+void menu_select_software::filter_selected(int index)
{
- if ((software_filter::FIRST <= m_filter_highlight) && (software_filter::LAST >= m_filter_highlight))
- {
- m_data->get_filter(software_filter::type(m_filter_highlight)).show_ui(
- ui(),
- container(),
- [this] (software_filter &filter)
+ assert((software_filter::FIRST <= index) && (software_filter::LAST >= index));
+
+ m_data->get_filter(software_filter::type(index)).show_ui(
+ ui(),
+ container(),
+ [this] (software_filter &filter)
+ {
+ software_filter::type const new_type(filter.get_type());
+ if (software_filter::CUSTOM == new_type)
{
- software_filter::type const new_type(filter.get_type());
- if (software_filter::CUSTOM == new_type)
+ emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ if (!file.open(util::string_format("custom_%s_filter.ini", m_system.driver->name)))
{
- emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- if (!file.open(util::string_format("custom_%s_filter.ini", m_system.driver->name)))
- {
- filter.save_ini(file, 0);
- file.close();
- }
+ filter.save_ini(file, 0);
+ file.close();
}
- m_data->set_filter_type(new_type);
- reset(reset_options::REMEMBER_REF);
- });
- }
+ }
+ m_data->set_filter_type(new_type);
+ reset(reset_options::REMEMBER_REF);
+ });
}
} // namespace ui
diff --git a/src/frontend/mame/ui/selsoft.h b/src/frontend/mame/ui/selsoft.h
index 3b3074ddcdf..e250a608c08 100644
--- a/src/frontend/mame/ui/selsoft.h
+++ b/src/frontend/mame/ui/selsoft.h
@@ -48,18 +48,19 @@ private:
virtual bool handle(event const *ev) override;
// drawing
- virtual float draw_left_panel(float x1, float y1, float x2, float y2) override;
+ virtual void draw_left_panel(u32 flags) override;
virtual render_texture *get_icon_texture(int linenum, void *selectedref) override;
// get selected software and/or driver
virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const override;
+ virtual void show_config_menu(int index) override;
// text for main top/bottom panels
virtual void make_topbox_text(std::string &line0, std::string &line1, std::string &line2) const override;
virtual std::string make_software_description(ui_software_info const &software, ui_system_info const *system) const override;
// filter navigation
- virtual void filter_selected() override;
+ virtual void filter_selected(int index) override;
// toolbar
virtual void inkey_export() override { throw false; }
diff --git a/src/frontend/mame/ui/simpleselgame.cpp b/src/frontend/mame/ui/simpleselgame.cpp
index 569aeed400d..354a8ad2c87 100644
--- a/src/frontend/mame/ui/simpleselgame.cpp
+++ b/src/frontend/mame/ui/simpleselgame.cpp
@@ -27,6 +27,7 @@
#include "uiinput.h"
#include <cctype>
+#include <string_view>
namespace ui {
@@ -143,16 +144,6 @@ bool simple_menu_select_game::handle(event const *ev)
inkey_special(*ev);
break;
}
-
- // if we're in an error state, overlay an error message
- if (m_error)
- {
- ui().draw_text_box(
- container(),
- _("The selected game is missing one or more required ROM or CHD images. "
- "Please select a different game.\n\nPress any key to continue."),
- text_layout::text_justify::CENTER, 0.5f, 0.5f, UI_RED_COLOR);
- }
return changed;
}
@@ -300,7 +291,7 @@ void simple_menu_select_game::recompute_metrics(uint32_t width, uint32_t height,
menu::recompute_metrics(width, height, aspect);
// configure the custom rendering
- set_custom_space(line_height() + 3.0f * tb_border(), 4.0f * line_height() + 3.0f * tb_border());
+ set_custom_space(line_height() + 3.0f * tb_border(), 5.0f * line_height() + 3.0f * tb_border());
}
@@ -308,11 +299,11 @@ void simple_menu_select_game::recompute_metrics(uint32_t width, uint32_t height,
// custom_render - perform our special rendering
//-------------------------------------------------
-void simple_menu_select_game::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void simple_menu_select_game::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
- // if no matches, display the error message
if (m_nomatch)
{
+ // if no matches, display the error message
ui().draw_text_box(
container(),
string_format(
@@ -326,104 +317,133 @@ void simple_menu_select_game::custom_render(void *selectedref, float top, float
UI_RED_COLOR);
return;
}
-
- const game_driver *driver;
- std::string tempbuf[5];
-
- // display the current typeahead
- if (!m_search.empty())
- tempbuf[0] = string_format(_("Type name or select: %1$s_"), m_search);
else
- tempbuf[0] = _("Type name or select: (random)");
-
- // draw the top box
- draw_text_box(
- tempbuf, tempbuf + 1,
- origx1, origx2, origy1 - top, origy1 - tb_border(),
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color());
-
- // determine the text to render below
- driver = ((uintptr_t)selectedref > 1) ? (const game_driver *)selectedref : nullptr;
- if (driver)
{
- // first line is game name
- tempbuf[0] = string_format(_("%1$-.100s"), driver->type.fullname());
-
- // next line is year, manufacturer
- tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver->year, driver->manufacturer);
-
- // next line source path
- tempbuf[2] = string_format(_("Source file: %1$s"), info_xml_creator::format_sourcefile(driver->type.source()));
+ std::string tempbuf[5];
- // update cached values if selection changed
- if (driver != m_cached_driver)
+ // display the current typeahead
+ if (!m_search.empty())
+ tempbuf[0] = string_format(_("Type name or select: %1$s_"), m_search);
+ else
+ tempbuf[0] = _("Type name or select: (random)");
+
+ // draw the top box
+ draw_text_box(
+ tempbuf, tempbuf + 1,
+ origx1, origx2, origy1 - top, origy1 - tb_border(),
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
+ ui().colors().text_color(), ui().colors().background_color());
+
+ // determine the text to render below
+ game_driver const *const driver = (uintptr_t(selectedref) > 1) ? (const game_driver *)selectedref : nullptr;
+ if (driver)
{
- emu_options clean_options;
- machine_static_info const info(ui().options(), machine_config(*driver, clean_options));
- m_cached_driver = driver;
- m_cached_flags = info.machine_flags();
- m_cached_unemulated = info.unemulated_features();
- m_cached_imperfect = info.imperfect_features();
- m_cached_color = info.status_color();
- }
+ // first line is game name
+ tempbuf[0] = string_format(_("%1$-.100s"), driver->type.fullname());
- // next line is overall driver status
- if (m_cached_flags & machine_flags::NOT_WORKING)
- tempbuf[3] = _("Overall: NOT WORKING");
- else if ((m_cached_unemulated | m_cached_imperfect) & device_t::feature::PROTECTION)
- tempbuf[3] = _("Overall: Unemulated Protection");
- else
- tempbuf[3] = _("Overall: Working");
+ // next line is year, manufacturer
+ tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver->year, driver->manufacturer);
- // next line is graphics, sound status
- if (m_cached_unemulated & device_t::feature::GRAPHICS)
- tempbuf[4] = _("Graphics: Unimplemented, ");
- else if ((m_cached_unemulated | m_cached_imperfect) & (device_t::feature::GRAPHICS | device_t::feature::PALETTE))
- tempbuf[4] = _("Graphics: Imperfect, ");
- else
- tempbuf[4] = _("Graphics: OK, ");
-
- if (m_cached_flags & machine_flags::NO_SOUND_HW)
- tempbuf[4].append(_("Sound: None"));
- else if (m_cached_unemulated & device_t::feature::SOUND)
- tempbuf[4].append(_("Sound: Unimplemented"));
- else if (m_cached_imperfect & device_t::feature::SOUND)
- tempbuf[4].append(_("Sound: Imperfect"));
- else
- tempbuf[4].append(_("Sound: OK"));
- }
- else
- {
- const char *s = emulator_info::get_copyright();
- unsigned line = 0;
+ // next line source path
+ tempbuf[2] = string_format(_("Source file: %1$s"), info_xml_creator::format_sourcefile(driver->type.source()));
+
+ // update cached values if selection changed
+ if (driver != m_cached_driver)
+ {
+ emu_options clean_options;
+ machine_static_info const info(ui().options(), machine_config(*driver, clean_options));
+ m_cached_driver = driver;
+ m_cached_flags = info.machine_flags();
+ m_cached_unemulated = info.unemulated_features();
+ m_cached_imperfect = info.imperfect_features();
+ m_cached_color = info.status_color();
+ }
- // first line is version string
- tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version);
+ // next line is overall driver status
+ if (m_cached_flags & machine_flags::NOT_WORKING)
+ tempbuf[3] = _("Overall: NOT WORKING");
+ else if ((m_cached_unemulated | m_cached_imperfect) & device_t::feature::PROTECTION)
+ tempbuf[3] = _("Overall: Unemulated Protection");
+ else
+ tempbuf[3] = _("Overall: Working");
- // output message
- while (line < std::size(tempbuf))
+ // next line is graphics, sound status
+ if (m_cached_unemulated & device_t::feature::GRAPHICS)
+ tempbuf[4] = _("Graphics: Unimplemented, ");
+ else if ((m_cached_unemulated | m_cached_imperfect) & (device_t::feature::GRAPHICS | device_t::feature::PALETTE))
+ tempbuf[4] = _("Graphics: Imperfect, ");
+ else
+ tempbuf[4] = _("Graphics: OK, ");
+
+ if (m_cached_flags & machine_flags::NO_SOUND_HW)
+ tempbuf[4].append(_("Sound: None"));
+ else if (m_cached_unemulated & device_t::feature::SOUND)
+ tempbuf[4].append(_("Sound: Unimplemented"));
+ else if (m_cached_imperfect & device_t::feature::SOUND)
+ tempbuf[4].append(_("Sound: Imperfect"));
+ else
+ tempbuf[4].append(_("Sound: OK"));
+ }
+ else
{
- if (!(*s == 0 || *s == '\n'))
- tempbuf[line].push_back(*s);
+ std::string_view s = emulator_info::get_copyright();
+ unsigned line = 0;
+
+ // first line is version string
+ tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version);
- if (*s == '\n')
+ // output message
+ while (line < std::size(tempbuf))
{
- line++;
- s++;
- } else if (*s != 0)
- s++;
- else
- line++;
+ auto const found = s.find('\n');
+ if (std::string::npos != found)
+ {
+ tempbuf[line++] = s.substr(0, found);
+ s.remove_prefix(found + 1);
+ }
+ else
+ {
+ tempbuf[line++] = s;
+ s = std::string_view();
+ }
+ }
}
+
+ // draw the bottom box
+ draw_text_box(
+ std::begin(tempbuf), std::end(tempbuf),
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, true,
+ ui().colors().text_color(), driver ? m_cached_color : ui().colors().background_color());
+ }
+
+ // if we're in an error state, overlay an error message
+ if (m_error)
+ {
+ ui().draw_text_box(
+ container(),
+ _("The selected system is missing one or more required ROMs/disk images. "
+ "Please select a different system.\n\nPress any key to continue."),
+ text_layout::text_justify::CENTER, 0.5f, 0.5f, UI_RED_COLOR);
}
+}
+
- // draw the bottom box
- draw_text_box(
- tempbuf, tempbuf + 4,
- origx1, origx2, origy2 + tb_border(), origy2 + bottom,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, true,
- ui().colors().text_color(), driver ? m_cached_color : ui().colors().background_color());
+//-------------------------------------------------
+// custom_pointer_updated - override pointer
+// handling
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> simple_menu_select_game::custom_pointer_updated(bool changed, ui_event const &uievt)
+{
+ // only override mouse handling when error message is visible
+ if (!m_error || !uievt.pointer_buttons)
+ return std::make_tuple(IPT_INVALID, false, false);
+
+ // primary click dismisses the message
+ if ((uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
+ m_error = false;
+ return std::make_tuple(IPT_INVALID, true, !m_error);
}
diff --git a/src/frontend/mame/ui/simpleselgame.h b/src/frontend/mame/ui/simpleselgame.h
index f010bf14e8e..fcfe94e6109 100644
--- a/src/frontend/mame/ui/simpleselgame.h
+++ b/src/frontend/mame/ui/simpleselgame.h
@@ -15,8 +15,10 @@
#include "menu.h"
+
class driver_enumerator;
+
namespace ui {
class simple_menu_select_game : public menu
@@ -30,8 +32,9 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual bool custom_ui_back() override { return !m_search.empty(); }
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
private:
enum { VISIBLE_GAMES_IN_LIST = 15 };
diff --git a/src/frontend/mame/ui/sliders.cpp b/src/frontend/mame/ui/sliders.cpp
index b708b6af56a..1cbec4e3528 100644
--- a/src/frontend/mame/ui/sliders.cpp
+++ b/src/frontend/mame/ui/sliders.cpp
@@ -255,11 +255,10 @@ void menu_sliders::recompute_metrics(uint32_t width, uint32_t height, float aspe
//-------------------------------------------------
-// menu_sliders_custom_render - perform our special
-// rendering
+// custom_render - perform our special rendering
//-------------------------------------------------
-void menu_sliders::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+void menu_sliders::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
const slider_state *curslider = (const slider_state *)selectedref;
if (curslider != nullptr)
diff --git a/src/frontend/mame/ui/sliders.h b/src/frontend/mame/ui/sliders.h
index f9c0c69a176..b3c8ae3aca7 100644
--- a/src/frontend/mame/ui/sliders.h
+++ b/src/frontend/mame/ui/sliders.h
@@ -26,7 +26,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual void menu_activated() override;
virtual void menu_deactivated() override;
diff --git a/src/frontend/mame/ui/slotopt.cpp b/src/frontend/mame/ui/slotopt.cpp
index bd92a2f9c9b..10fbeed03c6 100644
--- a/src/frontend/mame/ui/slotopt.cpp
+++ b/src/frontend/mame/ui/slotopt.cpp
@@ -222,7 +222,7 @@ void menu_slot_devices::recompute_metrics(uint32_t width, uint32_t height, float
// custom_render - draw extra menu content
//-------------------------------------------------
-void menu_slot_devices::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_slot_devices::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
if (selectedref && (ITEMREF_RESET != selectedref))
{
diff --git a/src/frontend/mame/ui/slotopt.h b/src/frontend/mame/ui/slotopt.h
index af272dde8ed..91c78de7d1e 100644
--- a/src/frontend/mame/ui/slotopt.h
+++ b/src/frontend/mame/ui/slotopt.h
@@ -27,7 +27,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) override;
private:
enum class step_t
diff --git a/src/frontend/mame/ui/state.cpp b/src/frontend/mame/ui/state.cpp
index 0c39c89d9e6..efbc9e67e98 100644
--- a/src/frontend/mame/ui/state.cpp
+++ b/src/frontend/mame/ui/state.cpp
@@ -13,6 +13,7 @@
#include "emuopts.h"
#include "inputdev.h"
+#include "uiinput.h"
#include "path.h"
@@ -376,10 +377,11 @@ void menu_load_save_state_base::slot_selected(std::string &&name)
// handle_keys - override key handling
//-------------------------------------------------
-void menu_load_save_state_base::handle_keys(uint32_t flags, int &iptkey)
+bool menu_load_save_state_base::handle_keys(uint32_t flags, int &iptkey)
{
if (m_confirm_delete)
{
+ bool updated(false);
if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0))
{
// try to remove the file
@@ -415,21 +417,40 @@ void menu_load_save_state_base::handle_keys(uint32_t flags, int &iptkey)
m_confirm_prompt.clear();
m_confirm_delete = nullptr;
m_keys_released = false;
+ updated = true;
}
iptkey = IPT_INVALID;
+ return updated;
}
else if (INPUT_CODE_INVALID != m_slot_selected)
{
iptkey = IPT_INVALID;
+ return false;
}
else
{
- menu::handle_keys(flags, iptkey);
+ return autopause_menu<>::handle_keys(flags, iptkey);
}
}
//-------------------------------------------------
+// custom_pointer_updated - override pointer
+// handling
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_load_save_state_base::custom_pointer_updated(bool changed, ui_event const &uievt)
+{
+ // suppress clicks on the menu while the delete prompt is visible
+ if (m_confirm_delete && uievt.pointer_buttons)
+ return std::make_tuple(IPT_INVALID, true, false);
+ else
+ return autopause_menu<>::custom_pointer_updated(changed, uievt);
+
+}
+
+
+//-------------------------------------------------
// recompute_metrics - recompute metrics
//-------------------------------------------------
@@ -446,7 +467,7 @@ void menu_load_save_state_base::recompute_metrics(uint32_t width, uint32_t heigh
// custom_render - perform our special rendering
//-------------------------------------------------
-void menu_load_save_state_base::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_load_save_state_base::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
std::string_view text[2];
unsigned count(0U);
diff --git a/src/frontend/mame/ui/state.h b/src/frontend/mame/ui/state.h
index 8d02ab08881..1bbdd1396ae 100644
--- a/src/frontend/mame/ui/state.h
+++ b/src/frontend/mame/ui/state.h
@@ -17,6 +17,7 @@
#include "iptseqpoll.h"
#include <chrono>
+#include <tuple>
#include <unordered_map>
@@ -37,8 +38,9 @@ protected:
bool one_shot);
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual void handle_keys(uint32_t flags, int &iptkey) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual bool handle_keys(uint32_t flags, int &iptkey) override;
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
virtual void populate() override;
virtual bool handle(event const *ev) override;
diff --git a/src/frontend/mame/ui/submenu.cpp b/src/frontend/mame/ui/submenu.cpp
index 188f6622c71..cce1f28eeb2 100644
--- a/src/frontend/mame/ui/submenu.cpp
+++ b/src/frontend/mame/ui/submenu.cpp
@@ -465,7 +465,7 @@ void submenu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
// perform our special rendering
//-------------------------------------------------
-void submenu::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void submenu::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
if (selectedref)
{
diff --git a/src/frontend/mame/ui/submenu.h b/src/frontend/mame/ui/submenu.h
index 87e92cc3d9d..80c836e67e8 100644
--- a/src/frontend/mame/ui/submenu.h
+++ b/src/frontend/mame/ui/submenu.h
@@ -61,7 +61,7 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
private:
virtual void populate() override;
diff --git a/src/frontend/mame/ui/text.cpp b/src/frontend/mame/ui/text.cpp
index be7105c1938..ffbddad418f 100644
--- a/src/frontend/mame/ui/text.cpp
+++ b/src/frontend/mame/ui/text.cpp
@@ -605,18 +605,22 @@ bool text_layout::hit_test(float x, float y, size_t &start, size_t &span)
// restyle
//-------------------------------------------------
-void text_layout::restyle(size_t start, size_t span, rgb_t *fgcolor, rgb_t *bgcolor)
+void text_layout::restyle(size_t start, size_t span, rgb_t const *fgcolor, rgb_t const *bgcolor)
{
for (const auto &line : m_lines)
{
for (size_t i = 0; i < line->character_count(); i++)
{
auto &ch = line->character(i);
- if (ch.source.start >= start && ch.source.start + ch.source.span <= start + span)
+ if ((ch.source.start + ch.source.span) > (start + span))
{
- if (fgcolor != nullptr)
+ return;
+ }
+ else if (ch.source.start >= start)
+ {
+ if (fgcolor)
ch.style.fgcolor = *fgcolor;
- if (bgcolor != nullptr)
+ if (bgcolor)
ch.style.bgcolor = *bgcolor;
}
}
diff --git a/src/frontend/mame/ui/text.h b/src/frontend/mame/ui/text.h
index 0e19970c3b0..a9169faa186 100644
--- a/src/frontend/mame/ui/text.h
+++ b/src/frontend/mame/ui/text.h
@@ -66,7 +66,7 @@ public:
bool empty() const { return m_lines.empty(); }
size_t lines() const { return m_lines.size(); }
bool hit_test(float x, float y, size_t &start, size_t &span);
- void restyle(size_t start, size_t span, rgb_t *fgcolor, rgb_t *bgcolor);
+ void restyle(size_t start, size_t span, rgb_t const *fgcolor, rgb_t const *bgcolor);
void emit(render_container &container, float x, float y);
void emit(render_container &container, size_t start, size_t lines, float x, float y);
void add_text(std::string_view text, rgb_t fgcolor = rgb_t::white(), rgb_t bgcolor = rgb_t::transparent(), float size = 1.0)
diff --git a/src/frontend/mame/ui/textbox.cpp b/src/frontend/mame/ui/textbox.cpp
index 065fca79101..33319047bd8 100644
--- a/src/frontend/mame/ui/textbox.cpp
+++ b/src/frontend/mame/ui/textbox.cpp
@@ -12,7 +12,8 @@
#include "textbox.h"
#include "ui/ui.h"
-#include "ui/utils.h"
+
+#include "uiinput.h"
#include <string_view>
#include <utility>
@@ -80,11 +81,18 @@ void populate_three_column_layout(std::string_view text, T &&l, U &&c, V &&r)
menu_textbox::menu_textbox(mame_ui_manager &mui, render_container &container)
: menu(mui, container)
, m_layout()
+ , m_line_bounds(0.0F, 0.0F)
+ , m_visible_top(0.0F)
, m_layout_width(-1.0F)
, m_desired_width(-1.0F)
, m_desired_lines(-1)
, m_window_lines(0)
, m_top_line(0)
+ , m_pointer_action(pointer_action::NONE)
+ , m_scroll_repeat(std::chrono::steady_clock::time_point::min())
+ , m_base_pointer(0.0F, 0.0F)
+ , m_last_pointer(0.0F, 0.0F)
+ , m_scroll_base(0)
{
}
@@ -97,50 +105,129 @@ menu_textbox::~menu_textbox()
void menu_textbox::reset_layout()
{
// force recompute and scroll to top
- m_layout = std::nullopt;
+ m_layout.reset();
m_top_line = 0;
+ m_pointer_action = pointer_action::NONE;
}
-bool menu_textbox::handle_key(int key)
+void menu_textbox::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
- switch (key)
- {
- case IPT_UI_UP:
- --m_top_line;
- return true;
-
- case IPT_UI_DOWN:
- ++m_top_line;
- return true;
-
- case IPT_UI_PAGE_UP:
- m_top_line -= m_window_lines - 3;
- return true;
+ menu::recompute_metrics(width, height, aspect);
- case IPT_UI_PAGE_DOWN:
- m_top_line += m_window_lines - 3;
- return true;
+ m_layout.reset();
+ m_pointer_action = pointer_action::NONE;
+}
- case IPT_UI_HOME:
- m_top_line = 0;
- return true;
- case IPT_UI_END:
- m_top_line = m_layout->lines() - m_window_lines;
- return true;
+std::tuple<int, bool, bool> menu_textbox::custom_pointer_updated(bool changed, ui_event const &uievt)
+{
+ // no pointer input if we don't have up-to-date content on-screen
+ if (!m_layout || (ui_event::type::POINTER_ABORT == uievt.event_type))
+ {
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
- default:
- return false;
+ // if nothing's happening, check for clicks
+ if (pointer_idle())
+ {
+ if ((uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
+ {
+ auto const [x, y] = pointer_location();
+ if ((x >= m_line_bounds.first) && (x < m_line_bounds.second))
+ {
+ if (m_top_line && pointer_in_line(y, 0))
+ {
+ // scroll up arrow
+ --m_top_line;
+ m_pointer_action = pointer_action::SCROLL_UP;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if (((m_top_line + m_window_lines) < m_layout->lines()) && pointer_in_line(y, m_window_lines - 1))
+ {
+ // scroll down arrow
+ ++m_top_line;
+ m_pointer_action = pointer_action::SCROLL_DOWN;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if ((2 == uievt.pointer_clicks) && pointer_in_line(y, m_window_lines + 1))
+ {
+ // return to previous menu item
+ // FIXME: this should really use the start point of the multi-click action
+ m_pointer_action = pointer_action::CHECK_EXIT;
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ else if ((ui_event::pointer::TOUCH == uievt.pointer_type) && (y >= m_visible_top) && (y < (m_visible_top + (float(m_window_lines) * line_height()))))
+ {
+ m_pointer_action = pointer_action::SCROLL_DRAG;
+ m_base_pointer = std::make_pair(x, y);
+ m_last_pointer = m_base_pointer;
+ m_scroll_base = m_top_line;
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ }
+ }
+ return std::make_tuple(IPT_INVALID, false, false);
}
-}
+ // handle in-progress actions
+ switch (m_pointer_action)
+ {
+ case pointer_action::NONE:
+ break;
-void menu_textbox::recompute_metrics(uint32_t width, uint32_t height, float aspect)
-{
- menu::recompute_metrics(width, height, aspect);
+ case pointer_action::SCROLL_UP:
+ case pointer_action::SCROLL_DOWN:
+ {
+ // check for re-entry
+ bool redraw(false);
+ float const linetop(m_visible_top + ((pointer_action::SCROLL_DOWN == m_pointer_action) ? (float(m_window_lines - 1) * line_height()) : 0.0F));
+ float const linebottom(linetop + line_height());
+ auto const [x, y] = pointer_location();
+ bool const reentered(reentered_rect(m_last_pointer.first, m_last_pointer.second, x, y, m_line_bounds.first, linetop, m_line_bounds.second, linebottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (scroll_if_expired(now))
+ {
+ redraw = true;
+ m_scroll_repeat = now + std::chrono::milliseconds(100);
+ }
+ }
+ m_last_pointer = std::make_pair(x, y);
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+ }
- m_layout = std::nullopt;
+ case pointer_action::SCROLL_DRAG:
+ {
+ // scroll if it moved
+ auto const newtop(drag_scroll(
+ pointer_location().second, m_base_pointer.second, m_last_pointer.second, -line_height(),
+ m_scroll_base, 0, int(m_layout->lines() - m_window_lines)));
+ bool const scrolled(newtop != m_top_line);
+ m_top_line = newtop;
+
+ // catch the end of the gesture
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, scrolled);
+ }
+
+ case pointer_action::CHECK_EXIT:
+ if (uievt.pointer_released & 0x01)
+ return std::make_tuple((2 == uievt.pointer_clicks) ? IPT_UI_SELECT : IPT_INVALID, false, false);
+ else if (uievt.pointer_buttons & ~u32(0x01))
+ return std::make_tuple(IPT_INVALID, false, false);
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ return std::make_tuple(IPT_INVALID, false, false);
}
@@ -151,6 +238,62 @@ bool menu_textbox::custom_mouse_scroll(int lines)
}
+bool menu_textbox::handle(event const *ev)
+{
+ // deal with repeating scroll arrows
+ bool scrolled(false);
+ if ((pointer_action::SCROLL_UP == m_pointer_action) || (pointer_action::SCROLL_DOWN == m_pointer_action))
+ {
+ float const linetop(m_visible_top + ((pointer_action::SCROLL_DOWN == m_pointer_action) ? (float(m_window_lines - 1) * line_height()) : 0.0F));
+ float const linebottom(linetop + line_height());
+ if (pointer_in_rect(m_line_bounds.first, linetop, m_line_bounds.second, linebottom))
+ {
+ while (scroll_if_expired(std::chrono::steady_clock::now()))
+ {
+ scrolled = true;
+ m_scroll_repeat += std::chrono::milliseconds(100);
+ }
+ }
+ }
+
+ if (ev)
+ {
+ switch (ev->iptkey)
+ {
+ case IPT_UI_SELECT:
+ stack_pop();
+ return true;
+
+ case IPT_UI_UP:
+ --m_top_line;
+ return true;
+
+ case IPT_UI_DOWN:
+ ++m_top_line;
+ return true;
+
+ case IPT_UI_PAGE_UP:
+ m_top_line -= m_window_lines - 3;
+ return true;
+
+ case IPT_UI_PAGE_DOWN:
+ m_top_line += m_window_lines - 3;
+ return true;
+
+ case IPT_UI_HOME:
+ m_top_line = 0;
+ return true;
+
+ case IPT_UI_END:
+ m_top_line = m_layout->lines() - m_window_lines;
+ return true;
+ }
+ }
+
+ return scrolled;
+}
+
+
void menu_textbox::draw(uint32_t flags)
{
float const visible_width = 1.0F - (2.0F * lr_border());
@@ -162,10 +305,9 @@ void menu_textbox::draw(uint32_t flags)
float const maximum_width = visible_width - (2.0F * gutter_width());
draw_background();
- map_mouse();
// account for extra space at the top and bottom and the separator/item for closing
- float visible_main_menu_height = 1.0F - 2.0F * tb_border() - visible_extra_menu_height;
+ float visible_main_menu_height = 1.0F - (2.0F * tb_border()) - visible_extra_menu_height;
m_window_lines = int(std::trunc(visible_main_menu_height / line_height()));
// lay out the text if necessary
@@ -179,7 +321,7 @@ void menu_textbox::draw(uint32_t flags)
visible_main_menu_height = float(m_window_lines) * line_height();
// compute top/left of inner menu area by centering, if the menu is at the bottom of the extra, adjust
- float const visible_top = ((1.0F - (visible_main_menu_height + visible_extra_menu_height)) * 0.5F) + get_customtop();
+ m_visible_top = ((1.0F - (visible_main_menu_height + visible_extra_menu_height)) * 0.5F) + get_customtop();
// get width required to draw the sole menu item
menu_item const &pitem = item(0);
@@ -189,54 +331,68 @@ void menu_textbox::draw(uint32_t flags)
// compute text box size
float const x1 = visible_left + ((maximum_width - draw_width) * 0.5F);
- float const y1 = visible_top - tb_border();
+ float const y1 = m_visible_top - tb_border();
float const x2 = visible_left + visible_width - ((maximum_width - draw_width) * 0.5F);
- float const y2 = visible_top + visible_main_menu_height + tb_border() + extra_height;
+ float const y2 = m_visible_top + visible_main_menu_height + tb_border() + extra_height;
float const effective_left = x1 + gutter_width();
- float const line_x0 = x1 + 0.5F * UI_LINE_WIDTH;
- float const line_x1 = x2 - 0.5F * UI_LINE_WIDTH;
- float const separator = visible_top + float(m_window_lines) * line_height();
+ m_line_bounds = std::make_pair(x1 + (0.5F * UI_LINE_WIDTH), x2 - (0.5F * UI_LINE_WIDTH));
+ float const separator = m_visible_top + float(m_window_lines) * line_height();
ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
- int const visible_items = m_layout->lines();
- m_window_lines = (std::min)(visible_items, m_window_lines);
+ int const desired_lines = m_layout->lines();
+ int const drawn_lines = (std::min)(desired_lines, m_window_lines);
m_top_line = (std::max)(0, m_top_line);
- if (m_top_line + m_window_lines >= visible_items)
- m_top_line = visible_items - m_window_lines;
+ if ((m_top_line + drawn_lines) >= desired_lines)
+ m_top_line = desired_lines - drawn_lines;
- clear_hover();
if (m_top_line)
{
- // if we're on the top line, display the up arrow
- rgb_t fgcolor = ui().colors().text_color();
- if (mouse_in_rect(line_x0, visible_top, line_x1, visible_top + line_height()))
+ // if we're not showing the top line, display the up arrow
+ rgb_t fgcolor(ui().colors().text_color());
+ bool const hovered(pointer_in_rect(m_line_bounds.first, m_visible_top, m_line_bounds.second, m_visible_top + line_height()));
+ if (hovered && (pointer_action::SCROLL_UP == m_pointer_action))
+ {
+ fgcolor = ui().colors().selected_color();
+ highlight(
+ m_line_bounds.first, m_visible_top,
+ m_line_bounds.second, m_visible_top + line_height(),
+ ui().colors().selected_bg_color());
+ }
+ else if ((hovered && pointer_idle()) || (pointer_action::SCROLL_UP == m_pointer_action))
{
fgcolor = ui().colors().mouseover_color();
highlight(
- line_x0, visible_top,
- line_x1, visible_top + line_height(),
+ m_line_bounds.first, m_visible_top,
+ m_line_bounds.second, m_visible_top + line_height(),
ui().colors().mouseover_bg_color());
- set_hover(HOVER_ARROW_UP);
}
draw_arrow(
- 0.5F * (x1 + x2 - ud_arrow_width()), visible_top + (0.25F * line_height()),
- 0.5F * (x1 + x2 + ud_arrow_width()), visible_top + (0.75F * line_height()),
+ 0.5F * (x1 + x2 - ud_arrow_width()), m_visible_top + (0.25F * line_height()),
+ 0.5F * (x1 + x2 + ud_arrow_width()), m_visible_top + (0.75F * line_height()),
fgcolor, ROT0);
}
- if ((m_top_line + m_window_lines) < visible_items)
+ if ((m_top_line + m_window_lines) < desired_lines)
{
- // if we're on the bottom line, display the down arrow
- float const line_y = visible_top + float(m_window_lines - 1) * line_height();
- rgb_t fgcolor = ui().colors().text_color();
- if (mouse_in_rect(line_x0, line_y, line_x1, line_y + line_height()))
+ // if we're not showing the bottom line, display the down arrow
+ float const line_y(m_visible_top + float(m_window_lines - 1) * line_height());
+ rgb_t fgcolor(ui().colors().text_color());
+ bool const hovered(pointer_in_rect(m_line_bounds.first, line_y, m_line_bounds.second, line_y + line_height()));
+ if (hovered && (pointer_action::SCROLL_DOWN == m_pointer_action))
+ {
+ fgcolor = ui().colors().selected_color();
+ highlight(
+ m_line_bounds.first, line_y,
+ m_line_bounds.second, line_y + line_height(),
+ ui().colors().selected_bg_color());
+ }
+ else if ((hovered && pointer_idle()) || (pointer_action::SCROLL_DOWN == m_pointer_action))
{
fgcolor = ui().colors().mouseover_color();
highlight(
- line_x0, line_y,
- line_x1, line_y + line_height(),
+ m_line_bounds.first, line_y,
+ m_line_bounds.second, line_y + line_height(),
ui().colors().mouseover_bg_color());
- set_hover(HOVER_ARROW_DOWN);
}
draw_arrow(
0.5F * (x1 + x2 - ud_arrow_width()), line_y + (0.25F * line_height()),
@@ -245,11 +401,11 @@ void menu_textbox::draw(uint32_t flags)
}
// draw visible lines, minus 1 for top arrow and 1 for bottom arrow
- auto const text_lines = m_window_lines - (m_top_line ? 1 : 0) - (m_top_line + m_window_lines != visible_items);
+ auto const text_lines = drawn_lines - (m_top_line ? 1 : 0) - ((m_top_line + drawn_lines) != desired_lines);
m_layout->emit(
container(),
m_top_line ? (m_top_line + 1) : 0, text_lines,
- effective_left, visible_top + (m_top_line ? line_height() : 0.0F));
+ effective_left, m_visible_top + (m_top_line ? line_height() : 0.0F));
// add visual separator before the "return to prevous menu" item
container().add_line(
@@ -257,13 +413,10 @@ void menu_textbox::draw(uint32_t flags)
x2, separator + (0.5F * line_height()),
UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- float const line_y0 = separator + line_height();
+ float const line_y0 = m_visible_top + float(m_window_lines + 1) * line_height();
float const line_y1 = line_y0 + line_height();
- if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
- set_hover(0);
-
- highlight(line_x0, line_y0, line_x1, line_y1, ui().colors().selected_bg_color());
+ highlight(m_line_bounds.first, line_y0, m_line_bounds.second, line_y1, ui().colors().selected_bg_color());
ui().draw_text_full(
container(), itemtext,
effective_left, line_y0, draw_width,
@@ -274,7 +427,37 @@ void menu_textbox::draw(uint32_t flags)
line_height());
// if there is something special to add, do it by calling the virtual method
- custom_render(get_selection_ref(), get_customtop(), get_custombottom(), x1, y1, x2, y2);
+ custom_render(flags, get_selection_ref(), get_customtop(), get_custombottom(), x1, y1, x2, y2);
+}
+
+
+bool menu_textbox::scroll_if_expired(std::chrono::steady_clock::time_point now)
+{
+ if (now < m_scroll_repeat)
+ return false;
+
+ if (pointer_action::SCROLL_DOWN == m_pointer_action)
+ {
+ if ((m_top_line + m_window_lines) < m_layout->lines())
+ ++m_top_line;
+ if ((m_top_line + m_window_lines) == m_layout->lines())
+ m_pointer_action = pointer_action::NONE;
+ }
+ else
+ {
+ if (0 < m_top_line)
+ --m_top_line;
+ if (!m_top_line)
+ m_pointer_action = pointer_action::NONE;
+ }
+ return true;
+}
+
+
+inline bool menu_textbox::pointer_in_line(float y, int line) const
+{
+ float const top(m_visible_top + (float(line) * line_height()));
+ return (top <= y) && ((top + line_height()) > y);
}
@@ -309,7 +492,7 @@ void menu_fixed_textbox::recompute_metrics(uint32_t width, uint32_t height, floa
}
-void menu_fixed_textbox::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+void menu_fixed_textbox::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
std::string_view const toptext[] = { m_heading };
draw_text_box(
@@ -367,10 +550,4 @@ void menu_fixed_textbox::populate()
{
}
-
-bool menu_fixed_textbox::handle(event const *ev)
-{
- return ev && handle_key(ev->iptkey);
-}
-
} // namespace ui
diff --git a/src/frontend/mame/ui/textbox.h b/src/frontend/mame/ui/textbox.h
index af755761d4b..3a123980c83 100644
--- a/src/frontend/mame/ui/textbox.h
+++ b/src/frontend/mame/ui/textbox.h
@@ -13,8 +13,11 @@
#include "ui/menu.h"
#include "ui/text.h"
+#include <chrono>
#include <optional>
#include <string>
+#include <tuple>
+#include <utility>
namespace ui {
@@ -28,22 +31,44 @@ protected:
menu_textbox(mame_ui_manager &mui, render_container &container);
void reset_layout();
- bool handle_key(int key);
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) = 0;
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
+ virtual std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
virtual bool custom_mouse_scroll(int lines) override;
+ virtual bool handle(event const *ev) override;
+
private:
+ enum class pointer_action
+ {
+ NONE,
+ SCROLL_UP,
+ SCROLL_DOWN,
+ SCROLL_DRAG,
+ CHECK_EXIT
+ };
+
virtual void draw(uint32_t flags) override;
+ bool scroll_if_expired(std::chrono::steady_clock::time_point now);
+ bool pointer_in_line(float y, int line) const;
+
std::optional<text_layout> m_layout;
+ std::pair<float, float> m_line_bounds;
+ float m_visible_top;
float m_layout_width;
float m_desired_width;
int m_desired_lines;
int m_window_lines;
int m_top_line;
+
+ pointer_action m_pointer_action;
+ std::chrono::steady_clock::time_point m_scroll_repeat;
+ std::pair<float, float> m_base_pointer;
+ std::pair<float, float> m_last_pointer;
+ int m_scroll_base;
};
@@ -59,13 +84,12 @@ public:
protected:
virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
virtual void populate() override;
- virtual bool handle(event const *ev) override;
std::string const m_heading;
std::string const m_content;
diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp
index 09a8d24bdd1..a9a4fd5ceed 100644
--- a/src/frontend/mame/ui/ui.cpp
+++ b/src/frontend/mame/ui/ui.cpp
@@ -153,6 +153,34 @@ static uint32_t const mouse_bitmap[32*32] =
};
+enum class mame_ui_manager::ui_callback_type : int
+{
+ NOINPUT,
+ GENERAL,
+ MODAL,
+ MENU,
+ CUSTOM
+};
+
+
+struct mame_ui_manager::active_pointer
+{
+ active_pointer(ui_event const &event) : target(event.target), type(event.pointer_type), ptrid(event.pointer_id), x(-1.0F), y(-1.0F)
+ {
+ }
+
+ bool operator<(std::pair<render_target *, u16> const &val) const noexcept
+ {
+ return std::make_pair(target, ptrid) < val;
+ }
+
+ render_target *target;
+ osd::ui_event_handler::pointer type;
+ u16 ptrid;
+ float x, y;
+};
+
+
//-------------------------------------------------
// ctor - set up the user interface
//-------------------------------------------------
@@ -161,7 +189,7 @@ mame_ui_manager::mame_ui_manager(running_machine &machine)
: ui_manager(machine)
, m_font()
, m_handler_callback()
- , m_handler_callback_type(ui_callback_type::GENERAL)
+ , m_handler_callback_type(ui_callback_type::NOINPUT)
, m_ui_active(true)
, m_single_step(false)
, m_showfps(false)
@@ -171,8 +199,8 @@ mame_ui_manager::mame_ui_manager(running_machine &machine)
, m_mouse_bitmap(32, 32)
, m_mouse_arrow_texture(nullptr)
, m_mouse_show(false)
- , m_mouse_target(-1)
- , m_mouse_position(0, 0)
+ , m_update_pointers(false)
+ , m_pointers_changed(false)
, m_target_font_height(0)
, m_has_warnings(false)
, m_unthrottle_mute(false)
@@ -182,7 +210,8 @@ mame_ui_manager::mame_ui_manager(running_machine &machine)
, m_imperfect_features()
, m_last_launch_time(std::time_t(-1))
, m_last_warning_time(std::time_t(-1))
-{ }
+{
+}
mame_ui_manager::~mame_ui_manager()
{
@@ -203,7 +232,7 @@ void mame_ui_manager::init()
// more initialization
set_handler(
- ui_callback_type::GENERAL,
+ ui_callback_type::NOINPUT,
handler_callback_func(
[this] (render_container &container) -> uint32_t
{
@@ -214,6 +243,7 @@ void mame_ui_manager::init()
m_mouse_show = machine().system().flags & machine_flags::CLICKABLE_ARTWORK ? true : false;
// request notification callbacks
+ machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&mame_ui_manager::frame_update, this));
machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&mame_ui_manager::exit, this));
machine().configuration().config_register(
"ui_warnings",
@@ -239,6 +269,25 @@ void mame_ui_manager::update_target_font_height()
//-------------------------------------------------
+// exit - called for each emulated frame
+//-------------------------------------------------
+
+void mame_ui_manager::frame_update()
+{
+ // this hackery is needed to ensure natural keyboard and clickable artwork input is in sync with I/O ports
+ if (ui_callback_type::GENERAL == m_handler_callback_type)
+ {
+ process_ui_events();
+ for (auto *target = machine().render().first_target(); target; target = target->next())
+ {
+ if (!target->hidden())
+ target->update_pointer_fields();
+ }
+ }
+}
+
+
+//-------------------------------------------------
// exit - clean up ourselves on exit
//-------------------------------------------------
@@ -366,6 +415,12 @@ void mame_ui_manager::initialize(running_machine &machine)
void mame_ui_manager::set_handler(ui_callback_type callback_type, handler_callback_func &&callback)
{
+ m_active_pointers.clear();
+ if (!m_display_pointers.empty())
+ {
+ m_display_pointers.clear();
+ m_pointers_changed = true;
+ }
m_handler_callback = std::move(callback);
m_handler_callback_type = callback_type;
}
@@ -625,7 +680,11 @@ void mame_ui_manager::set_startup_text(const char *text, bool force)
bool mame_ui_manager::update_and_render(render_container &container)
{
// always start clean
- container.empty();
+ for (auto &target : machine().render().targets())
+ {
+ if (target.ui_container())
+ target.ui_container()->empty();
+ }
// if we're paused, dim the whole screen
if (machine().phase() >= machine_phase::RESET && (single_step() || machine().paused()))
@@ -657,7 +716,12 @@ bool mame_ui_manager::update_and_render(render_container &container)
if (machine().phase() >= machine_phase::RESET)
mame_machine_manager::instance()->cheat().render_text(*this, container);
+ // draw the FPS counter if it should be visible
+ if (show_fps_counter())
+ draw_fps_counter(container);
+
// call the current UI handler
+ machine().ui_input().check_ui_inputs();
uint32_t const handler_result = m_handler_callback(container);
// display any popup messages
@@ -666,43 +730,34 @@ bool mame_ui_manager::update_and_render(render_container &container)
else
m_popup_text_end = 0;
- // display the internal mouse cursor
- bool mouse_moved = false;
+ // display the internal pointers
+ bool const pointer_update = m_pointers_changed;
+ m_pointers_changed = false;
if (m_mouse_show || (is_menu_active() && machine().options().ui_mouse()))
{
- int32_t mouse_target_x, mouse_target_y;
- bool mouse_button;
- render_target *const mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
-
- float mouse_y = -1, mouse_x = -1;
- if (mouse_target && mouse_target->map_point_container(mouse_target_x, mouse_target_y, container, mouse_x, mouse_y))
+ const float cursor_size = 0.6 * get_line_height();
+ for (auto const &pointer : m_display_pointers)
{
- const float cursor_size = 0.6 * get_line_height();
- container.add_quad(mouse_x, mouse_y, mouse_x + cursor_size * container.manager().ui_aspect(&container), mouse_y + cursor_size, colors().text_color(), m_mouse_arrow_texture, PRIMFLAG_ANTIALIAS(1) | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- if ((m_mouse_target != mouse_target->index()) || (std::make_pair(mouse_x, mouse_y) != m_mouse_position))
- {
- m_mouse_target = mouse_target->index();
- m_mouse_position = std::make_pair(mouse_x, mouse_y);
- mouse_moved = true;
- }
+ render_container &container = *pointer.target.get().ui_container();
+ container.add_quad(
+ pointer.x,
+ pointer.y,
+ pointer.x + cursor_size * container.manager().ui_aspect(&container),
+ pointer.y + cursor_size,
+ rgb_t::white(),
+ m_mouse_arrow_texture,
+ PRIMFLAG_ANTIALIAS(1) | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
- else if (0 <= m_mouse_target)
- {
- m_mouse_target = -1;
- mouse_moved = true;
- }
- }
- else if (0 <= m_mouse_target)
- {
- m_mouse_target = -1;
- mouse_moved = true;
}
// cancel takes us back to the in-game handler
if (handler_result & HANDLER_CANCEL)
+ {
+ machine().ui_input().reset();
set_handler(ui_callback_type::GENERAL, handler_callback_func(&mame_ui_manager::handler_ingame, this));
+ }
- return mouse_moved || (handler_result & HANDLER_UPDATE);
+ return pointer_update || (handler_result & HANDLER_UPDATE);
}
@@ -954,7 +1009,7 @@ void mame_ui_manager::draw_message_window(render_container &container, std::stri
void mame_ui_manager::show_fps_temp(double seconds)
{
if (!m_showfps)
- m_showfps_end = osd_ticks() + seconds * osd_ticks_per_second();
+ m_showfps_end = std::max<osd_ticks_t>(osd_ticks() + seconds * osd_ticks_per_second(), m_showfps_end);
}
@@ -966,10 +1021,7 @@ void mame_ui_manager::set_show_fps(bool show)
{
m_showfps = show;
if (!show)
- {
- m_showfps = 0;
m_showfps_end = 0;
- }
}
@@ -990,7 +1042,7 @@ bool mame_ui_manager::show_fps() const
bool mame_ui_manager::show_fps_counter()
{
- bool result = m_showfps || osd_ticks() < m_showfps_end;
+ bool const result = m_showfps || (osd_ticks() < m_showfps_end);
if (!result)
m_showfps_end = 0;
return result;
@@ -1025,17 +1077,13 @@ bool mame_ui_manager::show_profiler() const
void mame_ui_manager::show_menu()
{
- set_handler(ui_callback_type::MENU, ui::menu::get_ui_handler(*this));
-}
-
-
-//-------------------------------------------------
-// show_mouse - change mouse status
-//-------------------------------------------------
+ for (auto *target = machine().render().first_target(); target; target = target->next())
+ {
+ if (!target->hidden())
+ target->forget_pointers();
+ }
-void mame_ui_manager::show_mouse(bool status)
-{
- m_mouse_show = status;
+ set_handler(ui_callback_type::MENU, ui::menu::get_ui_handler(*this));
}
@@ -1044,10 +1092,9 @@ void mame_ui_manager::show_mouse(bool status)
// UI handler is active
//-------------------------------------------------
-bool mame_ui_manager::is_menu_active(void)
+bool mame_ui_manager::is_menu_active()
{
- return m_handler_callback_type == ui_callback_type::MENU
- || m_handler_callback_type == ui_callback_type::VIEWER;
+ return m_handler_callback_type == ui_callback_type::MENU;
}
@@ -1057,48 +1104,119 @@ bool mame_ui_manager::is_menu_active(void)
***************************************************************************/
//-------------------------------------------------
-// process_natural_keyboard - processes any
-// natural keyboard input
+// process_ui_events - processes queued UI input
+// events
//-------------------------------------------------
-void mame_ui_manager::process_natural_keyboard()
+void mame_ui_manager::process_ui_events()
{
+ // process UI events
+ bool const use_natkbd(machine().natkeyboard().in_use() && (machine().phase() == machine_phase::RUNNING));
ui_event event;
-
- // loop while we have interesting events
while (machine().ui_input().pop_event(&event))
{
- // if this was a UI_EVENT_CHAR event, post it
- if (event.event_type == ui_event::type::IME_CHAR)
- machine().natkeyboard().post_char(event.ch);
- }
+ switch (event.event_type)
+ {
+ case ui_event::type::NONE:
+ case ui_event::type::WINDOW_FOCUS:
+ case ui_event::type::WINDOW_DEFOCUS:
+ case ui_event::type::MOUSE_WHEEL:
+ break;
- // process natural keyboard keys that don't get UI_EVENT_CHARs
- for (int i = 0; i < std::size(non_char_keys); i++)
- {
- // identify this keycode
- input_item_id itemid = non_char_keys[i];
- input_code code = machine().input().code_from_itemid(itemid);
+ case ui_event::type::POINTER_UPDATE:
+ if (event.target)
+ {
+ if (osd::ui_event_handler::pointer::TOUCH != event.pointer_type)
+ {
+ m_update_pointers = true;
+ auto pos(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), std::make_pair(event.target, event.pointer_id)));
+ if ((m_active_pointers.end() == pos) || (pos->target != event.target) || (pos->ptrid != event.pointer_id))
+ pos = m_active_pointers.emplace(pos, event);
+ else
+ assert(pos->type == event.pointer_type);
+ event.target->map_point_container(event.pointer_x, event.pointer_y, *event.target->ui_container(), pos->x, pos->y);
+ }
- // ...and determine if it is pressed
- bool pressed = machine().input().code_pressed(code);
+ event.target->pointer_updated(
+ event.pointer_type, event.pointer_id, event.pointer_device,
+ event.pointer_x, event.pointer_y,
+ event.pointer_buttons, event.pointer_pressed, event.pointer_released,
+ event.pointer_clicks);
+ }
+ break;
- // figure out whey we are in the key_down map
- uint8_t *key_down_ptr = &m_non_char_keys_down[i / 8];
- uint8_t key_down_mask = 1 << (i % 8);
+ case ui_event::type::POINTER_LEAVE:
+ if (event.target)
+ {
+ auto const pos(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), std::make_pair(event.target, event.pointer_id)));
+ if (m_active_pointers.end() != pos)
+ {
+ m_update_pointers = true;
+ m_active_pointers.erase(pos);
+ }
- if (pressed && !(*key_down_ptr & key_down_mask))
- {
- // this key is now down
- *key_down_ptr |= key_down_mask;
+ event.target->pointer_left(
+ event.pointer_type, event.pointer_id, event.pointer_device,
+ event.pointer_x, event.pointer_y,
+ event.pointer_released,
+ event.pointer_clicks);
+ }
+ break;
+
+ case ui_event::type::POINTER_ABORT:
+ if (event.target)
+ {
+ auto const pos(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), std::make_pair(event.target, event.pointer_id)));
+ if (m_active_pointers.end() != pos)
+ {
+ m_update_pointers = true;
+ m_active_pointers.erase(pos);
+ }
- // post the key
- machine().natkeyboard().post_char(UCHAR_MAMEKEY_BEGIN + code.item_id());
+ event.target->pointer_aborted(
+ event.pointer_type, event.pointer_id, event.pointer_device,
+ event.pointer_x, event.pointer_y,
+ event.pointer_released,
+ event.pointer_clicks);
+ }
+ break;
+
+ case ui_event::type::IME_CHAR:
+ if (use_natkbd)
+ machine().natkeyboard().post_char(event.ch);
+ break;
}
- else if (!pressed && (*key_down_ptr & key_down_mask))
+ }
+
+ // process natural keyboard keys that don't get IME text input events
+ if (use_natkbd)
+ {
+ for (int i = 0; i < std::size(non_char_keys); i++)
{
- // this key is now up
- *key_down_ptr &= ~key_down_mask;
+ // identify this keycode
+ input_item_id itemid = non_char_keys[i];
+ input_code code = machine().input().code_from_itemid(itemid);
+
+ // ...and determine if it is pressed
+ bool pressed = machine().input().code_pressed(code);
+
+ // figure out whey we are in the key_down map
+ uint8_t *key_down_ptr = &m_non_char_keys_down[i / 8];
+ uint8_t key_down_mask = 1 << (i % 8);
+
+ if (pressed && !(*key_down_ptr & key_down_mask))
+ {
+ // this key is now down
+ *key_down_ptr |= key_down_mask;
+
+ // post the key
+ machine().natkeyboard().post_char(UCHAR_MAMEKEY_BEGIN + code.item_id());
+ }
+ else if (!pressed && (*key_down_ptr & key_down_mask))
+ {
+ // this key is now up
+ *key_down_ptr &= ~key_down_mask;
+ }
}
}
}
@@ -1226,10 +1344,6 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
bool is_paused = machine().paused();
- // first draw the FPS counter
- if (show_fps_counter())
- draw_fps_counter(container);
-
// draw the profiler if visible
if (show_profiler())
draw_profiler(container);
@@ -1263,9 +1377,17 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
}
}
- // is the natural keyboard enabled?
- if (machine().natkeyboard().in_use() && (machine().phase() == machine_phase::RUNNING))
- process_natural_keyboard();
+ // process UI events and update pointers if necessary
+ process_ui_events();
+ if (m_update_pointers)
+ {
+ display_pointer_vector pointers;
+ pointers.reserve(m_active_pointers.size());
+ for (auto const &pointer : m_active_pointers)
+ pointers.emplace_back(display_pointer{ *pointer.target, pointer.type, pointer.x, pointer.y });
+ set_pointers(pointers.begin(), pointers.end());
+ m_update_pointers = false;
+ }
if (!ui_disabled)
{
@@ -1309,11 +1431,17 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
// handle a request to display graphics/palette
if (machine().ui_input().pressed(IPT_UI_SHOW_GFX))
{
+ for (auto *target = machine().render().first_target(); target; target = target->next())
+ {
+ if (!target->hidden())
+ target->forget_pointers();
+ }
+
if (!is_paused)
machine().pause();
using namespace std::placeholders;
set_handler(
- ui_callback_type::VIEWER,
+ ui_callback_type::MENU,
handler_callback_func(
[this, is_paused] (render_container &container) -> uint32_t
{
@@ -2266,6 +2394,23 @@ void mame_ui_manager::menu_reset()
}
+bool mame_ui_manager::set_ui_event_handler(std::function<bool ()> &&handler)
+{
+ // only allow takeover if there's nothing else happening
+ if (ui_callback_type::GENERAL != m_handler_callback_type)
+ return false;
+
+ set_handler(
+ ui_callback_type::CUSTOM,
+ handler_callback_func(
+ [cb = std::move(handler)] (render_container &container) -> uint32_t
+ {
+ return !cb() ? HANDLER_CANCEL : 0;
+ }));
+ return true;
+}
+
+
//-------------------------------------------------
// get_general_input_setting - get the current
// default setting for an input type (useful for
diff --git a/src/frontend/mame/ui/ui.h b/src/frontend/mame/ui/ui.h
index 662d8d06fc1..231a6de15cb 100644
--- a/src/frontend/mame/ui/ui.h
+++ b/src/frontend/mame/ui/ui.h
@@ -13,18 +13,23 @@
#pragma once
-#include "render.h"
-#include "moptions.h"
#include "language.h"
#include "ui/uimain.h"
#include "ui/menuitem.h"
+#include "ui/moptions.h"
#include "ui/slider.h"
#include "ui/text.h"
+#include "render.h"
+
+#include "interface/uievents.h"
+
#include <any>
#include <cassert>
#include <ctime>
+#include <functional>
#include <set>
+#include <string>
#include <string_view>
#include <typeindex>
#include <typeinfo>
@@ -63,14 +68,6 @@ class laserdisc_device;
class mame_ui_manager;
-enum class ui_callback_type
-{
- GENERAL,
- MODAL,
- MENU,
- VIEWER
-};
-
// ======================> ui_colors
class ui_colors
@@ -132,6 +129,18 @@ public:
OPAQUE_
};
+ struct display_pointer
+ {
+ std::reference_wrapper<render_target> target;
+ osd::ui_event_handler::pointer type;
+ float x, y;
+
+ bool operator!=(display_pointer const &that) const noexcept
+ {
+ return (&target.get() != &that.target.get()) || (type != that.type) || (x != that.x) || (y != that.y);
+ }
+ };
+
// construction/destruction
mame_ui_manager(running_machine &machine);
~mame_ui_manager();
@@ -184,7 +193,6 @@ public:
void set_show_profiler(bool show);
bool show_profiler() const;
void show_menu();
- void show_mouse(bool status);
virtual bool is_menu_active() override;
bool can_paste();
bool found_machine_warnings() const { return m_has_warnings; }
@@ -195,8 +203,39 @@ public:
void draw_fps_counter(render_container &container);
void draw_profiler(render_container &container);
+ // pointer display
+ template <typename T>
+ void set_pointers(T first, T last)
+ {
+ auto dest = m_display_pointers.begin();
+ while ((m_display_pointers.end() != dest) && (first != last))
+ {
+ if (*first != *dest)
+ {
+ *dest = *first;
+ m_pointers_changed = true;
+ }
+ ++dest;
+ ++first;
+ }
+ if (m_display_pointers.end() != dest)
+ {
+ m_display_pointers.erase(dest, m_display_pointers.end());
+ m_pointers_changed = true;
+ }
+ else
+ {
+ while (first != last)
+ {
+ m_display_pointers.emplace_back(*first);
+ m_pointers_changed = true;
+ ++first;
+ }
+ }
+ }
+
// slider controls
- std::vector<ui::menu_item>& get_slider_list();
+ std::vector<ui::menu_item> &get_slider_list();
// metrics
float target_font_height() const { return m_target_font_height; }
@@ -205,7 +244,7 @@ public:
void update_target_font_height();
// other
- void process_natural_keyboard();
+ void process_ui_events();
ui::text_layout create_layout(render_container &container, float width = 1.0, ui::text_layout::text_justify justify = ui::text_layout::text_justify::LEFT, ui::text_layout::word_wrapping wrap = ui::text_layout::word_wrapping::WORD);
void set_image_display_enabled(bool image_display_enabled) { m_image_display_enabled = image_display_enabled; }
bool image_display_enabled() const { return m_image_display_enabled; }
@@ -215,6 +254,7 @@ public:
virtual void popup_time_string(int seconds, std::string message) override;
virtual void menu_reset() override;
+ virtual bool set_ui_event_handler(std::function<bool ()> &&handler) override;
template <typename Owner, typename Data, typename... Param>
Data &get_session_data(Param &&... args)
@@ -232,9 +272,15 @@ public:
std::string get_general_input_setting(ioport_type type, int player = 0, input_seq_type seqtype = SEQ_TYPE_STANDARD);
private:
+ enum class ui_callback_type : int;
+
+ struct active_pointer;
+
using handler_callback_func = delegate<uint32_t (render_container &)>;
using device_feature_set = std::set<std::pair<std::string, std::string> >;
using session_data_map = std::unordered_map<std::type_index, std::any>;
+ using active_pointer_vector = std::vector<active_pointer>;
+ using display_pointer_vector = std::vector<display_pointer>;
// instance variables
std::unique_ptr<render_font> m_font;
@@ -248,11 +294,13 @@ private:
osd_ticks_t m_popup_text_end;
std::unique_ptr<uint8_t []> m_non_char_keys_down;
+ active_pointer_vector m_active_pointers;
+ display_pointer_vector m_display_pointers;
bitmap_argb32 m_mouse_bitmap;
render_texture * m_mouse_arrow_texture;
bool m_mouse_show;
- int m_mouse_target;
- std::pair<float, float> m_mouse_position;
+ bool m_update_pointers;
+ bool m_pointers_changed;
ui_options m_ui_options;
ui_colors m_ui_colors;
@@ -280,6 +328,7 @@ private:
// private methods
void set_handler(ui_callback_type callback_type, handler_callback_func &&callback);
+ void frame_update();
void exit();
void config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
void config_save(config_type cfg_type, util::xml::data_node *parentnode);
diff --git a/src/frontend/mame/ui/utils.cpp b/src/frontend/mame/ui/utils.cpp
index b83abc5c277..b0107589e04 100644
--- a/src/frontend/mame/ui/utils.cpp
+++ b/src/frontend/mame/ui/utils.cpp
@@ -204,23 +204,16 @@ public:
virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function<void (Base &)> &&handler) override
{
- if (m_choices.empty())
- {
- handler(*this);
- }
- else
- {
- menu::stack_push<menu_selector>(
- mui, container,
- _("Filter"), // TODO: get localised name of filter in here somehow
- std::vector<std::string>(m_choices), // ouch, a vector copy!
- m_selection,
- [this, cb = std::move(handler)] (int selection)
- {
- m_selection = selection;
- cb(*this);
- });
- }
+ menu::stack_push<menu_selector>(
+ mui, container,
+ _("Filter"), // TODO: get localised name of filter in here somehow
+ std::vector<std::string>(m_choices), // ouch, a vector copy!
+ m_selection,
+ [this, cb = std::move(handler)] (int selection)
+ {
+ m_selection = selection;
+ cb(*this);
+ });
}
virtual bool wants_adjuster() const override { return have_choices(); }
diff --git a/src/frontend/mame/ui/utils.h b/src/frontend/mame/ui/utils.h
index c0a4e1d30ca..04cb25a840c 100644
--- a/src/frontend/mame/ui/utils.h
+++ b/src/frontend/mame/ui/utils.h
@@ -362,42 +362,12 @@ private:
enum
{
- RP_FIRST = 0,
- RP_IMAGES = RP_FIRST,
- RP_INFOS,
- RP_LAST = RP_INFOS
-};
-
-enum
-{
SHOW_PANELS = 0,
HIDE_LEFT_PANEL,
HIDE_RIGHT_PANEL,
HIDE_BOTH
};
-enum
-{
- HOVER_DAT_UP = -1000,
- HOVER_DAT_DOWN,
- HOVER_UI_LEFT,
- HOVER_UI_RIGHT,
- HOVER_ARROW_UP,
- HOVER_ARROW_DOWN,
- HOVER_B_FAV,
- HOVER_B_EXPORT,
- HOVER_B_AUDIT,
- HOVER_B_DATS,
- HOVER_BACKTRACK,
- HOVER_RPANEL_ARROW,
- HOVER_LPANEL_ARROW,
- HOVER_FILTER_FIRST,
- HOVER_FILTER_LAST = HOVER_FILTER_FIRST + std::max<int>(ui::machine_filter::COUNT, ui::software_filter::COUNT),
- HOVER_RP_FIRST,
- HOVER_RP_LAST = HOVER_RP_FIRST + 1 + RP_LAST,
- HOVER_INFO_TEXT
-};
-
// FIXME: this stuff shouldn't all be globals
// GLOBAL CLASS
diff --git a/src/frontend/mame/ui/viewgfx.cpp b/src/frontend/mame/ui/viewgfx.cpp
index c4a6ecdbf68..cbb104d5460 100644
--- a/src/frontend/mame/ui/viewgfx.cpp
+++ b/src/frontend/mame/ui/viewgfx.cpp
@@ -65,6 +65,81 @@ public:
if (!m_machine.paused())
m_bitmap_dirty = true;
+ // handle pointer events to show hover info
+ ui_event event;
+ while (m_machine.ui_input().pop_event(&event))
+ {
+ switch (event.event_type)
+ {
+ case ui_event::type::POINTER_UPDATE:
+ {
+ // ignore pointer input in windows other than the one that displays the UI
+ render_target &target(m_machine.render().ui_target());
+ if (&target != event.target)
+ break;
+
+ // don't change if the current pointer has buttons pressed and this one doesn't
+ if (event.pointer_id == m_current_pointer)
+ {
+ assert(m_pointer_type == event.pointer_type);
+ m_pointer_buttons = event.pointer_buttons;
+ m_pointer_inside = target.map_point_container(
+ event.pointer_x,
+ event.pointer_y,
+ container,
+ m_pointer_x,
+ m_pointer_y);
+ }
+ else if ((0 > m_current_pointer) || (!m_pointer_buttons && (!m_pointer_inside || event.pointer_buttons)))
+ {
+ float x, y;
+ bool const inside(target.map_point_container(event.pointer_x, event.pointer_y, container, x, y));
+ if ((0 > m_current_pointer) || event.pointer_buttons || (!m_pointer_inside && inside))
+ {
+ m_current_pointer = event.pointer_id;
+ m_pointer_type = event.pointer_type;
+ m_pointer_buttons = event.pointer_buttons;
+ m_pointer_x = x;
+ m_pointer_y = y;
+ m_pointer_inside = inside;
+ }
+ }
+ }
+ break;
+
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ {
+ // if this was our pointer, we've lost it
+ render_target &target(m_machine.render().ui_target());
+ if ((&target == event.target) && (event.pointer_id == m_current_pointer))
+ {
+ // keep the pointer position and type so we can show touch locations after release
+ m_current_pointer = -1;
+ m_pointer_buttons = 0U;
+ m_pointer_inside = target.map_point_container(
+ event.pointer_x,
+ event.pointer_y,
+ container,
+ m_pointer_x,
+ m_pointer_y);
+ }
+ }
+ break;
+
+ // ignore anything that isn't pointer-related
+ default:
+ break;
+ }
+ }
+
+ // always draw non-touch pointer
+ mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } };
+ if (m_pointer_inside && (0 <= m_current_pointer) && (ui_event::pointer::TOUCH != m_pointer_type))
+ mui.set_pointers(std::begin(pointers), std::end(pointers));
+ else
+ mui.set_pointers(std::begin(pointers), std::begin(pointers));
+
// try to display the selected view
while (true)
{
@@ -519,6 +594,13 @@ private:
if (input.pressed(IPT_UI_SELECT))
{
m_mode = view((int(m_mode) + 1) % 3);
+ if (0 > m_current_pointer)
+ {
+ m_pointer_type = ui_event::pointer::UNKNOWN;
+ m_pointer_x = -1.0F;
+ m_pointer_x = -1.0F;
+ m_pointer_inside = false;
+ }
m_bitmap_dirty = true;
}
@@ -543,6 +625,12 @@ private:
if (!uistate)
m_machine.resume();
m_machine.ui_input().reset();
+ m_current_pointer = -1;
+ m_pointer_type = ui_event::pointer::UNKNOWN;
+ m_pointer_buttons = 0U;
+ m_pointer_x = -1.0F;
+ m_pointer_y = -1.0F;
+ m_pointer_inside = false;
m_bitmap_dirty = true;
return mame_ui_manager::HANDLER_CANCEL;
}
@@ -595,20 +683,24 @@ private:
bool map_mouse(render_container &container, render_bounds const &clip, float &x, float &y) const
{
- int32_t target_x, target_y;
- bool button;
- render_target *const target = m_machine.ui_input().find_mouse(&target_x, &target_y, &button);
- if (!target)
- return false;
- else if (!target->map_point_container(target_x, target_y, container, x, y))
+ if (((0 > m_current_pointer) && (m_pointer_type != ui_event::pointer::TOUCH)) || !m_pointer_inside)
return false;
- else
- return clip.includes(x, y);
+
+ x = m_pointer_x;
+ y = m_pointer_y;
+ return clip.includes(x, y);
}
running_machine &m_machine;
view m_mode = view::PALETTE;
+ s32 m_current_pointer = -1;
+ ui_event::pointer m_pointer_type = ui_event::pointer::UNKNOWN;
+ u32 m_pointer_buttons = 0U;
+ float m_pointer_x = -1.0F;
+ float m_pointer_y = -1.0F;
+ bool m_pointer_inside = false;
+
bitmap_rgb32 m_bitmap;
render_texture *m_texture = nullptr;
bool m_bitmap_dirty = false;
@@ -961,6 +1053,11 @@ uint32_t gfx_viewer::handle_palette(mame_ui_manager &mui, render_container &cont
index,
col.a(), col.r(), col.g(), col.b());
}
+
+ // keep touch pointer displayed after release so they know what it's pointing at
+ mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } };
+ if (ui_event::pointer::TOUCH == m_pointer_type)
+ mui.set_pointers(std::begin(pointers), std::end(pointers));
}
}
@@ -1163,6 +1260,11 @@ uint32_t gfx_viewer::handle_gfxset(mame_ui_manager &mui, render_container &conta
code, set.m_color,
xpixel, ypixel,
gfx.colorbase() + (set.m_color * gfx.granularity()) + pixdata);
+
+ // keep touch pointer displayed after release so they know what it's pointing at
+ mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } };
+ if (ui_event::pointer::TOUCH == m_pointer_type)
+ mui.set_pointers(std::begin(pointers), std::end(pointers));
}
}
if (!found_pixel)
@@ -1331,6 +1433,11 @@ uint32_t gfx_viewer::handle_tilemap(mame_ui_manager &mui, render_container &cont
_("gfxview", " (%1$u %2$u) = GFX%3$u #%4$X:%5$X"),
col * tilemap.tilewidth(), row * tilemap.tileheight(),
gfxnum, code, color);
+
+ // keep touch pointer displayed after release so they know what it's pointing at
+ mame_ui_manager::display_pointer pointers[1]{ { m_machine.render().ui_target(), m_pointer_type, m_pointer_x, m_pointer_y } };
+ if (ui_event::pointer::TOUCH == m_pointer_type)
+ mui.set_pointers(std::begin(pointers), std::end(pointers));
}
else
{
diff --git a/src/mame/layout/alphie.lay b/src/mame/layout/alphie.lay
index 614ba89329d..3d3b591c31a 100644
--- a/src/mame/layout/alphie.lay
+++ b/src/mame/layout/alphie.lay
@@ -66,7 +66,7 @@ authors:hap
<element ref="text_q"><bounds x="8" y="0" width="10" height="2" /></element>
<element ref="text_ans"><bounds x="8" y="3" width="10" height="2" /></element>
<element ref="text_act"><bounds x="8" y="6" width="10" height="2" /></element>
- <element ref="nothing" inputtag="IN.1" inputmask="0x00" inputraw="yes"><bounds x="14" y="-1" width="4" height="10" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="14" y="-1" width="4" height="10" /></element>
<element ref="switch_qa" inputtag="IN.1" inputmask="0x1f" inputraw="yes"><bounds x="15" y="0" width="2" height="2" /></element>
<element ref="switch_qa" inputtag="IN.2" inputmask="0x1f" inputraw="yes"><bounds x="15" y="3" width="2" height="2" /></element>
<element ref="switch_act" inputtag="IN.3" inputmask="0x0f" inputraw="yes"><bounds x="15" y="6" width="2" height="2" /></element>
diff --git a/src/mame/layout/autorace.lay b/src/mame/layout/autorace.lay
index 61652a4acf2..8a4b215cb35 100644
--- a/src/mame/layout/autorace.lay
+++ b/src/mame/layout/autorace.lay
@@ -61,7 +61,7 @@ authors:hap
<!-- bezel -->
<element ref="lane"><bounds x="18.69" y="9.7" width="2.62" height="24" /></element>
- <element ref="white" inputtag="IN.0" inputmask="0x00" inputraw="yes"><bounds x="16" y="9.7" width="8" height="0.8" /></element>
+ <element ref="white" clickthrough="no"><bounds x="16" y="9.7" width="8" height="0.8" /></element>
<collection name="Gear Position">
<element ref="gear" inputtag="IN.0" inputmask="0x0c" inputraw="yes"><bounds x="16" y="9.7" width="8" height="0.8" /></element>
</collection>
diff --git a/src/mame/layout/bcheetah.lay b/src/mame/layout/bcheetah.lay
index 2f0fffa705e..b77a30dcb06 100644
--- a/src/mame/layout/bcheetah.lay
+++ b/src/mame/layout/bcheetah.lay
@@ -67,7 +67,7 @@ authors:hap
<bounds x="0" y="5" width="15" height="11" />
<!-- pro/run switch -->
- <element ref="nothing" inputtag="IN.0" inputmask="0x00" inputraw="yes"><bounds x="2" y="5" width="2.9" height="1.1" /></element> <!-- block clickable input -->
+ <element ref="nothing" clickthrough="no"><bounds x="2" y="5" width="2.9" height="1.1" /></element> <!-- block clickable input -->
<element ref="switch1" inputtag="IN.0" inputmask="0x02" inputraw="yes"><bounds x="2" y="5" width="2.9" height="1.1" /></element>
<element ref="gray" inputtag="IN.0" inputmask="0x02" inputraw="yes"><bounds x="2" y="6.2" width="2.9" height="2.2" /></element>
diff --git a/src/mame/layout/compperf.lay b/src/mame/layout/compperf.lay
index fde3964b50b..4ecbf9d5439 100644
--- a/src/mame/layout/compperf.lay
+++ b/src/mame/layout/compperf.lay
@@ -158,9 +158,9 @@ authors:hap
<element ref="whited" inputtag="IN.1" inputmask="0x0c" inputraw="yes"><bounds xc="116" yc="111" width="3.5" height="3.5" /></element>
<!-- block clickable input -->
- <element ref="nothing" inputtag="IN.1" inputmask="0x00" inputraw="yes"><bounds xc="89.5" yc="114.5" width="20" height="3" /></element>
- <element ref="nothing" inputtag="IN.0" inputmask="0x00" inputraw="yes"><bounds xc="106" yc="125.5" width="20" height="3" /></element>
- <element ref="nothing" inputtag="IN.1" inputmask="0x00" inputraw="yes"><bounds xc="122" yc="114.5" width="20" height="3" /></element>
+ <element ref="nothing" clickthrough="no"><bounds xc="89.5" yc="114.5" width="20" height="3" /></element>
+ <element ref="nothing" clickthrough="no"><bounds xc="106" yc="125.5" width="20" height="3" /></element>
+ <element ref="nothing" clickthrough="no"><bounds xc="122" yc="114.5" width="20" height="3" /></element>
<element ref="switch1" inputtag="IN.1" inputmask="0x03" inputraw="yes"><bounds xc="89.5" yc="114.5" width="20" height="3" /></element>
<element ref="switch2" inputtag="IN.0" inputmask="0x07" inputraw="yes"><bounds xc="106" yc="125.5" width="20" height="3" /></element>
diff --git a/src/mame/layout/copycat.lay b/src/mame/layout/copycat.lay
index dfb9a65349d..61e7fe258e4 100644
--- a/src/mame/layout/copycat.lay
+++ b/src/mame/layout/copycat.lay
@@ -120,13 +120,13 @@ authors:hap
<element ref="text_b2"><bounds x="5.125" y="6.7" width="1.75" height="0.25" /></element>
<element ref="text_b3"><bounds x="6.2375" y="6.7" width="1.9" height="0.25" /></element>
- <element ref="nothing" inputtag="IN.2" inputmask="0x00" inputraw="yes"><bounds x="6.25" y="4.9" width="1.2" height="0.5" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="6.25" y="4.9" width="1.2" height="0.5" /></element>
<element ref="switch" inputtag="IN.1" inputmask="0x0f" inputraw="yes"><bounds x="6.3" y="4.925" width="1" height="0.25" /></element>
<element ref="text_s1"><bounds x="5.15" y="4.925" width="1.5" height="0.25" /></element>
<element ref="text_ro"><bounds x="5.825" y="4.875" width="0.35" height="0.35" /></element>
<element ref="nothing" inputtag="IN.1" inputmask="0x0f" inputraw="yes"><bounds x="5.8" y="4.85" width="0.4" height="0.4" /></element>
- <element ref="nothing" inputtag="IN.2" inputmask="0x00" inputraw="yes"><bounds x="6.25" y="7.2" width="1.2" height="0.5" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="6.25" y="7.2" width="1.2" height="0.5" /></element>
<element ref="switch" inputtag="IN.2" inputmask="0x07" inputraw="yes"><bounds x="6.3" y="7.275" width="1" height="0.25" /></element>
<element ref="text_s2"><bounds x="5.15" y="7.275" width="1.5" height="0.25" /></element>
<element ref="text_rg"><bounds x="5.825" y="7.225" width="0.35" height="0.35" /></element>
diff --git a/src/mame/layout/matchme.lay b/src/mame/layout/matchme.lay
index e361585def4..c3ba6c269fd 100644
--- a/src/mame/layout/matchme.lay
+++ b/src/mame/layout/matchme.lay
@@ -115,7 +115,7 @@ authors:hap
<element ref="nothing" inputtag="IN.3" inputmask="0x08" inputraw="yes"><bounds x="9.805" y="9.55" width="0.39" height="0.39" /></element>
<element ref="nothing" inputtag="IN.3" inputmask="0x07" inputraw="yes"><bounds x="9.805" y="9.95" width="0.39" height="0.39" /></element>
- <element ref="nothing" inputtag="FAKE" inputmask="0x00" inputraw="yes"><bounds x="10.2" y="8.7" width="1.1" height="1.8" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="10.2" y="8.7" width="1.1" height="1.8" /></element>
<element ref="switch_music" inputtag="FAKE" inputmask="0x02" inputraw="yes"><bounds x="10.25" y="8.8" width="1" height="0.29" /></element>
<element ref="switch_speed" inputtag="IN.4" inputmask="0x01" inputraw="yes"><bounds x="10.25" y="9.2" width="1" height="0.29" /></element>
<element ref="switch_skill" inputtag="IN.3" inputmask="0x08" inputraw="yes"><bounds x="10.25" y="9.6" width="1" height="0.29" /></element>
diff --git a/src/mame/layout/mdndclab.lay b/src/mame/layout/mdndclab.lay
index bd2c4fbeb8d..8627e852475 100644
--- a/src/mame/layout/mdndclab.lay
+++ b/src/mame/layout/mdndclab.lay
@@ -5,101 +5,76 @@ authors:hap, Carl
-->
<mamelayout version="2">
-<!-- luascript (-plugin layout) -->
-<!-- this adds support for placing wall pieces by clicking between buttons -->
-
<script><![CDATA[
- local layout = {}
- local walls = { { 7.5, {0, 10, 20, 30, 40, 50, 60, 70}},
- {17.5, {0, 10, 20, 30, 40, 50, 60, 70}},
- {27.5, {0, 10, 20, 30, 40, 50, 60, 70}},
- {37.5, {0, 10, 20, 30, 40, 50, 60, 70}},
- {47.5, {0, 10, 20, 30, 40, 50, 60, 70}},
- {57.5, {0, 10, 20, 30, 40, 50, 60, 70}},
- {67.5, {0, 10, 20, 30, 40, 50, 60, 70}} }
-
- local last_state = false
-
- local function get_mouse()
- local x, y, button, target = machine.uiinput:find_mouse()
- if not button then
- last_state = false
- return nil
- end
- if last_state then
- return nil
- end
- last_state = true
- local h = target.height
- local w = target.width
- local vb = target.current_view.bounds
- local vw = (vb.x1 - vb.x0)
- local vh = (vb.y1 - vb.y0)
- if machine.options.entries.keepaspect:value() then
- if (vh / h) < (vw / w) then
- local oh = h
- h = w * (vh / vw)
- y = y - ((oh - h) / 2)
- else
- local ow = w
- w = h * (vw / vh)
- x = x - ((ow - w) / 2)
- end
- end
-
- x = ((x / w) * vw) + vb.x0
- y = ((y / h) * vh) + vb.y0
- return x, y
- end
-
- function layout.reset()
- for num, col in pairs(walls) do
- for num2, wall in pairs(col[2]) do
- machine.output:set_indexed_value("colwall", (num * 10) + num2, 0)
- end
- end
- for num, row in pairs(walls) do
- for num2, wall in pairs(row[2]) do
- machine.output:set_indexed_value("rowwall", (num * 10) + num2, 0)
- end
- end
- end
-
- function layout.frame()
- local x, y = get_mouse()
- if not x or x < 0 or y < 0 then
- return
- end
- for num, col in pairs(walls) do
- if col[1] < x and (col[1] + 2.5) > x then
- for num2, wall in pairs(col[2]) do
- if wall < y and (wall + 7.5) > y then
- local state = machine.output:get_indexed_value("colwall", (num * 10) + num2)
- machine.output:set_indexed_value("colwall", (num * 10) + num2, (~state) & 1)
- return
+ -- add support for placing wall pieces by clicking between buttons
+ file:set_resolve_tags_callback(
+ function ()
+ local walls = { { 7.5, { 0, 10, 20, 30, 40, 50, 60, 70 } },
+ { 17.5, { 0, 10, 20, 30, 40, 50, 60, 70 } },
+ { 27.5, { 0, 10, 20, 30, 40, 50, 60, 70 } },
+ { 37.5, { 0, 10, 20, 30, 40, 50, 60, 70 } },
+ { 47.5, { 0, 10, 20, 30, 40, 50, 60, 70 } },
+ { 57.5, { 0, 10, 20, 30, 40, 50, 60, 70 } },
+ { 67.5, { 0, 10, 20, 30, 40, 50, 60, 70 } } }
+
+ for num, col in pairs(walls) do
+ for num2, wall in pairs(col[2]) do
+ machine.output:set_indexed_value("colwall", (num * 10) + num2, 0)
end
end
- end
- end
- for num, row in pairs(walls) do
- if row[1] < y and (row[1] + 2.5) > y then
- for num2, wall in pairs(row[2]) do
- if wall < x and (wall + 7.5) > x then
- local state = machine.output:get_indexed_value("rowwall", (num * 10) + num2)
- machine.output:set_indexed_value("rowwall", (num * 10) + num2, (~state) & 1)
- return
+ for num, row in pairs(walls) do
+ for num2, wall in pairs(row[2]) do
+ machine.output:set_indexed_value("rowwall", (num * 10) + num2, 0)
end
end
- end
- end
- end
- return layout
+ local view = file.views["Internal Layout"]
+ local boundsref = view.items["boundsref"]
+ local transform
+
+ view:set_recomputed_callback(
+ function ()
+ transform = boundsref.bounds
+ end)
+
+ view:set_pointer_updated_callback(
+ function (pointertype, id, device, x, y, buttons, pressed, released, clicks)
+ if ((pressed & 0x1) ~= 0) and (1 == buttons) then
+ -- convert to layout units
+ x = (x - transform.x0) / transform.width
+ y = (y - transform.y0) / transform.height
+
+ for num, col in pairs(walls) do
+ if col[1] < x and (col[1] + 2.5) > x then
+ for num2, wall in pairs(col[2]) do
+ if wall < y and (wall + 7.5) > y then
+ local state = machine.output:get_indexed_value("colwall", (num * 10) + num2)
+ machine.output:set_indexed_value("colwall", (num * 10) + num2, (~state) & 1)
+ return
+ end
+ end
+ end
+ end
+ for num, row in pairs(walls) do
+ if row[1] < y and (row[1] + 2.5) > y then
+ for num2, wall in pairs(row[2]) do
+ if wall < x and (wall + 7.5) > x then
+ local state = machine.output:get_indexed_value("rowwall", (num * 10) + num2)
+ machine.output:set_indexed_value("rowwall", (num * 10) + num2, (~state) & 1)
+ return
+ end
+ end
+ end
+ end
+ end
+ end)
+ end)
]]></script>
<!-- define elements -->
+ <element name="empty" />
<element name="static_black"><rect><color red="0.07" green="0.07" blue="0.06" /></rect></element>
<element name="static_red"><rect><color red="0.55" green="0.1" blue="0.05" /></rect></element>
<element name="static_white"><rect><color red="0.8" green="0.75" blue="0.7" /></rect></element>
@@ -155,6 +130,10 @@ authors:hap, Carl
<view name="Internal Layout">
<bounds left="-15" right="80" top="-2.5" bottom="80" />
+
+ <!-- for script coordinate calculation -->
+ <element id="boundsref" ref="empty"><bounds x="0" y="0" width="1" height="1" /></element>
+
<element ref="static_black">
<bounds left="-16" right="81" top="-3" bottom="81" />
</element>
diff --git a/src/mame/layout/microvision.lay b/src/mame/layout/microvision.lay
index 6210d8ade72..445e50c06c8 100644
--- a/src/mame/layout/microvision.lay
+++ b/src/mame/layout/microvision.lay
@@ -54,7 +54,7 @@ authors:hap
<collection name="Paddle Position">
<!-- show live paddle position -->
- <element ref="nothing" inputtag="PADDLE" inputmask="0x00" inputraw="yes"><bounds x="12" y="0" width="6" height="1" /></element> <!-- block clickable input -->
+ <element ref="nothing" clickthrough="no"><bounds x="12" y="0" width="6" height="1" /></element> <!-- block clickable input -->
<element ref="bar" blend="multiply" inputtag="PADDLE" inputmask="0xff" inputraw="yes"><bounds x="12.75" y="0.12" width="4.2" height="0.75" /></element>
<element ref="text_p1" blend="add"><bounds x="10.5" y="0.1" width="5" height="0.75" /></element>
diff --git a/src/mame/layout/novag_savant.lay b/src/mame/layout/novag_savant.lay
index 7959df33a14..fed46185014 100644
--- a/src/mame/layout/novag_savant.lay
+++ b/src/mame/layout/novag_savant.lay
@@ -155,7 +155,7 @@ authors:hap
</repeat>
<!-- backlight -->
- <element ref="nothing" blend="add" inputtag="LIGHT" inputmask="0x00" inputraw="yes"><bounds x="0" y="0" width="101" height="114" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="0" y="0" width="101" height="114" /></element>
<element ref="cb_light" blend="multiply" inputtag="LIGHT" inputmask="0x01" inputraw="yes"><bounds x="0" y="0" width="101" height="114" /></element>
<!-- mask edges -->
diff --git a/src/mame/layout/novag_supercon.lay b/src/mame/layout/novag_supercon.lay
index 834b2c9b88c..b03b4ae3174 100644
--- a/src/mame/layout/novag_supercon.lay
+++ b/src/mame/layout/novag_supercon.lay
@@ -572,15 +572,15 @@ authors:hap
<bounds left="-19" right="116" top="-0.5" bottom="88.5" />
<!-- block buttons -->
- <element ref="hlb" inputtag="IN.1" inputmask="0x00" inputraw="yes"><bounds x="88" y="16" width="6" height="4" /></element>
- <element ref="hlb" inputtag="IN.2" inputmask="0x00" inputraw="yes"><bounds x="88" y="26" width="6" height="4" /></element>
- <element ref="hlb" inputtag="IN.5" inputmask="0x00" inputraw="yes"><bounds x="88" y="56" width="6" height="4" /></element>
-
- <element ref="hlb" inputtag="IN.1" inputmask="0x00" inputraw="yes"><bounds x="97" y="16" width="6" height="4" /></element>
- <element ref="hlb" inputtag="IN.2" inputmask="0x00" inputraw="yes"><bounds x="97" y="26" width="6" height="4" /></element>
- <element ref="hlb" inputtag="IN.4" inputmask="0x00" inputraw="yes"><bounds x="97" y="46" width="6" height="4" /></element>
- <element ref="hlb" inputtag="IN.5" inputmask="0x00" inputraw="yes"><bounds x="97" y="56" width="6" height="4" /></element>
- <element ref="hlb" inputtag="IN.6" inputmask="0x00" inputraw="yes"><bounds x="97" y="66" width="6" height="4" /></element>
+ <element ref="hlb" clickthrough="no"><bounds x="88" y="16" width="6" height="4" /></element>
+ <element ref="hlb" clickthrough="no"><bounds x="88" y="26" width="6" height="4" /></element>
+ <element ref="hlb" clickthrough="no"><bounds x="88" y="56" width="6" height="4" /></element>
+
+ <element ref="hlb" clickthrough="no"><bounds x="97" y="16" width="6" height="4" /></element>
+ <element ref="hlb" clickthrough="no"><bounds x="97" y="26" width="6" height="4" /></element>
+ <element ref="hlb" clickthrough="no"><bounds x="97" y="46" width="6" height="4" /></element>
+ <element ref="hlb" clickthrough="no"><bounds x="97" y="56" width="6" height="4" /></element>
+ <element ref="hlb" clickthrough="no"><bounds x="97" y="66" width="6" height="4" /></element>
<group ref="default"><bounds left="-19" right="116" top="-0.5" bottom="88.5" /></group>
diff --git a/src/mame/layout/saitek_mark5.lay b/src/mame/layout/saitek_mark5.lay
index 503638b7727..ed1878dcda4 100644
--- a/src/mame/layout/saitek_mark5.lay
+++ b/src/mame/layout/saitek_mark5.lay
@@ -219,7 +219,7 @@ authors:hap
<element ref="hlc" blend="add" inputtag="IN.8" inputmask="0x80"><bounds x="18.333" y="12.333" width="4.666" height="4.666" /></element>
<element ref="black"><bounds x="9" y="22.5" width="14" height="45" /></element>
- <element ref="nothing" blend="add" inputtag="IN.0" inputmask="0x00" inputraw="yes"><bounds x="-1" y="-1" width="34" height="69" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="-1" y="-1" width="34" height="69" /></element>
<!-- button labels (upper) -->
<element ref="text_b01e" blend="add" inputtag="IN.6" inputmask="0x04" inputraw="yes"><bounds x="0" y="1" width="6" height="1.4" /></element>
@@ -405,7 +405,7 @@ authors:hap
<group name="display">
<screen index="0" blend="alpha"><bounds x="0" y="0" width="100" height="114.65" /></screen>
- <element ref="nothing" blend="add" inputtag="IN.7" inputmask="0x00" inputraw="yes"><bounds x="0" y="0" width="100" height="114.65" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="0" y="0" width="100" height="114.65" /></element>
<element ref="lcd_bg" blend="multiply" inputtag="IN.7" inputmask="0x02" inputraw="yes"><bounds x="0" y="0" width="100" height="114.65" /></element>
<element ref="lcd_cb" blend="multiply"><bounds x="0" y="0" width="100" height="95.9" /></element>
</group>
diff --git a/src/mame/layout/saitek_mark6.lay b/src/mame/layout/saitek_mark6.lay
index 260f5b3421b..d7fe6d5f1ee 100644
--- a/src/mame/layout/saitek_mark6.lay
+++ b/src/mame/layout/saitek_mark6.lay
@@ -513,7 +513,7 @@ authors:hap
<element ref="hlc" blend="add" inputtag="IN.8" inputmask="0x80"><bounds x="18.333" y="12.333" width="4.666" height="4.666" /></element>
<element ref="black"><bounds x="9" y="22.5" width="14" height="45" /></element>
- <element ref="nothing" blend="add" inputtag="IN.0" inputmask="0x00" inputraw="yes"><bounds x="-1" y="-1" width="34" height="69" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="-1" y="-1" width="34" height="69" /></element>
<!-- button labels (upper) -->
<element ref="text_b01e" blend="add" inputtag="IN.6" inputmask="0x04" inputraw="yes"><bounds x="0" y="1" width="6" height="1.4" /></element>
@@ -699,7 +699,7 @@ authors:hap
<group name="display">
<screen index="0" blend="alpha"><bounds x="0" y="0" width="100" height="114.65" /></screen>
- <element ref="nothing" blend="add" inputtag="IN.7" inputmask="0x00" inputraw="yes"><bounds x="0" y="0" width="100" height="114.65" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="0" y="0" width="100" height="114.65" /></element>
<element ref="lcd_bg" blend="multiply" inputtag="IN.7" inputmask="0x02" inputraw="yes"><bounds x="0" y="0" width="100" height="114.65" /></element>
<element ref="lcd_cb" blend="multiply"><bounds x="0" y="0" width="100" height="95.9" /></element>
</group>
diff --git a/src/mame/layout/saitek_ssystem3.lay b/src/mame/layout/saitek_ssystem3.lay
index 0e45199ed6b..694194e3442 100644
--- a/src/mame/layout/saitek_ssystem3.lay
+++ b/src/mame/layout/saitek_ssystem3.lay
@@ -146,7 +146,7 @@ authors:hap
<group name="lcd1">
<bounds x="-1" y="3" width="18" height="8" />
<screen index="0" blend="alpha"><bounds x="0" y="4" width="15.8" height="6" /></screen>
- <element ref="nothing" blend="add" inputtag="IN.4" inputmask="0x00" inputraw="yes"><bounds x="-1" y="3" width="18" height="8" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="-1" y="3" width="18" height="8" /></element>
<element ref="lcd_bg" blend="multiply" inputtag="IN.4" inputmask="0x02" inputraw="yes"><bounds x="-1" y="3" width="18" height="8" /></element>
</group>
@@ -206,7 +206,7 @@ authors:hap
<screen index="1" blend="alpha"><bounds x="9.8" y="10" width="106" height="108" /></screen>
<element ref="lcd_cb" blend="multiply"><bounds x="1.5" y="2.9" width="122.6" height="122.1" /></element>
- <element ref="nothing" blend="add" inputtag="IN.4" inputmask="0x00" inputraw="yes"><bounds x="6.2" y="7.6" width="113.3" height="112.8" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="6.2" y="7.6" width="113.3" height="112.8" /></element>
<element ref="lcd2m" blend="multiply" inputtag="IN.4" inputmask="0x04" inputraw="yes"><bounds x="6.2" y="7.6" width="113.3" height="112.8" /></element>
</group>
diff --git a/src/mame/layout/saitek_ssystem4.lay b/src/mame/layout/saitek_ssystem4.lay
index cd72b1d0edc..5ff273b0e01 100644
--- a/src/mame/layout/saitek_ssystem4.lay
+++ b/src/mame/layout/saitek_ssystem4.lay
@@ -91,7 +91,7 @@ authors:hap
<bounds left="8" right="52" top="1" bottom="39" />
<screen index="0" blend="alpha"><bounds x="10" y="4" width="15.8" height="6" /></screen>
- <element ref="nothing" blend="add" inputtag="IN.4" inputmask="0x00" inputraw="yes"><bounds x="9" y="3" width="18" height="8" /></element>
+ <element ref="nothing" blend="add" clickthrough="no"><bounds x="9" y="3" width="18" height="8" /></element>
<element ref="lcd_bg" blend="multiply" inputtag="IN.4" inputmask="0x02" inputraw="yes"><bounds x="9" y="3" width="18" height="8" /></element>
<element ref="button" inputtag="IN.0" inputmask="0x04"><bounds x="28" y="4" width="5" height="6" /></element>
diff --git a/src/mame/layout/simon.lay b/src/mame/layout/simon.lay
index ef56c8eef3f..25993d3cb2c 100644
--- a/src/mame/layout/simon.lay
+++ b/src/mame/layout/simon.lay
@@ -130,7 +130,7 @@ authors:hap
<!-- switches -->
<element ref="text_game"><bounds x="68" y="93" width="20" height="4" /></element>
- <element ref="nothing" inputtag="IN.0" inputmask="0x00" inputraw="yes"><bounds x="73.9" y="97.9" width="3.2" height="4.2" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="73.9" y="97.9" width="3.2" height="4.2" /></element>
<element ref="switch_game" inputtag="IN.0" inputmask="0x07" inputraw="yes"><bounds x="74" y="98" width="3" height="4" /></element>
<element ref="text_lg"><bounds x="66.5" y="97" width="6" height="6" /></element>
<element ref="nothing" inputtag="SWITCH" inputmask="0x01"><bounds x="65.5" y="96" width="8" height="8" /></element>
@@ -138,7 +138,7 @@ authors:hap
<element ref="nothing" inputtag="SWITCH" inputmask="0x02"><bounds x="77" y="96" width="8" height="8" /></element>
<element ref="text_skill"><bounds x="112" y="93" width="30" height="4" /></element>
- <element ref="nothing" inputtag="IN.3" inputmask="0x00" inputraw="yes"><bounds x="122.9" y="97.9" width="3.2" height="4.2" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="122.9" y="97.9" width="3.2" height="4.2" /></element>
<element ref="switch_skill" inputtag="IN.3" inputmask="0x0f" inputraw="yes"><bounds x="123" y="98" width="3" height="4" /></element>
<element ref="text_lb"><bounds x="115.5" y="97" width="6" height="6" /></element>
<element ref="nothing" inputtag="SWITCH" inputmask="0x04"><bounds x="114.5" y="96" width="8" height="8" /></element>
diff --git a/src/mame/layout/ssimon.lay b/src/mame/layout/ssimon.lay
index a725bf4d053..2f48a491466 100644
--- a/src/mame/layout/ssimon.lay
+++ b/src/mame/layout/ssimon.lay
@@ -174,7 +174,7 @@ authors:hap
<element ref="text_s3"><bounds x="-3.9" y="32.15" width="3.5" height="0.9" /></element>
<element ref="static_black2"><bounds x="-6.5" y="29.125" width="0.5" height="4.5" /></element>
- <element ref="nothing" inputtag="IN.6" inputmask="0x00" inputraw="yes"><bounds x="-7.25" y="29.125" width="2" height="4.5" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="-7.25" y="29.125" width="2" height="4.5" /></element>
<element ref="switch0" inputtag="IN.6" inputmask="0x03" inputraw="yes"><bounds x="-7" y="29.625" width="1.5" height="1" /></element>
<element ref="switch1" inputtag="IN.6" inputmask="0x03" inputraw="yes"><bounds x="-7" y="30.875" width="1.5" height="1" /></element>
<element ref="switch2" inputtag="IN.6" inputmask="0x03" inputraw="yes"><bounds x="-7" y="32.125" width="1.5" height="1" /></element>
@@ -207,7 +207,7 @@ authors:hap
<element ref="text_4"><bounds x="50.25" y="34.65" width="1" height="0.9" /></element>
<element ref="static_black2"><bounds x="53" y="16.875" width="0.5" height="7" /></element>
- <element ref="nothing" inputtag="IN.0" inputmask="0x00" inputraw="yes"><bounds x="52.25" y="16.875" width="2" height="7" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="52.25" y="16.875" width="2" height="7" /></element>
<element ref="switch0" inputtag="IN.0" inputmask="0x0f" inputraw="yes"><bounds x="52.5" y="17.375" width="1.5" height="1" /></element>
<element ref="switch8" inputtag="IN.0" inputmask="0x0f" inputraw="yes"><bounds x="52.5" y="18.625" width="1.5" height="1" /></element>
<element ref="switch4" inputtag="IN.0" inputmask="0x0f" inputraw="yes"><bounds x="52.5" y="19.875" width="1.5" height="1" /></element>
@@ -215,7 +215,7 @@ authors:hap
<element ref="switch1" inputtag="IN.0" inputmask="0x0f" inputraw="yes"><bounds x="52.5" y="22.375" width="1.5" height="1" /></element>
<element ref="static_black2"><bounds x="53" y="29.125" width="0.5" height="7" /></element>
- <element ref="nothing" inputtag="IN.4" inputmask="0x00" inputraw="yes"><bounds x="52.25" y="29.125" width="2" height="7" /></element>
+ <element ref="nothing" clickthrough="no"><bounds x="52.25" y="29.125" width="2" height="7" /></element>
<element ref="switch0" inputtag="IN.4" inputmask="0x0f" inputraw="yes"><bounds x="52.5" y="29.625" width="1.5" height="1" /></element>
<element ref="switch2" inputtag="IN.4" inputmask="0x0f" inputraw="yes"><bounds x="52.5" y="30.875" width="1.5" height="1" /></element>
<element ref="switch4" inputtag="IN.4" inputmask="0x0f" inputraw="yes"><bounds x="52.5" y="32.125" width="1.5" height="1" /></element>
diff --git a/src/mame/layout/strobe.lay b/src/mame/layout/strobe.lay
index 7947e1c3bc5..6d44a11acb1 100644
--- a/src/mame/layout/strobe.lay
+++ b/src/mame/layout/strobe.lay
@@ -134,7 +134,7 @@ authors:hap
<element ref="nothing" inputtag="IN.5" inputmask="0x07" inputraw="yes"><bounds xc="100" yc="97" width="3.5" height="3.5" /></element>
<element ref="nothing" inputtag="IN.6" inputmask="0x06" inputraw="yes"><bounds xc="106.5" yc="97" width="3.5" height="3.5" /></element>
- <element ref="nothing" inputtag="IN.3" inputmask="0x00" inputraw="yes"><bounds xc="100" y="85" width="25" height="18" /></element>
+ <element ref="nothing" clickthrough="no"><bounds xc="100" y="85" width="25" height="18" /></element>
<element ref="redd"><bounds xc="100" yc="100" width="20" height="20" /></element>
<element ref="text_set" blend="multiply"><bounds xc="100" yc="106" width="20" height="5" /></element>
diff --git a/src/mame/layout/tmbaskb.lay b/src/mame/layout/tmbaskb.lay
index e1c509fddeb..6b24e829b88 100644
--- a/src/mame/layout/tmbaskb.lay
+++ b/src/mame/layout/tmbaskb.lay
@@ -245,7 +245,7 @@ authors:hap
<group ref="digits"><bounds xc="150" y="33" width="45" height="43" /></group>
<!-- other buttons -->
- <element ref="nothing" inputtag="IN.3" inputmask="0x00" inputraw="yes"><bounds xc="150" y="232.5" width="230" height="4.5" /></element>
+ <element ref="nothing" clickthrough="no"><bounds xc="150" y="232.5" width="230" height="4.5" /></element>
<element ref="switch1" inputtag="IN.3" inputmask="0x02" inputraw="yes"><bounds xc="111" y="232.5" width="30" height="4.5" /></element>
<element ref="switch2" inputtag="IN.3" inputmask="0x01" inputraw="yes"><bounds xc="193" y="232.5" width="30" height="4.5" /></element>
diff --git a/src/mame/layout/tmvolleyb.lay b/src/mame/layout/tmvolleyb.lay
index ea09bf021ef..c5aacfb9e6f 100644
--- a/src/mame/layout/tmvolleyb.lay
+++ b/src/mame/layout/tmvolleyb.lay
@@ -178,7 +178,7 @@ authors:hap
<element ref="text_serve"><bounds xc="271.5" yc="104" width="7" height="30" /><orientation rotate="270" /></element>
<element ref="but1" inputtag="IN.2" inputmask="0x08"><bounds xc="281" yc="104" width="6" height="23" /></element>
- <element ref="nothing" inputtag="IN.7" inputmask="0x00" inputraw="yes"><bounds xc="150" y="232.5" width="230" height="4.5" /></element>
+ <element ref="nothing" clickthrough="no"><bounds xc="150" y="232.5" width="230" height="4.5" /></element>
<element ref="switch1" inputtag="IN.7" inputmask="0x01" inputraw="yes"><bounds xc="109" y="232.5" width="30" height="4.5" /></element>
<element ref="switch2" inputtag="IN.6" inputmask="0x08" inputraw="yes"><bounds xc="193" y="232.5" width="30" height="4.5" /></element>
diff --git a/src/mame/layout/vclock3.lay b/src/mame/layout/vclock3.lay
index 8e0c0bd9cd8..cc02e133a32 100644
--- a/src/mame/layout/vclock3.lay
+++ b/src/mame/layout/vclock3.lay
@@ -41,7 +41,7 @@ authors:hap
<element name="0.8" ref="led"><bounds x="-2" y="0" width="1.5" height="1.5" /></element>
<element ref="text_pm"><bounds x="-4" y="2" width="5.5" height="1.5" /></element>
- <element ref="nothing" inputtag="IN.6" inputmask="0x00" inputraw="yes"><bounds x="-2" y="9" width="1.5" height="1.5" /></element> <!-- block clickable input -->
+ <element ref="nothing" clickthrough="no"><bounds x="-2" y="9" width="1.5" height="1.5" /></element> <!-- block clickable input -->
<element ref="led" inputtag="IN.6" inputmask="0x02"><bounds x="-2" y="9" width="1.5" height="1.5" /></element>
<element ref="text_b1"><bounds x="-3" y="11" width="7.5" height="1.5" /></element>
<element ref="text_b2"><bounds x="-3" y="12.5" width="7.5" height="1.5" /></element>
diff --git a/src/mame/layout/videoart.lay b/src/mame/layout/videoart.lay
index f8504dd9fa4..590a1bd99ea 100644
--- a/src/mame/layout/videoart.lay
+++ b/src/mame/layout/videoart.lay
@@ -64,7 +64,7 @@ authors:hap
<element ref="text_erase" blend="multiply"><bounds x="16" yc="5" width="30" height="6" /></element>
<element name="led" ref="led"><bounds x="33" yc="5" width="4" height="4" /></element>
- <element ref="nothing" inputtag="AN0" inputmask="0x00" inputraw="yes"><bounds x="54" y="2" width="360" height="6" /></element> <!-- block clickable input -->
+ <element ref="nothing" clickthrough="no"><bounds x="54" y="2" width="360" height="6" /></element> <!-- block clickable input -->
<element ref="color" inputtag="AN0" inputmask="0xff" inputraw="yes"><bounds x="54" y="2" width="360" height="6" /></element>
<element ref="text_color" blend="multiply"><bounds x="55" yc="5" width="30" height="6" /></element>
diff --git a/src/osd/interface/uievents.h b/src/osd/interface/uievents.h
new file mode 100644
index 00000000000..199d612713a
--- /dev/null
+++ b/src/osd/interface/uievents.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Vas Crabb
+/***************************************************************************
+
+ uievents.h
+
+ OSD UI event interfaces
+
+***************************************************************************/
+#ifndef MAME_OSD_INTERFACE_UIEVENTS_H
+#define MAME_OSD_INTERFACE_UIEVENTS_H
+
+
+//**************************************************************************
+// FORWARD DECLARATIONS
+//**************************************************************************
+
+class render_target;
+
+
+namespace osd {
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// UI event handler interface
+
+class ui_event_handler
+{
+protected:
+ ~ui_event_handler() = default;
+
+public:
+ enum class pointer
+ {
+ UNKNOWN,
+ MOUSE,
+ PEN,
+ TOUCH
+ };
+
+ // window events
+ virtual void push_window_focus_event(render_target *target) = 0;
+ virtual void push_window_defocus_event(render_target *target) = 0;
+
+ // legacy mouse events
+ virtual void push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int lines) = 0;
+
+ // pointer events
+ virtual void push_pointer_update(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 buttons, u32 pressed, u32 released, s16 clicks) = 0;
+ virtual void push_pointer_leave(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) = 0;
+ virtual void push_pointer_abort(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) = 0;
+
+ // text input events
+ virtual void push_char_event(render_target *target, char32_t ch) = 0;
+};
+
+} // namespace osd
+
+#endif // MAME_OSD_INTERFACE_UIEVENTS_H
diff --git a/src/osd/modules/debugger/debugimgui.cpp b/src/osd/modules/debugger/debugimgui.cpp
index 4ae514f857f..e2d4eba363a 100644
--- a/src/osd/modules/debugger/debugimgui.cpp
+++ b/src/osd/modules/debugger/debugimgui.cpp
@@ -18,6 +18,7 @@
#include "debug/debugcpu.h"
#include "debugger.h"
#include "render.h"
+#include "ui/uimain.h"
#include "uiinput.h"
#include "formats/flopimg.h"
@@ -36,16 +37,16 @@ class debug_area
DISABLE_COPYING(debug_area);
public:
- debug_area(running_machine &machine, debug_view_type type)
- : next(nullptr),
- type(0),
- ofs_x(0),
- ofs_y(0),
- is_collapsed(false),
- exec_cmd(false),
- scroll_end(false),
- scroll_follow(false)
- {
+ debug_area(running_machine &machine, debug_view_type type) :
+ next(nullptr),
+ type(0),
+ ofs_x(0),
+ ofs_y(0),
+ is_collapsed(false),
+ exec_cmd(false),
+ scroll_end(false),
+ scroll_follow(false)
+ {
this->view = machine.debug_view().alloc_view(type, nullptr, this);
this->type = type;
this->m_machine = &machine;
@@ -63,7 +64,7 @@ public:
default:
break;
}
- }
+ }
~debug_area()
{
//this->target->debug_free(*this->container);
@@ -72,7 +73,7 @@ public:
running_machine &machine() const { assert(m_machine != nullptr); return *m_machine; }
- debug_area * next;
+ debug_area * next;
int type;
debug_view * view;
@@ -99,9 +100,11 @@ public:
class debug_imgui : public osd_module, public debug_module
{
public:
- debug_imgui()
- : osd_module(OSD_DEBUG_PROVIDER, "imgui"), debug_module(),
+ debug_imgui() :
+ osd_module(OSD_DEBUG_PROVIDER, "imgui"), debug_module(),
m_machine(nullptr),
+ m_take_ui(false),
+ m_current_pointer(-1),
m_mouse_x(0),
m_mouse_y(0),
m_mouse_button(false),
@@ -155,9 +158,8 @@ private:
std::string longname;
};
- void handle_mouse();
+ void handle_events();
void handle_mouse_views();
- void handle_keys();
void handle_keys_views();
void handle_console(running_machine* machine);
void update();
@@ -184,15 +186,17 @@ private:
static int history_set(ImGuiInputTextCallbackData* data);
running_machine* m_machine;
- int32_t m_mouse_x;
- int32_t m_mouse_y;
+ bool m_take_ui;
+ int32_t m_current_pointer;
+ int32_t m_mouse_x;
+ int32_t m_mouse_y;
bool m_mouse_button;
bool m_prev_mouse_button;
bool m_running;
const char* font_name;
float font_size;
ImVec2 m_text_size; // size of character (assumes monospaced font is in use)
- uint8_t m_key_char;
+ uint8_t m_key_char;
bool m_hide;
int m_win_count; // number of active windows, does not decrease, used to ID individual windows
bool m_has_images; // true if current system has any image devices
@@ -271,72 +275,18 @@ bool debug_imgui::get_view_source(void* data, int idx, const char** out_text)
return true;
}
-void debug_imgui::handle_mouse()
+void debug_imgui::handle_events()
{
- m_prev_mouse_button = m_mouse_button;
- m_machine->ui_input().find_mouse(&m_mouse_x, &m_mouse_y, &m_mouse_button);
ImGuiIO& io = ImGui::GetIO();
- io.MousePos = ImVec2(m_mouse_x,m_mouse_y);
- io.MouseDown[0] = m_mouse_button;
-}
-
-void debug_imgui::handle_mouse_views()
-{
- rectangle rect;
- bool clicked = false;
- if(m_mouse_button == true && m_prev_mouse_button == false)
- clicked = true;
-
- // check all views, and pass mouse clicks to them
- if(!m_mouse_button)
- return;
- rect.min_x = view_main_disasm->ofs_x;
- rect.min_y = view_main_disasm->ofs_y;
- rect.max_x = view_main_disasm->ofs_x + view_main_disasm->view_width;
- rect.max_y = view_main_disasm->ofs_y + view_main_disasm->view_height;
- if(rect.contains(m_mouse_x,m_mouse_y) && clicked && view_main_disasm->has_focus)
- {
- debug_view_xy topleft = view_main_disasm->view->visible_position();
- debug_view_xy newpos;
- newpos.x = topleft.x + (m_mouse_x-view_main_disasm->ofs_x) / m_text_size.x;
- newpos.y = topleft.y + (m_mouse_y-view_main_disasm->ofs_y) / m_text_size.y;
- view_main_disasm->view->set_cursor_position(newpos);
- view_main_disasm->view->set_cursor_visible(true);
- }
- for(auto it = view_list.begin();it != view_list.end();++it)
- {
- rect.min_x = (*it)->ofs_x;
- rect.min_y = (*it)->ofs_y;
- rect.max_x = (*it)->ofs_x + (*it)->view_width;
- rect.max_y = (*it)->ofs_y + (*it)->view_height;
- if(rect.contains(m_mouse_x,m_mouse_y) && clicked && (*it)->has_focus)
- {
- if((*it)->view->cursor_supported())
- {
- debug_view_xy topleft = (*it)->view->visible_position();
- debug_view_xy newpos;
- newpos.x = topleft.x + (m_mouse_x-(*it)->ofs_x) / m_text_size.x;
- newpos.y = topleft.y + (m_mouse_y-(*it)->ofs_y) / m_text_size.y;
- (*it)->view->set_cursor_position(newpos);
- (*it)->view->set_cursor_visible(true);
- }
- }
- }
-}
-
-void debug_imgui::handle_keys()
-{
- ImGuiIO& io = ImGui::GetIO();
- ui_event event;
- debug_area* focus_view = nullptr;
// find view that has focus (should only be one at a time)
- for(auto view_ptr = view_list.begin();view_ptr != view_list.end();++view_ptr)
+ debug_area* focus_view = nullptr;
+ for(auto view_ptr = view_list.begin();view_ptr != view_list.end(); ++view_ptr)
if((*view_ptr)->has_focus)
focus_view = *view_ptr;
// check views in main views also (only the disassembler view accepts inputs)
- if(view_main_disasm != nullptr)
+ if(view_main_disasm)
if(view_main_disasm->has_focus)
focus_view = view_main_disasm;
@@ -355,21 +305,65 @@ void debug_imgui::handle_keys()
for(input_item_id id = ITEM_ID_A; id <= ITEM_ID_CANCEL; ++id)
{
- if(m_machine->input().code_pressed(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id))) {
- if (m_mapping.count(id)) io.AddKeyEvent(m_mapping[id], true);
- } else {
- if (m_mapping.count(id)) io.AddKeyEvent(m_mapping[id], false);
+ if(m_machine->input().code_pressed(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
+ {
+ if(m_mapping.count(id))
+ io.AddKeyEvent(m_mapping[id], true);
+ }
+ else
+ {
+ if(m_mapping.count(id))
+ io.AddKeyEvent(m_mapping[id], false);
}
}
+ m_prev_mouse_button = m_mouse_button;
m_key_char = 0;
- while (m_machine->ui_input().pop_event(&event))
+ ui_event event;
+ while(m_machine->ui_input().pop_event(&event))
{
switch (event.event_type)
{
+ case ui_event::type::POINTER_UPDATE:
+ if(&m_machine->render().ui_target() != event.target)
+ break;
+ if(event.pointer_id != m_current_pointer)
+ {
+ if((0 > m_current_pointer) || ((event.pointer_pressed & 1) && !m_mouse_button))
+ m_current_pointer = event.pointer_id;
+ }
+ if(event.pointer_id == m_current_pointer)
+ {
+ bool changed = (m_mouse_x != event.pointer_x) || (m_mouse_y != event.pointer_y) || (m_mouse_button != bool(event.pointer_buttons & 1));
+ m_mouse_x = event.pointer_x;
+ m_mouse_y = event.pointer_y;
+ m_mouse_button = bool(event.pointer_buttons & 1);
+ if(changed)
+ {
+ io.MousePos = ImVec2(m_mouse_x,m_mouse_y);
+ io.MouseDown[0] = m_mouse_button;
+ }
+ }
+ break;
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ if((&m_machine->render().ui_target() == event.target) && (event.pointer_id == m_current_pointer))
+ {
+ m_current_pointer = -1;
+ bool changed = (m_mouse_x != event.pointer_x) || (m_mouse_y != event.pointer_y) || m_mouse_button;
+ m_mouse_x = event.pointer_x;
+ m_mouse_y = event.pointer_y;
+ m_mouse_button = false;
+ if(changed)
+ {
+ io.MousePos = ImVec2(m_mouse_x,m_mouse_y);
+ io.MouseDown[0] = m_mouse_button;
+ }
+ }
+ break;
case ui_event::type::IME_CHAR:
- m_key_char = event.ch;
- if(focus_view != nullptr)
+ m_key_char = event.ch; // FIXME: assigning 4-byte UCS4 character to 8-bit variable
+ if(focus_view)
focus_view->view->process_char(m_key_char);
return;
default:
@@ -431,6 +425,50 @@ void debug_imgui::handle_keys()
}
+void debug_imgui::handle_mouse_views()
+{
+ rectangle rect;
+ bool clicked = false;
+ if(m_mouse_button == true && m_prev_mouse_button == false)
+ clicked = true;
+
+ // check all views, and pass mouse clicks to them
+ if(!m_mouse_button)
+ return;
+ rect.min_x = view_main_disasm->ofs_x;
+ rect.min_y = view_main_disasm->ofs_y;
+ rect.max_x = view_main_disasm->ofs_x + view_main_disasm->view_width;
+ rect.max_y = view_main_disasm->ofs_y + view_main_disasm->view_height;
+ if(rect.contains(m_mouse_x,m_mouse_y) && clicked && view_main_disasm->has_focus)
+ {
+ debug_view_xy topleft = view_main_disasm->view->visible_position();
+ debug_view_xy newpos;
+ newpos.x = topleft.x + (m_mouse_x-view_main_disasm->ofs_x) / m_text_size.x;
+ newpos.y = topleft.y + (m_mouse_y-view_main_disasm->ofs_y) / m_text_size.y;
+ view_main_disasm->view->set_cursor_position(newpos);
+ view_main_disasm->view->set_cursor_visible(true);
+ }
+ for(auto it = view_list.begin();it != view_list.end();++it)
+ {
+ rect.min_x = (*it)->ofs_x;
+ rect.min_y = (*it)->ofs_y;
+ rect.max_x = (*it)->ofs_x + (*it)->view_width;
+ rect.max_y = (*it)->ofs_y + (*it)->view_height;
+ if(rect.contains(m_mouse_x,m_mouse_y) && clicked && (*it)->has_focus)
+ {
+ if((*it)->view->cursor_supported())
+ {
+ debug_view_xy topleft = (*it)->view->visible_position();
+ debug_view_xy newpos;
+ newpos.x = topleft.x + (m_mouse_x-(*it)->ofs_x) / m_text_size.x;
+ newpos.y = topleft.y + (m_mouse_y-(*it)->ofs_y) / m_text_size.y;
+ (*it)->view->set_cursor_position(newpos);
+ (*it)->view->set_cursor_visible(true);
+ }
+ }
+ }
+}
+
void debug_imgui::handle_keys_views()
{
debug_area* focus_view = nullptr;
@@ -1557,10 +1595,15 @@ void debug_imgui::wait_for_debugger(device_t &device, bool firststop)
device.machine().ui_input().reset();
m_running = false;
}
+ if(!m_take_ui)
+ {
+ m_take_ui = m_machine->ui().set_ui_event_handler([this] () { return m_take_ui; });
+ if(m_take_ui)
+ m_machine->ui_input().reset();
+ }
m_hide = false;
m_machine->osd().input_update(true);
- handle_mouse();
- handle_keys();
+ handle_events();
handle_console(m_machine);
update_cpu_view(&device);
imguiBeginFrame(m_mouse_x,m_mouse_y,m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height,m_key_char);
@@ -1576,19 +1619,31 @@ void debug_imgui::wait_for_debugger(device_t &device, bool firststop)
void debug_imgui::debugger_update()
{
- if(view_main_disasm == nullptr || view_main_regs == nullptr || view_main_console == nullptr)
+ if(!view_main_disasm || !view_main_regs || !view_main_console || !m_machine || (m_machine->phase() != machine_phase::RUNNING))
return;
- if (m_machine && (m_machine->phase() == machine_phase::RUNNING) && !m_machine->debugger().cpu().is_stopped() && !m_hide)
+ if(!m_machine->debugger().cpu().is_stopped())
{
- uint32_t width = m_machine->render().ui_target().width();
- uint32_t height = m_machine->render().ui_target().height();
- m_machine->osd().input_update(true);
- handle_mouse();
- handle_keys();
- imguiBeginFrame(m_mouse_x,m_mouse_y,m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height, m_key_char);
- update();
- imguiEndFrame();
+ if(m_take_ui)
+ {
+ m_take_ui = false;
+ m_current_pointer = -1;
+ m_prev_mouse_button = m_mouse_button;
+ if(m_mouse_button)
+ {
+ m_mouse_button = false;
+ ImGuiIO& io = ImGui::GetIO();
+ io.MouseDown[0] = false;
+ }
+ }
+ if(!m_hide)
+ {
+ uint32_t width = m_machine->render().ui_target().width();
+ uint32_t height = m_machine->render().ui_target().height();
+ imguiBeginFrame(m_mouse_x, m_mouse_y, 0, 0, width, height, m_key_char);
+ update();
+ imguiEndFrame();
+ }
}
}
diff --git a/src/osd/modules/input/input_dinput.cpp b/src/osd/modules/input/input_dinput.cpp
index 7732d610bba..6f539c2f7fc 100644
--- a/src/osd/modules/input/input_dinput.cpp
+++ b/src/osd/modules/input/input_dinput.cpp
@@ -232,7 +232,7 @@ public:
virtual void configure(input_device &device) override;
private:
- mouse_state m_mouse;
+ DIMOUSESTATE2 m_mouse;
};
dinput_mouse_device::dinput_mouse_device(
diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp
index d6b99649c4a..437abf598a4 100644
--- a/src/osd/modules/input/input_rawinput.cpp
+++ b/src/osd/modules/input/input_rawinput.cpp
@@ -433,7 +433,8 @@ public:
m_mouse({0}),
m_x(0),
m_y(0),
- m_z(0)
+ m_v(0),
+ m_h(0)
{
}
@@ -444,28 +445,44 @@ public:
{
m_mouse.lX = std::exchange(m_x, 0);
m_mouse.lY = std::exchange(m_y, 0);
- m_mouse.lZ = std::exchange(m_z, 0);
+ m_mouse.lV = std::exchange(m_v, 0);
+ m_mouse.lH = std::exchange(m_h, 0);
}
}
virtual void reset() override
{
memset(&m_mouse, 0, sizeof(m_mouse));
- m_x = m_y = m_z = 0;
+ m_x = m_y = m_v = m_h = 0;
}
virtual void configure(input_device &device) override
{
// populate the axes
- for (int axisnum = 0; axisnum < 3; axisnum++)
- {
- device.add_item(
- default_axis_name[axisnum],
- std::string_view(),
- input_item_id(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &m_mouse.lX + axisnum);
- }
+ device.add_item(
+ "X",
+ std::string_view(),
+ ITEM_ID_XAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lX);
+ device.add_item(
+ "Y",
+ std::string_view(),
+ ITEM_ID_YAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lY);
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_RZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lH);
// populate the buttons
for (int butnum = 0; butnum < 5; butnum++)
@@ -489,9 +506,11 @@ public:
m_x += rawinput.data.mouse.lLastX * input_device::RELATIVE_PER_PIXEL;
m_y += rawinput.data.mouse.lLastY * input_device::RELATIVE_PER_PIXEL;
- // update Z axis (vertical scroll)
+ // update Z/Rz axes (vertical/horizontal scroll)
if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL)
- m_z += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
+ m_v += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_HWHEEL)
+ m_h += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
// update the button states; always update the corresponding mouse buttons
if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_mouse.rgbButtons[0] = 0x80;
@@ -509,7 +528,7 @@ public:
private:
mouse_state m_mouse;
- LONG m_x, m_y, m_z;
+ LONG m_x, m_y, m_v, m_h;
};
@@ -523,7 +542,8 @@ public:
rawinput_lightgun_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) :
rawinput_device(std::move(name), std::move(id), module, handle),
m_lightgun({0}),
- m_z(0)
+ m_v(0),
+ m_h(0)
{
}
@@ -531,13 +551,17 @@ public:
{
rawinput_device::poll(relative_reset);
if (relative_reset)
- m_lightgun.lZ = std::exchange(m_z, 0);
+ {
+ m_lightgun.lV = std::exchange(m_v, 0);
+ m_lightgun.lH = std::exchange(m_h, 0);
+ }
}
virtual void reset() override
{
memset(&m_lightgun, 0, sizeof(m_lightgun));
- m_z = 0;
+ m_v = 0;
+ m_h = 0;
}
virtual void configure(input_device &device) override
@@ -553,13 +577,19 @@ public:
&m_lightgun.lX + axisnum);
}
- // scroll wheel is always relative if present
+ // scroll wheels are always relative if present
device.add_item(
- default_axis_name[2],
+ "Scroll V",
std::string_view(),
ITEM_ID_ADD_RELATIVE1,
generic_axis_get_state<LONG>,
- &m_lightgun.lZ);
+ &m_lightgun.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE2,
+ generic_axis_get_state<LONG>,
+ &m_lightgun.lH);
// populate the buttons
for (int butnum = 0; butnum < 5; butnum++)
@@ -583,9 +613,11 @@ public:
m_lightgun.lX = normalize_absolute_axis(rawinput.data.mouse.lLastX, 0, input_device::ABSOLUTE_MAX);
m_lightgun.lY = normalize_absolute_axis(rawinput.data.mouse.lLastY, 0, input_device::ABSOLUTE_MAX);
- // update zaxis
+ // update Z/Rz axes
if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL)
- m_z += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
+ m_v += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
+ if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_HWHEEL)
+ m_h += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL;
// update the button states; always update the corresponding mouse buttons
if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_lightgun.rgbButtons[0] = 0x80;
@@ -603,7 +635,7 @@ public:
private:
mouse_state m_lightgun;
- LONG m_z;
+ LONG m_v, m_h;
};
diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp
index cc7cb2f1451..62e13cbf960 100644
--- a/src/osd/modules/input/input_sdl.cpp
+++ b/src/osd/modules/input/input_sdl.cpp
@@ -736,12 +736,13 @@ public:
break;
case SDL_MOUSEWHEEL:
+ // adjust SDL 1-per-click to match Win32 120-per-click
#if SDL_VERSION_ATLEAST(2, 0, 18)
- m_v += event.wheel.preciseY * input_device::RELATIVE_PER_PIXEL;
- m_h += event.wheel.preciseX * input_device::RELATIVE_PER_PIXEL;
+ m_v += std::lround(event.wheel.preciseY * 120 * input_device::RELATIVE_PER_PIXEL);
+ m_h += std::lround(event.wheel.preciseX * 120 * input_device::RELATIVE_PER_PIXEL);
#else
- m_v += event.wheel.y * input_device::RELATIVE_PER_PIXEL;
- m_h += event.wheel.x * input_device::RELATIVE_PER_PIXEL;
+ m_v += event.wheel.y * 120 * input_device::RELATIVE_PER_PIXEL;
+ m_h += event.wheel.x * 120 * input_device::RELATIVE_PER_PIXEL;
#endif
break;
}
diff --git a/src/osd/modules/input/input_win32.cpp b/src/osd/modules/input/input_win32.cpp
index 3803bc2a180..49c211740c0 100644
--- a/src/osd/modules/input/input_win32.cpp
+++ b/src/osd/modules/input/input_win32.cpp
@@ -129,13 +129,15 @@ public:
// win32_mouse_device
//============================================================
-class win32_mouse_device : public event_based_device<MouseButtonEventArgs>
+class win32_mouse_device : public event_based_device<MouseUpdateEventArgs>
{
public:
win32_mouse_device(std::string &&name, std::string &&id, input_module &module) :
event_based_device(std::move(name), std::move(id), module),
m_mouse({0}),
- m_win32_mouse({{0}})
+ m_win32_mouse({{0}}),
+ m_vscroll(0),
+ m_hscroll(0)
{
}
@@ -146,7 +148,7 @@ public:
if (!relative_reset)
return;
- CURSORINFO cursor_info = {0};
+ CURSORINFO cursor_info = { 0 };
cursor_info.cbSize = sizeof(CURSORINFO);
GetCursorInfo(&cursor_info);
@@ -169,23 +171,42 @@ public:
SetCursorPos(m_win32_mouse.last_point.x, m_win32_mouse.last_point.y);
}
+
+ // update scroll axes
+ m_mouse.lV = std::exchange(m_vscroll, 0) * input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lH = std::exchange(m_hscroll, 0) * input_device::RELATIVE_PER_PIXEL;
}
virtual void configure(input_device &device) override
{
// populate the axes
- for (int axisnum = 0; axisnum < 2; axisnum++)
- {
- device.add_item(
- default_axis_name[axisnum],
- std::string_view(),
- input_item_id(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &m_mouse.lX + axisnum);
- }
+ device.add_item(
+ "X",
+ std::string_view(),
+ ITEM_ID_XAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lX);
+ device.add_item(
+ "Y",
+ std::string_view(),
+ ITEM_ID_YAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lY);
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_RZAXIS,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lH);
// populate the buttons
- for (int butnum = 0; butnum < 2; butnum++)
+ for (int butnum = 0; butnum < 5; butnum++)
{
device.add_item(
default_button_name(butnum),
@@ -200,13 +221,25 @@ public:
{
memset(&m_mouse, 0, sizeof(m_mouse));
memset(&m_win32_mouse, 0, sizeof(m_win32_mouse));
+ m_vscroll = m_hscroll = 0;
}
protected:
- virtual void process_event(MouseButtonEventArgs const &args) override
+ virtual void process_event(MouseUpdateEventArgs const &args) override
{
// set the button state
- m_mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00;
+ assert(!(args.pressed & args.released));
+ for (unsigned i = 0; 5 > i; ++i)
+ {
+ if (BIT(args.pressed, i))
+ m_mouse.rgbButtons[i] = 0x80;
+ else if (BIT(args.released, i))
+ m_mouse.rgbButtons[i] = 0x00;
+ }
+
+ // accumulate scroll delta
+ m_vscroll += args.vdelta;
+ m_hscroll += args.hdelta;
}
private:
@@ -217,6 +250,7 @@ private:
mouse_state m_mouse;
win32_mouse_state m_win32_mouse;
+ long m_vscroll, m_hscroll;
};
@@ -244,14 +278,18 @@ public:
virtual bool handle_input_event(input_event eventid, void *eventdata) override
{
- if (!manager().class_enabled(DEVICE_CLASS_MOUSE) || eventid != INPUT_EVENT_MOUSE_BUTTON)
- return false;
-
- auto const *const args = static_cast<MouseButtonEventArgs *>(eventdata);
- devicelist().for_each_device(
- [args] (auto &device) { device.queue_events(args, 1); });
+ if (manager().class_enabled(DEVICE_CLASS_MOUSE))
+ {
+ if ((eventid == INPUT_EVENT_MOUSE_BUTTON) || (eventid == INPUT_EVENT_MOUSE_WHEEL))
+ {
+ auto const *const args = reinterpret_cast<MouseUpdateEventArgs *>(eventdata);
+ devicelist().for_each_device(
+ [args] (auto &device) { device.queue_events(args, 1); });
+ return true;
+ }
+ }
- return true;
+ return false;
}
};
@@ -260,7 +298,7 @@ public:
// win32_lightgun_device_base
//============================================================
-class win32_lightgun_device_base : public event_based_device<MouseButtonEventArgs>
+class win32_lightgun_device_base : public event_based_device<MouseUpdateEventArgs>
{
public:
virtual void reset() override
@@ -268,7 +306,17 @@ public:
memset(&m_mouse, 0, sizeof(m_mouse));
}
- virtual void configure(input_device &device) override
+protected:
+ win32_lightgun_device_base(
+ std::string &&name,
+ std::string &&id,
+ input_module &module) :
+ event_based_device(std::move(name), std::move(id), module),
+ m_mouse({ 0 })
+ {
+ }
+
+ void do_configure(input_device &device, unsigned buttons)
{
// populate the axes
for (int axisnum = 0; axisnum < 2; axisnum++)
@@ -282,7 +330,7 @@ public:
}
// populate the buttons
- for (int butnum = 0; butnum < 2; butnum++)
+ for (int butnum = 0; butnum < buttons; butnum++)
{
device.add_item(
default_button_name(butnum),
@@ -293,16 +341,6 @@ public:
}
}
-protected:
- win32_lightgun_device_base(
- std::string &&name,
- std::string &&id,
- input_module &module) :
- event_based_device(std::move(name), std::move(id), module),
- m_mouse({ 0 })
- {
- }
-
mouse_state m_mouse;
};
@@ -319,7 +357,9 @@ public:
std::string &&name,
std::string &&id,
input_module &module) :
- win32_lightgun_device_base(std::move(name), std::move(id), module)
+ win32_lightgun_device_base(std::move(name), std::move(id), module),
+ m_vscroll(0),
+ m_hscroll(0)
{
}
@@ -348,14 +388,60 @@ public:
// update the X/Y positions
m_mouse.lX = xpos;
m_mouse.lY = ypos;
+
+ // update the scroll axes if appropriate
+ if (relative_reset)
+ {
+ m_mouse.lV = std::exchange(m_vscroll, 0) * input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lH = std::exchange(m_hscroll, 0) * input_device::RELATIVE_PER_PIXEL;
+ }
+ }
+
+ virtual void configure(input_device &device) override
+ {
+ do_configure(device, 5);
+
+ // add scroll axes
+ device.add_item(
+ "Scroll V",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE1,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lV);
+ device.add_item(
+ "Scroll H",
+ std::string_view(),
+ ITEM_ID_ADD_RELATIVE2,
+ generic_axis_get_state<LONG>,
+ &m_mouse.lH);
+ }
+
+ virtual void reset() override
+ {
+ win32_lightgun_device_base::reset();
+ m_vscroll = m_hscroll = 0;
}
protected:
- virtual void process_event(MouseButtonEventArgs const &args) override
+ virtual void process_event(MouseUpdateEventArgs const &args) override
{
// In non-shared axis mode, just update the button state
- m_mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00;
+ assert(!(args.pressed & args.released));
+ for (unsigned i = 0; 5 > i; ++i)
+ {
+ if (BIT(args.pressed, i))
+ m_mouse.rgbButtons[i] = 0x80;
+ else if (BIT(args.released, i))
+ m_mouse.rgbButtons[i] = 0x00;
+ }
+
+ // accumulate scroll delta
+ m_vscroll += args.vdelta;
+ m_hscroll += args.hdelta;
}
+
+private:
+ long m_vscroll, m_hscroll;
};
@@ -376,38 +462,42 @@ public:
{
}
-protected:
- virtual void process_event(MouseButtonEventArgs const &args) override
+ virtual void configure(input_device &device) override
{
- int const button = args.button;
-
- if (button > 3)
- return; // We only handle the first four buttons in shared axis mode
- else if (button >= 2 && m_gun_index == 0)
- return; // First gun doesn't handle buttons 2 and 3
- else if (button < 2 && m_gun_index == 1)
- return; // Second gun doesn't handle buttons 0 and 1
-
- // Adjust the button if we're the second lightgun
- int const logical_button = (m_gun_index == 1) ? (button - 2) : button;
+ do_configure(device, 2);
+ }
- // set the button state
- m_mouse.rgbButtons[logical_button] = args.keydown ? 0x80 : 0x00;
- if (args.keydown)
+protected:
+ virtual void process_event(MouseUpdateEventArgs const &args) override
+ {
+ // We only handle the first four buttons in shared axis mode
+ assert(!(args.pressed & args.released));
+ for (unsigned i = 0; 2 > i; ++i)
{
- // get the position relative to the window
- HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window();
- RECT client_rect;
- GetClientRect(hwnd, &client_rect);
-
- POINT mousepos;
- mousepos.x = args.xpos;
- mousepos.y = args.ypos;
- ScreenToClient(hwnd, &mousepos);
-
- // convert to absolute coordinates
- m_mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
- m_mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
+ // Adjust the button if we're the second lightgun
+ unsigned const bit = i + ((1 == m_gun_index) ? 2 : 0);
+ if (BIT(args.pressed, bit))
+ {
+ m_mouse.rgbButtons[i] = 0x80;
+
+ // get the position relative to the window
+ HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window();
+ RECT client_rect;
+ GetClientRect(hwnd, &client_rect);
+
+ POINT mousepos;
+ mousepos.x = args.xpos;
+ mousepos.y = args.ypos;
+ ScreenToClient(hwnd, &mousepos);
+
+ // convert to absolute coordinates
+ m_mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right);
+ m_mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom);
+ }
+ else if (BIT(args.released, bit))
+ {
+ m_mouse.rgbButtons[i] = 0x00;
+ }
}
}
@@ -449,14 +539,18 @@ public:
virtual bool handle_input_event(input_event eventid, void *eventdata) override
{
- if (!manager().class_enabled(DEVICE_CLASS_LIGHTGUN) || eventid != INPUT_EVENT_MOUSE_BUTTON)
- return false;
-
- auto const *const args = static_cast<MouseButtonEventArgs *>(eventdata);
- devicelist().for_each_device(
- [args] (auto &device) { device.queue_events(args, 1); });
+ if (manager().class_enabled(DEVICE_CLASS_LIGHTGUN))
+ {
+ if ((eventid == INPUT_EVENT_MOUSE_BUTTON) || (eventid == INPUT_EVENT_MOUSE_WHEEL))
+ {
+ auto const *const args = reinterpret_cast<MouseUpdateEventArgs *>(eventdata);
+ devicelist().for_each_device(
+ [args] (auto &device) { device.queue_events(args, 1); });
+ return true;
+ }
+ }
- return true;
+ return false;
}
};
diff --git a/src/osd/modules/input/input_wincommon.h b/src/osd/modules/input/input_wincommon.h
index d77ab8dce9a..6b34d60abca 100644
--- a/src/osd/modules/input/input_wincommon.h
+++ b/src/osd/modules/input/input_wincommon.h
@@ -25,12 +25,13 @@ struct keyboard_state
int8_t currkey[MAX_KEYS];
};
-// state information for a mouse (matches DIMOUSESTATE exactly)
+// state information for a mouse
struct mouse_state
{
LONG lX;
LONG lY;
- LONG lZ;
+ LONG lV;
+ LONG lH;
BYTE rgbButtons[8];
};
diff --git a/src/osd/modules/osdwindow.h b/src/osd/modules/osdwindow.h
index f3d39878b5e..9167a775682 100644
--- a/src/osd/modules/osdwindow.h
+++ b/src/osd/modules/osdwindow.h
@@ -15,6 +15,8 @@
#include "osdhelper.h"
#include "../frontend/mame/ui/menuitem.h"
+#include <cassert>
+#include <chrono>
#include <memory>
#include <string>
#include <vector>
@@ -94,6 +96,65 @@ public:
virtual void complete_destroy() = 0;
protected:
+ static inline constexpr int CLICK_DISTANCE = 16; // in pointer units squared
+ static inline constexpr int TAP_DISTANCE = 49; // taps are less repeatable than clicks
+
+ struct prev_touch
+ {
+ prev_touch() = default;
+ prev_touch(prev_touch const &) = default;
+ prev_touch(prev_touch &&) = default;
+ prev_touch &operator=(prev_touch const &) = default;
+ prev_touch &operator=(prev_touch &&) = default;
+
+ std::chrono::steady_clock::time_point when = std::chrono::steady_clock::time_point::min();
+ int x = 0, y = 0, cnt = 0;
+ };
+
+ struct pointer_dev_info
+ {
+ pointer_dev_info() = default;
+ pointer_dev_info(pointer_dev_info const &) = default;
+ pointer_dev_info(pointer_dev_info &&) = default;
+ pointer_dev_info &operator=(pointer_dev_info const &) = default;
+ pointer_dev_info &operator=(pointer_dev_info &&) = default;
+
+ unsigned clear_expired_touches(std::chrono::steady_clock::time_point const &now, std::chrono::steady_clock::duration const &time);
+ unsigned consume_touch(unsigned i);
+
+ prev_touch touches[8];
+ };
+
+ struct pointer_info
+ {
+ pointer_info(pointer_info const &) = default;
+ pointer_info(pointer_info &&) = default;
+ pointer_info &operator=(pointer_info const &) = default;
+ pointer_info &operator=(pointer_info &&) = default;
+
+ pointer_info(unsigned i, unsigned d);
+
+ void primary_down(
+ int cx,
+ int cy,
+ std::chrono::steady_clock::duration const &time,
+ int tolerance,
+ bool checkprev,
+ std::vector<pointer_dev_info> &devices);
+ void check_primary_hold_drag(
+ int cx,
+ int cy,
+ std::chrono::steady_clock::duration const &time,
+ int tolerance);
+
+ std::chrono::steady_clock::time_point pressed;
+ unsigned index, device;
+ int x, y;
+ unsigned buttons;
+ int pressedx, pressedy;
+ int clickcnt;
+ };
+
osd_window(
running_machine &machine,
render_module &renderprovider,
@@ -127,6 +188,140 @@ private:
const std::string m_title;
};
+
+inline unsigned osd_window::pointer_dev_info::clear_expired_touches(
+ std::chrono::steady_clock::time_point const &now,
+ std::chrono::steady_clock::duration const &time)
+{
+ // find first non-expired touch (if any)
+ unsigned end(0);
+ while ((std::size(touches) > end) && touches[end].cnt && ((touches[end].when + time) < now))
+ touches[end++].cnt = 0;
+
+ // shift non-expired touches back if necessary
+ if (end)
+ {
+ unsigned pos(0);
+ while ((std::size(touches) > end) && touches[end].cnt)
+ {
+ touches[pos++] = std::move(touches[end]);
+ touches[end++].cnt = 0;
+ }
+ return pos;
+ }
+ else
+ {
+ while ((std::size(touches) > end) && touches[end].cnt)
+ ++end;
+ return end;
+ }
+}
+
+inline unsigned osd_window::pointer_dev_info::consume_touch(unsigned i)
+{
+ assert(std::size(touches) > i);
+ touches[i].cnt = 0;
+ unsigned pos(i + 1);
+ while ((std::size(touches) > pos) && touches[pos].cnt)
+ {
+ touches[i++] = std::move(touches[pos]);
+ touches[pos++].cnt = 0;
+ }
+ return i;
+}
+
+inline osd_window::pointer_info::pointer_info(unsigned i, unsigned d) :
+ pressed(std::chrono::steady_clock::time_point::min()),
+ index(i),
+ device(d),
+ x(-1),
+ y(-1),
+ buttons(0),
+ pressedx(0),
+ pressedy(0),
+ clickcnt(0)
+{
+}
+
+inline void osd_window::pointer_info::primary_down(
+ int cx,
+ int cy,
+ std::chrono::steady_clock::duration const &time,
+ int tolerance,
+ bool checkprev,
+ std::vector<pointer_dev_info> &devices)
+{
+ auto const now(std::chrono::steady_clock::now());
+ auto const exp(time + pressed);
+ if (0 > clickcnt)
+ {
+ // previous click turned into a hold/drag
+ clickcnt = 1;
+ }
+ else if (clickcnt)
+ {
+ // potential multi-click action
+ int const dx(cx - pressedx);
+ int const dy(cy - pressedy);
+ int const distance((dx * dx) + (dy * dy));
+ if ((exp < now) || (tolerance < distance))
+ clickcnt = 1;
+ else
+ ++clickcnt;
+ }
+ else
+ {
+ // first click for this pointer, but may need to check previous touches
+ clickcnt = 1;
+ if (checkprev)
+ {
+ if (devices.size() > device)
+ {
+ auto &devinfo(devices[device]);
+ unsigned const end(devinfo.clear_expired_touches(now, time));
+ for (int i = 0; end > i; ++i)
+ {
+ int const dx(cx - devinfo.touches[i].x);
+ int const dy(cy - devinfo.touches[i].y);
+ int const distance((dx * dx) + (dy * dy));
+ if (tolerance >= distance)
+ {
+ // close match - consume it
+ clickcnt = devinfo.touches[i].cnt + 1;
+ devinfo.consume_touch(i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // record the time/location where the pointer was pressed
+ pressed = now;
+ pressedx = cx;
+ pressedy = cy;
+}
+
+inline void osd_window::pointer_info::check_primary_hold_drag(
+ int cx,
+ int cy,
+ std::chrono::steady_clock::duration const &time,
+ int tolerance)
+{
+ // check for conversion to a (multi-)click-and-hold/drag
+ if (0 < clickcnt)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const exp(time + pressed);
+ int const dx(cx - pressedx);
+ int const dy(cy - pressedy);
+ int const distance((dx * dx) + (dy * dy));
+ if ((exp < now) || (tolerance < distance))
+ clickcnt = -clickcnt;
+ }
+}
+
+
template <class TWindowHandle>
class osd_window_t : public osd_window
{
diff --git a/src/osd/sdl/osdsdl.cpp b/src/osd/sdl/osdsdl.cpp
index 2d3e5e4f02c..87af652733e 100644
--- a/src/osd/sdl/osdsdl.cpp
+++ b/src/osd/sdl/osdsdl.cpp
@@ -17,8 +17,8 @@
#include "ui/uimain.h"
-#include <SDL2/SDL.h>
-
+#include <algorithm>
+#include <cmath>
#include <cstdio>
#include <cstring>
@@ -168,7 +168,9 @@ sdl_osd_interface::sdl_osd_interface(sdl_options &options) :
m_modifier_keys(0),
m_last_click_time(std::chrono::steady_clock::time_point::min()),
m_last_click_x(0),
- m_last_click_y(0)
+ m_last_click_y(0),
+ m_enable_touch(false),
+ m_next_ptrdev(0)
{
}
@@ -260,6 +262,34 @@ void sdl_osd_interface::init(running_machine &machine)
}
}
+ /* do we want touch support or will we use mouse emulation? */
+ m_enable_touch = options().enable_touch();
+ try
+ {
+ if (m_enable_touch)
+ {
+ int const count(SDL_GetNumTouchDevices());
+ m_ptrdev_map.reserve(std::max<int>(count + 1, 8));
+ map_pointer_device(SDL_MOUSE_TOUCHID);
+ for (int i = 0; count > i; ++i)
+ {
+ SDL_TouchID const device(SDL_GetTouchDevice(i));
+ if (device)
+ map_pointer_device(device);
+ }
+ }
+ else
+ {
+ m_ptrdev_map.reserve(1);
+ map_pointer_device(SDL_MOUSE_TOUCHID);
+ }
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_osd_interface: error allocating pointer data\n");
+ // survivable - it will still attempt to allocate mappings when it first sees devices
+ }
+
#if defined(SDLMAME_ANDROID)
SDL_SetHint(SDL_HINT_VIDEO_EXTERNAL_CONTEXT, "1");
#endif
@@ -502,74 +532,126 @@ void sdl_osd_interface::process_events()
break;
case SDL_MOUSEMOTION:
+ if (!m_enable_touch || (SDL_TOUCH_MOUSEID != event.motion.which))
{
- int cx, cy;
- auto const window = focus_window(event.motion);
- if (window && window->xy_to_render_target(event.motion.x, event.motion.y, &cx, &cy))
- machine().ui_input().push_mouse_move_event(window->target(), cx, cy);
+ auto const window = window_from_id(event.motion.windowID);
+ if (!window)
+ break;
+
+ unsigned device;
+ try
+ {
+ device = map_pointer_device(SDL_MOUSE_TOUCHID);
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_osd_interface: error allocating pointer data\n");
+ break;
+ }
+
+ int x, y;
+ window->xy_to_render_target(event.motion.x, event.motion.y, &x, &y);
+ window->mouse_moved(device, x, y);
}
break;
case SDL_MOUSEBUTTONDOWN:
- //printf("But down %d %d %d %d %s\n", event.button.which, event.button.button, event.button.x, event.button.y, devinfo->name.c_str());
- if (event.button.button == 1)
+ case SDL_MOUSEBUTTONUP:
+ if (!m_enable_touch || (SDL_TOUCH_MOUSEID != event.button.which))
+ {
+ auto const window = window_from_id(event.button.windowID);
+ if (!window)
+ break;
+
+ unsigned device;
+ try
+ {
+ device = map_pointer_device(SDL_MOUSE_TOUCHID);
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_osd_interface: error allocating pointer data\n");
+ break;
+ }
+
+ int x, y;
+ window->xy_to_render_target(event.button.x, event.button.y, &x, &y);
+ unsigned button(event.button.button - 1);
+ if ((1 == button) || (2 == button))
+ button ^= 3;
+ if (SDL_PRESSED == event.button.state)
+ window->mouse_down(device, x, y, button);
+ else
+ window->mouse_up(device, x, y, button);
+ }
+ break;
+
+ case SDL_MOUSEWHEEL:
{
- int cx, cy;
- auto const window = focus_window(event.button);
- if (window && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy))
+ auto const window = window_from_id(event.wheel.windowID);
+ if (window)
{
- auto const double_click_speed = std::chrono::milliseconds(250);
- auto const click = std::chrono::steady_clock::now();
- machine().ui_input().push_mouse_down_event(window->target(), cx, cy);
-
- // avoid overflow with std::chrono::time_point::min() by adding rather than subtracting
- if (click < (m_last_click_time + double_click_speed)
- && (cx >= (m_last_click_x - 4) && cx <= (m_last_click_x + 4))
- && (cy >= (m_last_click_y - 4) && cy <= (m_last_click_y + 4)))
+ unsigned device;
+ try
{
- m_last_click_time = std::chrono::time_point<std::chrono::steady_clock>::min();
- machine().ui_input().push_mouse_double_click_event(window->target(), cx, cy);
+ device = map_pointer_device(SDL_MOUSE_TOUCHID);
}
- else
+ catch (std::bad_alloc const &)
{
- m_last_click_time = click;
- m_last_click_x = cx;
- m_last_click_y = cy;
+ osd_printf_error("sdl_osd_interface: error allocating pointer data\n");
+ break;
}
+#if SDL_VERSION_ATLEAST(2, 0, 18)
+ window->mouse_wheel(device, std::lround(event.wheel.preciseY * 120));
+#else
+ window->mouse_wheel(device, event.wheel.y);
+#endif
}
}
- else if (event.button.button == 3)
- {
- int cx, cy;
- auto const window = focus_window(event.button);
- if (window != nullptr && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy))
- machine().ui_input().push_mouse_rdown_event(window->target(), cx, cy);
- }
break;
- case SDL_MOUSEBUTTONUP:
- //printf("But up %d %d %d %d\n", event.button.which, event.button.button, event.button.x, event.button.y);
- if (event.button.button == 1)
+ case SDL_FINGERMOTION:
+ case SDL_FINGERDOWN:
+ case SDL_FINGERUP:
+ if (m_enable_touch && (SDL_MOUSE_TOUCHID != event.tfinger.touchId))
{
- int cx, cy;
- auto const window = focus_window(event.button);
- if (window && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy))
- machine().ui_input().push_mouse_up_event(window->target(), cx, cy);
- }
- else if (event.button.button == 3)
- {
- int cx, cy;
- auto window = focus_window(event.button);
- if (window && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy))
- machine().ui_input().push_mouse_rup_event(window->target(), cx, cy);
- }
- break;
+ // ignore if it doesn't map to a window we own
+ auto const window = window_from_id(event.tfinger.windowID);
+ if (!window)
+ break;
+
+ // map SDL touch device ID to a zero-based device number
+ unsigned device;
+ try
+ {
+ device = map_pointer_device(event.tfinger.touchId);
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_osd_interface: error allocating pointer data\n");
+ break;
+ }
- case SDL_MOUSEWHEEL:
- {
- auto const window = focus_window(event.wheel);
- if (window)
- machine().ui_input().push_mouse_wheel_event(window->target(), 0, 0, event.wheel.y, 3);
+ // convert normalised coordinates to what MAME wants
+ auto const size = window->get_size();
+ int const winx = std::lround(event.tfinger.x * size.width());
+ int const winy = std::lround(event.tfinger.y * size.height());
+ int x, y;
+ window->xy_to_render_target(winx, winy, &x, &y);
+
+ // call appropriate window method
+ switch (event.type)
+ {
+ case SDL_FINGERMOTION:
+ window->finger_moved(event.tfinger.fingerId, device, x, y);
+ break;
+ case SDL_FINGERDOWN:
+ window->finger_down(event.tfinger.fingerId, device, x, y);
+ break;
+ case SDL_FINGERUP:
+ window->finger_up(event.tfinger.fingerId, device, x, y);
+ break;
+ }
}
break;
}
@@ -626,12 +708,37 @@ void sdl_osd_interface::process_window_event(SDL_Event const &event)
break;
case SDL_WINDOWEVENT_ENTER:
- m_mouse_over_window = 1;
+ {
+ m_mouse_over_window = 1;
+ unsigned device;
+ try
+ {
+ device = map_pointer_device(SDL_MOUSE_TOUCHID);
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_osd_interface: error allocating pointer data\n");
+ break;
+ }
+ window->mouse_entered(device);
+ }
break;
case SDL_WINDOWEVENT_LEAVE:
- machine().ui_input().push_mouse_leave_event(window->target());
- m_mouse_over_window = 0;
+ {
+ m_mouse_over_window = 0;
+ unsigned device;
+ try
+ {
+ device = map_pointer_device(SDL_MOUSE_TOUCHID);
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_osd_interface: error allocating pointer data\n");
+ break;
+ }
+ window->mouse_left(device);
+ }
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
@@ -717,9 +824,28 @@ void sdl_osd_interface::check_osd_inputs()
template <typename T>
sdl_window_info *sdl_osd_interface::focus_window(T const &event) const
{
- // FIXME: SDL does not properly report the window for certain OS.
- if (false)
+ // FIXME: SDL does not properly report the window for certain versions of Ubuntu - is this still relevant?
+ if (m_enable_touch)
return window_from_id(event.windowID);
else
return m_focus_window;
}
+
+
+unsigned sdl_osd_interface::map_pointer_device(SDL_TouchID device)
+{
+ auto devpos(std::lower_bound(
+ m_ptrdev_map.begin(),
+ m_ptrdev_map.end(),
+ device,
+ [] (std::pair<SDL_TouchID, unsigned> const &mapping, SDL_TouchID id)
+ {
+ return mapping.first < id;
+ }));
+ if ((m_ptrdev_map.end() == devpos) || (device != devpos->first))
+ {
+ devpos = m_ptrdev_map.emplace(devpos, device, m_next_ptrdev);
+ ++m_next_ptrdev;
+ }
+ return devpos->second;
+}
diff --git a/src/osd/sdl/osdsdl.h b/src/osd/sdl/osdsdl.h
index c8ce167d699..e1d472d9e45 100644
--- a/src/osd/sdl/osdsdl.h
+++ b/src/osd/sdl/osdsdl.h
@@ -10,11 +10,14 @@
#include "modules/lib/osdobj_common.h"
#include "modules/osdmodule.h"
+#include <SDL2/SDL.h>
+
#include <cassert>
#include <chrono>
#include <memory>
#include <mutex>
#include <unordered_map>
+#include <utility>
#include <string>
#include <vector>
@@ -128,8 +131,6 @@ public:
};
-union SDL_Event;
-
using sdl_event_manager = event_subscription_manager<SDL_Event, uint32_t>;
@@ -194,6 +195,8 @@ private:
bool mouse_over_window() const { return m_mouse_over_window > 0; }
template <typename T> sdl_window_info *focus_window(T const &event) const;
+ unsigned map_pointer_device(SDL_TouchID device);
+
sdl_options &m_options;
sdl_window_info *m_focus_window;
int m_mouse_over_window;
@@ -202,6 +205,10 @@ private:
std::chrono::steady_clock::time_point m_last_click_time;
int m_last_click_x;
int m_last_click_y;
+
+ bool m_enable_touch;
+ unsigned m_next_ptrdev;
+ std::vector<std::pair<SDL_TouchID, unsigned> > m_ptrdev_map;
};
//============================================================
diff --git a/src/osd/sdl/sdlopts.cpp b/src/osd/sdl/sdlopts.cpp
index 8e922cb7a4e..f08dc41f963 100644
--- a/src/osd/sdl/sdlopts.cpp
+++ b/src/osd/sdl/sdlopts.cpp
@@ -66,7 +66,8 @@ const options_entry f_sdl_option_entries[] =
{ SDLOPTION_KEYMAP_FILE, "keymap.dat", core_options::option_type::PATH, "keymap filename" },
// joystick mapping
- { nullptr, nullptr, core_options::option_type::HEADER, "SDL JOYSTICK MAPPING" },
+ { nullptr, nullptr, core_options::option_type::HEADER, "SDL INPUT OPTIONS" },
+ { SDLOPTION_ENABLE_TOUCH, "0", core_options::option_type::BOOLEAN, "enable touch input support" },
{ SDLOPTION_SIXAXIS, "0", core_options::option_type::BOOLEAN, "use special handling for PS3 Sixaxis controllers" },
#if (USE_XINPUT)
diff --git a/src/osd/sdl/sdlopts.h b/src/osd/sdl/sdlopts.h
index d754a0e8619..fe75e097407 100644
--- a/src/osd/sdl/sdlopts.h
+++ b/src/osd/sdl/sdlopts.h
@@ -26,6 +26,7 @@
#define SDLOPTION_KEYMAP "keymap"
#define SDLOPTION_KEYMAP_FILE "keymap_file"
+#define SDLOPTION_ENABLE_TOUCH "enable_touch"
#define SDLOPTION_SIXAXIS "sixaxis"
#if defined(USE_XINPUT) && USE_XINPUT
#define SDLOPTION_LIGHTGUNINDEX "lightgun_index"
@@ -82,7 +83,8 @@ public:
bool keymap() const { return bool_value(SDLOPTION_KEYMAP); }
const char *keymap_file() const { return value(SDLOPTION_KEYMAP_FILE); }
- // joystick mapping
+ // input options
+ bool enable_touch() const { return bool_value(SDLOPTION_ENABLE_TOUCH); }
bool sixaxis() const { return bool_value(SDLOPTION_SIXAXIS); }
const char *video_driver() const { return value(SDLOPTION_VIDEODRIVER); }
diff --git a/src/osd/sdl/window.cpp b/src/osd/sdl/window.cpp
index 4bb8531df2a..1e6318f57a9 100644
--- a/src/osd/sdl/window.cpp
+++ b/src/osd/sdl/window.cpp
@@ -13,24 +13,27 @@
#include "emuopts.h"
#include "render.h"
#include "screen.h"
+#include "uiinput.h"
#include "ui/uimain.h"
// OSD headers
-#include "window.h"
-#include "osdsdl.h"
#include "modules/monitor/monitor_common.h"
+#include "osdsdl.h"
+#include "window.h"
// standard SDL headers
-#include <SDL2/SDL.h>
#include <SDL2/SDL_syswm.h>
// standard C headers
+#include <algorithm>
+#include <cassert>
#include <cmath>
+#include <list>
+#include <memory>
+
#ifndef _MSC_VER
#include <unistd.h>
#endif
-#include <list>
-#include <memory>
#ifdef SDLMAME_WIN32
#include <windows.h>
@@ -61,11 +64,6 @@
#define SDL_VERSION_EQUALS(v1, vnum2) (SDL_VERSIONNUM(v1.major, v1.minor, v1.patch) == vnum2)
-class SDL_DM_Wrapper
-{
-public:
- SDL_DisplayMode mode;
-};
//============================================================
@@ -248,9 +246,9 @@ void sdl_window_info::toggle_full_screen()
#endif
if (fullscreen() && (video_config.switchres || is_osx))
{
- SDL_SetWindowFullscreen(platform_window(), 0); // Try to set mode
- SDL_SetWindowDisplayMode(platform_window(), &m_original_mode->mode); // Try to set mode
- SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN); // Try to set mode
+ SDL_SetWindowFullscreen(platform_window(), 0);
+ SDL_SetWindowDisplayMode(platform_window(), &m_original_mode);
+ SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN);
}
SDL_DestroyWindow(platform_window());
set_platform_window(nullptr);
@@ -334,6 +332,293 @@ int sdl_window_info::xy_to_render_target(int x, int y, int *xt, int *yt)
return renderer().xy_to_render_target(x, y, xt, yt);
}
+void sdl_window_info::mouse_entered(unsigned device)
+{
+ m_mouse_inside = true;
+}
+
+void sdl_window_info::mouse_left(unsigned device)
+{
+ m_mouse_inside = false;
+
+ auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), SDL_FingerID(-1), &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() == info) || (info->finger != SDL_FingerID(-1)))
+ return;
+
+ // leaving implicitly releases buttons, so check hold/drag if necessary
+ if (BIT(info->buttons, 0))
+ {
+ assert(0 <= info->clickcnt);
+
+ auto const now(std::chrono::steady_clock::now());
+ auto const exp(std::chrono::milliseconds(250) + info->pressed);
+ int const dx(info->x - info->pressedx);
+ int const dy(info->y - info->pressedy);
+ int const distance((dx * dx) + (dy * dy));
+ if ((exp < now) || (CLICK_DISTANCE < distance))
+ info->clickcnt = -info->clickcnt;
+ }
+
+ // push to UI manager
+ machine().ui_input().push_pointer_leave(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ info->x, info->y,
+ info->buttons, info->clickcnt);
+
+ // dump pointer data
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+}
+
+void sdl_window_info::mouse_down(unsigned device, int x, int y, unsigned button)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ if ((x == info->x) && (y == info->y) && BIT(info->buttons, button))
+ return;
+
+ // detect multi-click actions
+ if (0 == button)
+ {
+ info->primary_down(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ CLICK_DISTANCE,
+ false,
+ m_ptrdev_info);
+ }
+
+ // update info and push to UI manager
+ auto const pressed(decltype(info->buttons)(1) << button);
+ info->x = x;
+ info->y = y;
+ info->buttons |= pressed;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ x, y,
+ info->buttons, pressed, 0, info->clickcnt);
+}
+
+void sdl_window_info::mouse_up(unsigned device, int x, int y, unsigned button)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ if ((x == info->x) && (y == info->y) && !BIT(info->buttons, button))
+ return;
+
+ // detect multi-click actions
+ if (0 == button)
+ {
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ CLICK_DISTANCE);
+ }
+
+ // update info and push to UI manager
+ auto const released(decltype(info->buttons)(1) << button);
+ info->x = x;
+ info->y = y;
+ info->buttons &= ~released;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ x, y,
+ info->buttons, 0, released, info->clickcnt);
+}
+
+void sdl_window_info::mouse_moved(unsigned device, int x, int y)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ // detect multi-click actions
+ if (BIT(info->buttons, 0))
+ {
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ CLICK_DISTANCE);
+ }
+
+ // update info and push to UI manager
+ info->x = x;
+ info->y = y;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::MOUSE,
+ info->index,
+ device,
+ x, y,
+ info->buttons, 0, 0, info->clickcnt);
+}
+
+void sdl_window_info::mouse_wheel(unsigned device, int y)
+{
+ if (!m_mouse_inside)
+ return;
+
+ auto const info(map_pointer(SDL_FingerID(-1), device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ // push to UI manager
+ machine().ui_input().push_mouse_wheel_event(target(), info->x, info->y, y, 3);
+}
+
+void sdl_window_info::finger_down(SDL_FingerID finger, unsigned device, int x, int y)
+{
+ auto const info(map_pointer(finger, device));
+ if (m_active_pointers.end() == info)
+ return;
+
+ assert(!info->buttons);
+
+ // detect multi-click actions
+ info->primary_down(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ TAP_DISTANCE,
+ true,
+ m_ptrdev_info);
+
+ // update info and push to UI manager
+ info->x = x;
+ info->y = y;
+ info->buttons = 1;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 1, 1, 0, info->clickcnt);
+}
+
+void sdl_window_info::finger_up(SDL_FingerID finger, unsigned device, int x, int y)
+{
+ auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() == info) || (info->finger != finger))
+ return;
+
+ assert(1 == info->buttons);
+
+ // check for conversion to a (multi-)click-and-hold/drag
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ TAP_DISTANCE);
+
+ // need to remember touches to recognise multi-tap gestures
+ if (0 < info->clickcnt)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const time = std::chrono::milliseconds(250);
+ if ((time + info->pressed) >= now)
+ {
+ try
+ {
+ unsigned i(0);
+ if (m_ptrdev_info.size() > device)
+ i = m_ptrdev_info[device].clear_expired_touches(now, time);
+ else
+ m_ptrdev_info.resize(device + 1);
+
+ if (std::size(m_ptrdev_info[device].touches) > i)
+ {
+ m_ptrdev_info[device].touches[i].when = info->pressed;
+ m_ptrdev_info[device].touches[i].x = info->pressedx;
+ m_ptrdev_info[device].touches[i].y = info->pressedy;
+ m_ptrdev_info[device].touches[i].cnt = info->clickcnt;
+ }
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ }
+ }
+ }
+
+ // push to UI manager
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 0, 0, 1, info->clickcnt);
+ machine().ui_input().push_pointer_leave(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 0, info->clickcnt);
+
+ // dump pointer data
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+}
+
+void sdl_window_info::finger_moved(SDL_FingerID finger, unsigned device, int x, int y)
+{
+ auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() == info) || (info->finger != finger))
+
+ assert(1 == info->buttons);
+
+ if ((x != info->x) || (y != info->y))
+ {
+ info->check_primary_hold_drag(
+ x,
+ y,
+ std::chrono::milliseconds(250),
+ TAP_DISTANCE);
+
+ // update info and push to UI manager
+ info->x = x;
+ info->y = y;
+ machine().ui_input().push_pointer_update(
+ target(),
+ osd::ui_event_handler::pointer::TOUCH,
+ info->index,
+ device,
+ x, y,
+ 1, 0, 0, info->clickcnt);
+ }
+}
+
//============================================================
// sdlwindow_video_window_create
// (main thread)
@@ -373,9 +658,9 @@ void sdl_window_info::complete_destroy()
if (fullscreen() && video_config.switchres)
{
- SDL_SetWindowFullscreen(platform_window(), 0); // Try to set mode
- SDL_SetWindowDisplayMode(platform_window(), &m_original_mode->mode); // Try to set mode
- SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN); // Try to set mode
+ SDL_SetWindowFullscreen(platform_window(), 0);
+ SDL_SetWindowDisplayMode(platform_window(), &m_original_mode);
+ SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN);
}
renderer_reset();
@@ -684,7 +969,7 @@ int sdl_window_info::complete_create()
SDL_DisplayMode mode;
//SDL_GetCurrentDisplayMode(window().monitor()->handle, &mode);
SDL_GetWindowDisplayMode(platform_window(), &mode);
- m_original_mode->mode = mode;
+ m_original_mode = mode;
mode.w = temp.width();
mode.h = temp.height();
if (m_win_config.refresh)
@@ -1021,6 +1306,52 @@ osd_dim sdl_window_info::get_max_bounds(int constrain)
return maximum.dim();
}
+
+std::vector<sdl_window_info::sdl_pointer_info>::iterator sdl_window_info::map_pointer(SDL_FingerID finger, unsigned device)
+{
+ auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->finger == finger))
+ return found;
+
+ if ((sizeof(m_next_pointer) * 8) <= m_next_pointer)
+ {
+ assert(~decltype(m_pointer_mask)(0) == m_pointer_mask);
+ osd_printf_warning("sdl_window_info: exceeded maximum number of active pointers\n");
+ return m_active_pointers.end();
+ }
+ assert(!BIT(m_pointer_mask, m_next_pointer));
+
+ try
+ {
+ found = m_active_pointers.emplace(
+ found,
+ sdl_pointer_info(finger, m_next_pointer, device));
+ m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer;
+ do
+ {
+ ++m_next_pointer;
+ }
+ while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer));
+
+ return found;
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("sdl_window_info: error allocating pointer data\n");
+ return m_active_pointers.end();
+ }
+}
+
+
+inline sdl_window_info::sdl_pointer_info::sdl_pointer_info(SDL_FingerID f, unsigned i, unsigned d)
+ : pointer_info(i, d)
+ , finger(f)
+{
+}
+
+
+
+
//============================================================
// construction and destruction
//============================================================
@@ -1040,13 +1371,18 @@ sdl_window_info::sdl_window_info(
, m_extra_flags(0)
, m_mouse_captured(false)
, m_mouse_hidden(false)
+ , m_pointer_mask(0)
+ , m_next_pointer(0)
+ , m_mouse_inside(false)
{
//FIXME: these should be per_window in config-> or even better a bit set
m_fullscreen = !video_config.windowed;
m_prescale = video_config.prescale;
m_windowed_dim = osd_dim(config->width, config->height);
- m_original_mode = std::make_unique<SDL_DM_Wrapper>();
+
+ m_ptrdev_info.reserve(1);
+ m_active_pointers.reserve(16);
}
sdl_window_info::~sdl_window_info()
diff --git a/src/osd/sdl/window.h b/src/osd/sdl/window.h
index 77477176b51..49fe452192f 100644
--- a/src/osd/sdl/window.h
+++ b/src/osd/sdl/window.h
@@ -14,22 +14,20 @@
#include "modules/osdwindow.h"
#include "osdsync.h"
+#include <SDL2/SDL.h>
+
+#include <chrono>
#include <cstdint>
#include <memory>
+#include <vector>
//============================================================
// TYPE DEFINITIONS
//============================================================
-struct SDL_Window;
-
class render_target;
-// forward of SDL_DisplayMode not possible (typedef struct) - define wrapper
-
-class SDL_DM_Wrapper;
-
typedef uintptr_t HashT;
#define OSDWORK_CALLBACK(name) void *name(void *param, int threadid)
@@ -65,26 +63,34 @@ public:
int xy_to_render_target(int x, int y, int *xt, int *yt);
-private:
- // window handle and info
- int m_startmaximized;
+ void mouse_entered(unsigned device);
+ void mouse_left(unsigned device);
+ void mouse_down(unsigned device, int x, int y, unsigned button);
+ void mouse_up(unsigned device, int x, int y, unsigned button);
+ void mouse_moved(unsigned device, int x, int y);
+ void mouse_wheel(unsigned device, int y);
+ void finger_down(SDL_FingerID finger, unsigned device, int x, int y);
+ void finger_up(SDL_FingerID finger, unsigned device, int x, int y);
+ void finger_moved(SDL_FingerID finger, unsigned device, int x, int y);
- // dimensions
- osd_dim m_minimum_dim;
- osd_dim m_windowed_dim;
+private:
+ struct sdl_pointer_info : public pointer_info
+ {
+ static constexpr bool compare(sdl_pointer_info const &info, SDL_FingerID finger) { return info.finger < finger; }
- // rendering info
- osd_event m_rendered_event;
+ sdl_pointer_info(sdl_pointer_info const &) = default;
+ sdl_pointer_info(sdl_pointer_info &&) = default;
+ sdl_pointer_info &operator=(sdl_pointer_info const &) = default;
+ sdl_pointer_info &operator=(sdl_pointer_info &&) = default;
- // Original display_mode
- std::unique_ptr<SDL_DM_Wrapper> m_original_mode;
+ sdl_pointer_info(SDL_FingerID, unsigned i, unsigned d);
- int m_extra_flags;
+ SDL_FingerID finger;
+ };
// returns 0 on success, else 1
int complete_create();
-private:
int wnd_extra_width();
int wnd_extra_height();
osd_rect constrain_to_aspect_ratio(const osd_rect &rect, int adjustment);
@@ -94,12 +100,35 @@ private:
osd_dim pick_best_mode();
void set_fullscreen(int afullscreen) { m_fullscreen = afullscreen; }
- // monitor info
- bool m_mouse_captured;
- bool m_mouse_hidden;
-
void measure_fps(int update);
+ std::vector<sdl_pointer_info>::iterator map_pointer(SDL_FingerID finger, unsigned device);
+
+ // window handle and info
+ int m_startmaximized;
+
+ // dimensions
+ osd_dim m_minimum_dim;
+ osd_dim m_windowed_dim;
+
+ // rendering info
+ osd_event m_rendered_event;
+
+ // Original display_mode
+ SDL_DisplayMode m_original_mode;
+
+ int m_extra_flags;
+
+ // monitor info
+ bool m_mouse_captured;
+ bool m_mouse_hidden;
+
+ // info on currently active pointers - 64 pointers ought to be enough for anyone
+ uint64_t m_pointer_mask;
+ unsigned m_next_pointer;
+ bool m_mouse_inside;
+ std::vector<pointer_dev_info> m_ptrdev_info;
+ std::vector<sdl_pointer_info> m_active_pointers;
};
#endif // MAME_OSD_SDL_WINDOW_H
diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp
index 87467a7e06f..690cc90658c 100644
--- a/src/osd/windows/window.cpp
+++ b/src/osd/windows/window.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
//============================================================
//
// window.cpp - Win32 window handling
@@ -64,6 +64,29 @@
#define WM_USER_SET_MINSIZE (WM_USER + 5)
+namespace {
+
+constexpr unsigned get_pointer_buttons(WPARAM wparam)
+{
+ return
+ (IS_POINTER_FIRSTBUTTON_WPARAM(wparam) ? 0x01 : 0x00) |
+ (IS_POINTER_SECONDBUTTON_WPARAM(wparam) ? 0x02 : 0x00) |
+ (IS_POINTER_THIRDBUTTON_WPARAM(wparam) ? 0x04 : 0x00) |
+ (IS_POINTER_FOURTHBUTTON_WPARAM(wparam) ? 0x08 : 0x00) |
+ (IS_POINTER_FIFTHBUTTON_WPARAM(wparam) ? 0x10 : 0x00);
+}
+
+constexpr osd::ui_event_handler::pointer convert_pointer_type(POINTER_INPUT_TYPE type)
+{
+ // PT_POINTER isn't a real type, and we'll leave PT_TOUCHPAD as unknown for now
+ return
+ (PT_TOUCH == type) ? osd::ui_event_handler::pointer::TOUCH :
+ (PT_PEN == type) ? osd::ui_event_handler::pointer::PEN :
+ (PT_MOUSE == type) ? osd::ui_event_handler::pointer::MOUSE :
+ osd::ui_event_handler::pointer::UNKNOWN;
+}
+
+} // anonymous namespace
//============================================================
// GLOBAL VARIABLES
@@ -190,6 +213,14 @@ void windows_osd_interface::window_exit()
}
+inline win_window_info::win_pointer_info::win_pointer_info(WORD p, POINTER_INPUT_TYPE t, unsigned i, unsigned d)
+ : pointer_info(i, d)
+ , ptrid(p)
+ , type(t)
+{
+}
+
+
win_window_info::win_window_info(
running_machine &machine,
render_module &renderprovider,
@@ -216,6 +247,10 @@ win_window_info::win_window_info(
, m_resize_state(RESIZE_STATE_NORMAL)
, m_main(nullptr)
, m_attached_mode(false)
+ , m_pointer_mask(0)
+ , m_next_pointer(0)
+ , m_next_ptrdev(0)
+ , m_tracking_mouse(false)
{
m_non_fullscreen_bounds.left = 0;
m_non_fullscreen_bounds.top = 0;
@@ -224,6 +259,10 @@ win_window_info::win_window_info(
m_fullscreen = !video_config.windowed;
m_prescale = video_config.prescale;
+
+ m_ptrdev_map.reserve(8);
+ m_ptrdev_info.reserve(1);
+ m_active_pointers.reserve(16);
}
POINT win_window_info::s_saved_cursor_pos = { -1, -1 };
@@ -314,9 +353,11 @@ static bool is_mame_window(HWND hwnd)
inline static BOOL handle_mouse_button(windows_osd_interface *osd, int button, int down, int x, int y)
{
- MouseButtonEventArgs args;
- args.button = button;
- args.keydown = down;
+ MouseUpdateEventArgs args;
+ args.pressed = (down ? 1 : 0) << button;
+ args.released = (down ? 0 : 1) << button;
+ args.vdelta = 0;
+ args.hdelta = 0;
args.xpos = x;
args.ypos = y;
@@ -328,6 +369,24 @@ inline static BOOL handle_mouse_button(windows_osd_interface *osd, int button, i
return handled && !osd->options().lightgun() && !osd->options().mouse();
}
+inline static BOOL handle_mouse_wheel(windows_osd_interface *osd, int v, int h, int x, int y)
+{
+ MouseUpdateEventArgs args;
+ args.pressed = 0;
+ args.released = 0;
+ args.vdelta = v;
+ args.hdelta = h;
+ args.xpos = x;
+ args.ypos = y;
+
+ bool handled = osd->handle_input_event(INPUT_EVENT_MOUSE_WHEEL, &args);
+
+ // When in lightgun mode or mouse mode, the mouse wheel may be routed to the input system
+ // because the mouse interactions in the UI are routed from the video_window_proc below
+ // we need to make sure they aren't suppressed in these cases.
+ return handled && !osd->options().lightgun() && !osd->options().mouse();
+}
+
inline static BOOL handle_keypress(windows_osd_interface *osd, int vkey, int down, int scancode, BOOL extended_key)
{
KeyPressEventArgs args;
@@ -408,6 +467,15 @@ void windows_osd_interface::process_events(bool ingame, bool nodispatch)
dispatch = !handle_mouse_button(this, 3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
break;
+ // forward mouse wheel movement to the input system
+ case WM_MOUSEWHEEL:
+ dispatch = !handle_mouse_wheel(this, GET_WHEEL_DELTA_WPARAM(message.wParam), 0, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
+ case WM_MOUSEHWHEEL:
+ dispatch = !handle_mouse_wheel(this, 0, GET_WHEEL_DELTA_WPARAM(message.wParam), GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam));
+ break;
+
case WM_KEYDOWN:
if (NOT_ALREADY_DOWN(message.lParam))
dispatch = !handle_keypress(this, message.wParam, TRUE, SCAN_CODE(message.lParam), IS_EXTENDED(message.lParam));
@@ -1066,49 +1134,7 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
case WM_SYSKEYDOWN:
break;
- // input events
- case WM_MOUSEMOVE:
- window->machine().ui_input().push_mouse_move_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
- case WM_MOUSELEAVE:
- window->machine().ui_input().push_mouse_leave_event(window->target());
- break;
-
- case WM_LBUTTONDOWN:
- {
- auto const ticks = std::chrono::steady_clock::now();
- window->machine().ui_input().push_mouse_down_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
-
- // check for a double-click - avoid overflow by adding times rather than subtracting
- if (ticks < (window->m_lastclicktime + std::chrono::milliseconds(GetDoubleClickTime())) &&
- GET_X_LPARAM(lparam) >= window->m_lastclickx - 4 && GET_X_LPARAM(lparam) <= window->m_lastclickx + 4 &&
- GET_Y_LPARAM(lparam) >= window->m_lastclicky - 4 && GET_Y_LPARAM(lparam) <= window->m_lastclicky + 4)
- {
- window->m_lastclicktime = std::chrono::steady_clock::time_point::min();
- window->machine().ui_input().push_mouse_double_click_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- }
- else
- {
- window->m_lastclicktime = ticks;
- window->m_lastclickx = GET_X_LPARAM(lparam);
- window->m_lastclicky = GET_Y_LPARAM(lparam);
- }
- }
- break;
-
- case WM_LBUTTONUP:
- window->machine().ui_input().push_mouse_up_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
- case WM_RBUTTONDOWN:
- window->machine().ui_input().push_mouse_rdown_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
- case WM_RBUTTONUP:
- window->machine().ui_input().push_mouse_rup_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
- break;
-
+ // text input events
case WM_CHAR:
{
char16_t const ch = char16_t(wparam);
@@ -1140,14 +1166,61 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR
window->machine().ui_input().push_char_event(window->target(), char32_t(wparam));
break;
+ // legacy mouse events
+ case WM_MOUSEMOVE:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_MOUSELEAVE:
+ window->mouse_left(wparam, lparam);
+ break;
+
+ case WM_LBUTTONDOWN:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_LBUTTONUP:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ window->mouse_updated(wparam, lparam);
+ break;
+
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP:
+ window->mouse_updated(wparam, lparam);
+ return TRUE;
+
case WM_MOUSEWHEEL:
{
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(wnd, &where);
UINT ucNumLines = 3; // default
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucNumLines, 0);
- window->machine().ui_input().push_mouse_wheel_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), GET_WHEEL_DELTA_WPARAM(wparam), ucNumLines);
+ window->machine().ui_input().push_mouse_wheel_event(window->target(), where.x, where.y, GET_WHEEL_DELTA_WPARAM(wparam), ucNumLines);
}
break;
+ // new-style pointer handling (mouse/pen/touch)
+ case WM_POINTERENTER:
+ window->pointer_entered(wparam, lparam);
+ break;
+ case WM_POINTERLEAVE:
+ window->pointer_left(wparam, lparam);
+ break;
+ case WM_POINTERDOWN:
+ case WM_POINTERUP:
+ case WM_POINTERUPDATE:
+ window->pointer_updated(wparam, lparam);
+ break;
+ case WM_POINTERCAPTURECHANGED:
+ window->pointer_capture_changed(wparam, lparam);
+ break;
+
// pause the system when we start a menu or resize
case WM_ENTERSIZEMOVE:
window->m_resize_state = RESIZE_STATE_RESIZING;
@@ -1834,11 +1907,405 @@ win_window_focus win_window_info::focus() const
}
+void win_window_info::pointer_entered(WPARAM wparam, LPARAM lparam)
+{
+ assert(!IS_POINTER_CANCELED_WPARAM(wparam));
+ auto const info(map_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(platform_window(), &where);
+ info->x = where.x;
+ info->y = where.y;
+ machine().ui_input().push_pointer_update(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ where.x, where.y,
+ 0U, 0U, 0U, 0U);
+ }
+}
+
+void win_window_info::pointer_left(WPARAM wparam, LPARAM lparam)
+{
+ auto const info(find_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ bool const canceled(IS_POINTER_CANCELED_WPARAM(wparam));
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(platform_window(), &where);
+ expire_pointer(info, where, canceled);
+ }
+}
+
+void win_window_info::pointer_updated(WPARAM wparam, LPARAM lparam)
+{
+ bool const canceled(IS_POINTER_CANCELED_WPARAM(wparam));
+ auto const info(canceled ? find_pointer(wparam) : map_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ ScreenToClient(platform_window(), &where);
+ unsigned const buttons(canceled ? 0 : get_pointer_buttons(wparam));
+ update_pointer(*info, where, buttons, canceled);
+ }
+}
+
+void win_window_info::pointer_capture_changed(WPARAM wparam, LPARAM lparam)
+{
+ auto const info(find_pointer(wparam));
+ if (m_active_pointers.end() != info)
+ {
+ // treat this as the pointer being stolen - fail any gestures
+ if (BIT(info->buttons, 0))
+ {
+ assert(0 <= info->clickcnt);
+ info->clickcnt = -info->clickcnt;
+ }
+
+ // push to UI manager and dump pointer data
+ machine().ui_input().push_pointer_abort(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ info->x, info->y,
+ info->buttons, info->clickcnt);
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+ }
+}
+
+void win_window_info::mouse_left(WPARAM wparam, LPARAM lparam)
+{
+ m_tracking_mouse = false;
+ auto const info(find_mouse_pointer());
+ if (m_active_pointers.end() != info)
+ {
+ POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ expire_pointer(info, where, false);
+ }
+}
+
+void win_window_info::mouse_updated(WPARAM wparam, LPARAM lparam)
+{
+ auto const info(map_mouse_pointer());
+ if (m_active_pointers.end() == info)
+ return;
+
+ POINT const where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
+ unsigned const buttons(
+ ((MK_LBUTTON & wparam) ? 0x01 : 0x00) |
+ ((MK_RBUTTON & wparam) ? 0x02 : 0x00) |
+ ((MK_MBUTTON & wparam) ? 0x04 : 0x00) |
+ ((MK_XBUTTON1 & wparam) ? 0x08 : 0x00) |
+ ((MK_XBUTTON2 & wparam) ? 0x10 : 0x00));
+
+ // continue to track the mouse outside the window if buttons are down
+ if (buttons && !info->buttons)
+ {
+ SetCapture(platform_window());
+
+ TRACKMOUSEEVENT tm;
+ std::memset(&tm, 0, sizeof(tm));
+ tm.cbSize = sizeof(tm);
+ tm.dwFlags = TME_CANCEL | TME_LEAVE;
+ tm.hwndTrack = platform_window();
+ TrackMouseEvent(&tm);
+ m_tracking_mouse = false;
+ }
+ else
+ {
+ if (!buttons && info->buttons)
+ {
+ if (GetCapture() == platform_window())
+ ReleaseCapture();
+ }
+
+ if (!m_tracking_mouse)
+ {
+ TRACKMOUSEEVENT tm;
+ std::memset(&tm, 0, sizeof(tm));
+ tm.cbSize = sizeof(tm);
+ tm.dwFlags = TME_LEAVE;
+ tm.hwndTrack = platform_window();
+ TrackMouseEvent(&tm);
+ m_tracking_mouse = true;
+ }
+ }
+
+ update_pointer(*info, where, buttons, false);
+}
+
+void win_window_info::expire_pointer(std::vector<win_pointer_info>::iterator info, POINT const &where, bool canceled)
+{
+ // leaving implicitly releases buttons, so check hold/drag if necessary
+ if (BIT(info->buttons, 0))
+ {
+ assert(0 <= info->clickcnt);
+ if (!canceled)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const exp(std::chrono::milliseconds(GetDoubleClickTime()) + info->pressed);
+ int const dx(where.x - info->pressedx);
+ int const dy(where.y - info->pressedy);
+ int const distance((dx * dx) + (dy * dy));
+ int const tolerance((PT_TOUCH == info->type) ? TAP_DISTANCE : CLICK_DISTANCE);
+ if ((exp < now) || (tolerance < distance))
+ info->clickcnt = -info->clickcnt;
+ }
+ else
+ {
+ info->clickcnt = -info->clickcnt;
+ }
+ }
+
+ // need to remember touches to recognise multi-tap gestures
+ if (!canceled && (PT_TOUCH == info->type) && (0 < info->clickcnt))
+ {
+ auto const now(std::chrono::steady_clock::now());
+ auto const time = std::chrono::milliseconds(GetDoubleClickTime());
+ if ((time + info->pressed) >= now)
+ {
+ try
+ {
+ unsigned i(0);
+ if (m_ptrdev_info.size() > info->device)
+ i = m_ptrdev_info[info->device].clear_expired_touches(now, time);
+ else
+ m_ptrdev_info.resize(info->device + 1);
+
+ if (std::size(m_ptrdev_info[info->device].touches) > i)
+ {
+ m_ptrdev_info[info->device].touches[i].when = info->pressed;
+ m_ptrdev_info[info->device].touches[i].x = info->pressedx;
+ m_ptrdev_info[info->device].touches[i].y = info->pressedy;
+ m_ptrdev_info[info->device].touches[i].cnt = info->clickcnt;
+ }
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ }
+ }
+ }
+
+ // push to UI manager and dump pointer data
+ if (!canceled)
+ {
+ machine().ui_input().push_pointer_leave(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ where.x, where.y,
+ info->buttons, info->clickcnt);
+ }
+ else
+ {
+ machine().ui_input().push_pointer_abort(
+ target(),
+ convert_pointer_type(info->type),
+ info->index,
+ info->device,
+ where.x, where.y,
+ info->buttons, info->clickcnt);
+ }
+ m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index);
+ if (info->index < m_next_pointer)
+ m_next_pointer = info->index;
+ m_active_pointers.erase(info);
+}
+
+void win_window_info::update_pointer(win_pointer_info &info, POINT const &where, unsigned buttons, bool canceled)
+{
+ if (!canceled && (where.x == info.x) && (where.y == info.y) && (buttons == info.buttons))
+ return;
+
+ // detect multi-click actions
+ unsigned const pressed(canceled ? 0 : (buttons & ~info.buttons));
+ unsigned const released(canceled ? ~info.buttons : (~buttons & info.buttons));
+ if (BIT(pressed, 0))
+ {
+ info.primary_down(
+ where.x, where.y,
+ std::chrono::milliseconds(GetDoubleClickTime()),
+ (PT_TOUCH == info.type) ? TAP_DISTANCE : CLICK_DISTANCE,
+ PT_TOUCH == info.type,
+ m_ptrdev_info);
+ }
+ else if (BIT(info.buttons, 0))
+ {
+ info.check_primary_hold_drag(
+ where.x, where.y,
+ std::chrono::milliseconds(GetDoubleClickTime()),
+ (PT_TOUCH == info.type) ? TAP_DISTANCE : CLICK_DISTANCE);
+ }
+
+ // update info and push to UI manager
+ info.x = where.x;
+ info.y = where.y;
+ info.buttons = buttons;
+ if (!canceled)
+ {
+ machine().ui_input().push_pointer_update(
+ target(),
+ convert_pointer_type(info.type),
+ info.index,
+ info.device,
+ where.x, where.y,
+ buttons, pressed, released, info.clickcnt);
+ }
+ else
+ {
+ machine().ui_input().push_pointer_abort(
+ target(),
+ convert_pointer_type(info.type),
+ info.index,
+ info.device,
+ where.x, where.y,
+ released, info.clickcnt);
+ }
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::map_pointer(WPARAM wparam)
+{
+ WORD const ptrid(GET_POINTERID_WPARAM(wparam));
+ auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == ptrid))
+ return found;
+
+ if ((sizeof(m_next_pointer) * 8) <= m_next_pointer)
+ {
+ assert(~decltype(m_pointer_mask)(0) == m_pointer_mask);
+ osd_printf_warning("win_window_info: exceeded maximum number of active pointers\n");
+ return m_active_pointers.end();
+ }
+ assert(!BIT(m_pointer_mask, m_next_pointer));
+
+ POINTER_INFO info = { 0 };
+ if (!OSD_DYNAMIC_CALL(GetPointerInfo, ptrid, &info))
+ {
+ osd_printf_error("win_window_info: failed to get info for pointer ID %u\n", ptrid);
+ return m_active_pointers.end();
+ }
+
+ auto devpos(std::lower_bound(
+ m_ptrdev_map.begin(),
+ m_ptrdev_map.end(),
+ info.sourceDevice,
+ [] (std::pair<HANDLE, unsigned> const &mapping, HANDLE device)
+ {
+ return mapping.first < device;
+ }));
+
+ try
+ {
+ if ((m_ptrdev_map.end() == devpos) || (devpos->first != info.sourceDevice))
+ {
+ devpos = m_ptrdev_map.emplace(devpos, info.sourceDevice, m_next_ptrdev);
+ ++m_next_ptrdev;
+ }
+
+ found = m_active_pointers.emplace(
+ found,
+ win_pointer_info(ptrid, info.pointerType, m_next_pointer, devpos->second));
+ m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer;
+ do
+ {
+ ++m_next_pointer;
+ }
+ while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer));
+
+ return found;
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ return m_active_pointers.end();
+ }
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::find_pointer(WPARAM wparam)
+{
+ WORD const ptrid(GET_POINTERID_WPARAM(wparam));
+ auto const found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == ptrid))
+ return found;
+ else
+ return m_active_pointers.end();
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::map_mouse_pointer()
+{
+ WORD const ptrid(~WORD(0));
+ auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == ptrid))
+ return found;
+
+ if ((sizeof(m_next_pointer) * 8) <= m_next_pointer)
+ {
+ assert(~decltype(m_pointer_mask)(0) == m_pointer_mask);
+ osd_printf_warning("win_window_info: exceeded maximum number of active pointers\n");
+ return m_active_pointers.end();
+ }
+ assert(!BIT(m_pointer_mask, m_next_pointer));
+
+ auto devpos(std::lower_bound(
+ m_ptrdev_map.begin(),
+ m_ptrdev_map.end(),
+ INVALID_HANDLE_VALUE,
+ [] (std::pair<HANDLE, unsigned> const &mapping, HANDLE device)
+ {
+ return mapping.first < device;
+ }));
+
+ try
+ {
+ if ((m_ptrdev_map.end() == devpos) || (devpos->first != INVALID_HANDLE_VALUE))
+ {
+ devpos = m_ptrdev_map.emplace(devpos, INVALID_HANDLE_VALUE, m_next_ptrdev);
+ ++m_next_ptrdev;
+ }
+
+ found = m_active_pointers.emplace(
+ found,
+ win_pointer_info(ptrid, PT_MOUSE, m_next_pointer, devpos->second));
+ m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer;
+ do
+ {
+ ++m_next_pointer;
+ }
+ while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer));
+
+ return found;
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("win_window_info: error allocating pointer data\n");
+ return m_active_pointers.end();
+ }
+}
+
+std::vector<win_window_info::win_pointer_info>::iterator win_window_info::find_mouse_pointer()
+{
+ WORD const ptrid(~WORD(0));
+ auto const found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare));
+ if ((m_active_pointers.end() != found) && (found->ptrid == ptrid))
+ return found;
+ else
+ return m_active_pointers.end();
+}
+
+
+#if (USE_QTDEBUG)
//============================================================
// winwindow_qt_filter
//============================================================
-#if (USE_QTDEBUG)
bool winwindow_qt_filter(void *message)
{
MSG *msg = (MSG *)message;
diff --git a/src/osd/windows/window.h b/src/osd/windows/window.h
index 53021e43a78..bdfd8a79b61 100644
--- a/src/osd/windows/window.h
+++ b/src/osd/windows/window.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
//============================================================
//
// window.h - Win32 window handling
@@ -128,6 +128,21 @@ public:
int m_resize_state;
private:
+ struct win_pointer_info : public pointer_info
+ {
+ static constexpr bool compare(win_pointer_info const &info, WORD ptrid) { return info.ptrid < ptrid; }
+
+ win_pointer_info(win_pointer_info const &) = default;
+ win_pointer_info(win_pointer_info &&) = default;
+ win_pointer_info &operator=(win_pointer_info const &) = default;
+ win_pointer_info &operator=(win_pointer_info &&) = default;
+
+ win_pointer_info(WORD p, POINTER_INPUT_TYPE t, unsigned i, unsigned d);
+
+ WORD ptrid;
+ POINTER_INPUT_TYPE type;
+ };
+
void draw_video_contents(HDC dc, bool update);
int complete_create();
int wnd_extra_width();
@@ -141,9 +156,38 @@ private:
void adjust_window_position_after_major_change();
void set_fullscreen(int fullscreen);
+ // pointer handling helpers
+ void pointer_entered(WPARAM wparam, LPARAM lparam);
+ void pointer_left(WPARAM wparam, LPARAM lparam);
+ void pointer_updated(WPARAM wparam, LPARAM lparam);
+ void pointer_capture_changed(WPARAM wparam, LPARAM lparam);
+ void mouse_left(WPARAM wparam, LPARAM lparam);
+ void mouse_updated(WPARAM wparam, LPARAM lparam);
+ void expire_pointer(std::vector<win_pointer_info>::iterator info, POINT const &where, bool canceled);
+ void update_pointer(win_pointer_info &info, POINT const &where, unsigned buttons, bool canceled);
+ std::vector<win_pointer_info>::iterator map_pointer(WPARAM wparam);
+ std::vector<win_pointer_info>::iterator find_pointer(WPARAM wparam);
+ std::vector<win_pointer_info>::iterator map_mouse_pointer();
+ std::vector<win_pointer_info>::iterator find_mouse_pointer();
+
win_window_info * m_main;
bool m_attached_mode;
+ // these functions first appear in Windows 8/Server 2012
+ OSD_DYNAMIC_API(user32, "User32.dll", "User32.dll");
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerType, UINT32, POINTER_INPUT_TYPE *);
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerInfo, UINT32, POINTER_INFO *);
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerPenInfo, UINT32, POINTER_PEN_INFO *);
+ OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerTouchInfo, UINT32, POINTER_TOUCH_INFO *);
+
+ // info on currently active pointers - 64 pointers ought to be enough for anyone
+ uint64_t m_pointer_mask;
+ unsigned m_next_pointer, m_next_ptrdev;
+ bool m_tracking_mouse;
+ std::vector<std::pair<HANDLE, unsigned> > m_ptrdev_map;
+ std::vector<pointer_dev_info> m_ptrdev_info;
+ std::vector<win_pointer_info> m_active_pointers;
+
static POINT s_saved_cursor_pos;
};
diff --git a/src/osd/windows/winmain.h b/src/osd/windows/winmain.h
index bb96ec2a3e4..2ba8335c175 100644
--- a/src/osd/windows/winmain.h
+++ b/src/osd/windows/winmain.h
@@ -30,7 +30,8 @@ enum input_event
INPUT_EVENT_RAWINPUT,
INPUT_EVENT_ARRIVAL,
INPUT_EVENT_REMOVAL,
- INPUT_EVENT_MOUSE_BUTTON
+ INPUT_EVENT_MOUSE_BUTTON,
+ INPUT_EVENT_MOUSE_WHEEL
};
struct KeyPressEventArgs
@@ -40,10 +41,12 @@ struct KeyPressEventArgs
uint8_t scancode;
};
-struct MouseButtonEventArgs
+struct MouseUpdateEventArgs
{
- int button;
- int keydown;
+ unsigned pressed;
+ unsigned released;
+ int vdelta;
+ int hdelta;
int xpos;
int ypos;
};