summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/ui')
-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.cpp400
-rw-r--r--src/frontend/mame/ui/analogipt.h32
-rw-r--r--src/frontend/mame/ui/audio_effect_eq.cpp365
-rw-r--r--src/frontend/mame/ui/audio_effect_eq.h61
-rw-r--r--src/frontend/mame/ui/audio_effect_filter.cpp351
-rw-r--r--src/frontend/mame/ui/audio_effect_filter.h60
-rw-r--r--src/frontend/mame/ui/audioeffects.cpp292
-rw-r--r--src/frontend/mame/ui/audioeffects.h53
-rw-r--r--src/frontend/mame/ui/audiomix.cpp1053
-rw-r--r--src/frontend/mame/ui/audiomix.h95
-rw-r--r--src/frontend/mame/ui/auditmenu.cpp4
-rw-r--r--src/frontend/mame/ui/auditmenu.h2
-rw-r--r--src/frontend/mame/ui/cheatopt.cpp57
-rw-r--r--src/frontend/mame/ui/cheatopt.h1
-rw-r--r--src/frontend/mame/ui/confswitch.cpp74
-rw-r--r--src/frontend/mame/ui/confswitch.h5
-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.cpp19
-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.cpp49
-rw-r--r--src/frontend/mame/ui/filemngr.h9
-rw-r--r--src/frontend/mame/ui/filesel.cpp243
-rw-r--r--src/frontend/mame/ui/filesel.h65
-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/icorender.cpp118
-rw-r--r--src/frontend/mame/ui/icorender.h8
-rw-r--r--src/frontend/mame/ui/imgcntrl.cpp85
-rw-r--r--src/frontend/mame/ui/imgcntrl.h10
-rw-r--r--src/frontend/mame/ui/info.cpp115
-rw-r--r--src/frontend/mame/ui/info.h32
-rw-r--r--src/frontend/mame/ui/inputdevices.cpp20
-rw-r--r--src/frontend/mame/ui/inputmap.cpp10
-rw-r--r--src/frontend/mame/ui/inputmap.h2
-rw-r--r--src/frontend/mame/ui/mainmenu.cpp44
-rw-r--r--src/frontend/mame/ui/menu.cpp1480
-rw-r--r--src/frontend/mame/ui/menu.h191
-rw-r--r--src/frontend/mame/ui/midiinout.cpp103
-rw-r--r--src/frontend/mame/ui/midiinout.h44
-rw-r--r--src/frontend/mame/ui/miscmenu.cpp43
-rw-r--r--src/frontend/mame/ui/prscntrl.cpp4
-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.cpp194
-rw-r--r--src/frontend/mame/ui/selgame.h8
-rw-r--r--src/frontend/mame/ui/selmenu.cpp3075
-rw-r--r--src/frontend/mame/ui/selmenu.h192
-rw-r--r--src/frontend/mame/ui/selsoft.cpp144
-rw-r--r--src/frontend/mame/ui/selsoft.h5
-rw-r--r--src/frontend/mame/ui/simpleselgame.cpp218
-rw-r--r--src/frontend/mame/ui/simpleselgame.h8
-rw-r--r--src/frontend/mame/ui/sliders.cpp13
-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/sndmenu.cpp1
-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/swlist.cpp30
-rw-r--r--src/frontend/mame/ui/tapectrl.cpp138
-rw-r--r--src/frontend/mame/ui/tapectrl.h9
-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.cpp331
-rw-r--r--src/frontend/mame/ui/textbox.h30
-rw-r--r--src/frontend/mame/ui/ui.cpp766
-rw-r--r--src/frontend/mame/ui/ui.h120
-rw-r--r--src/frontend/mame/ui/utils.cpp31
-rw-r--r--src/frontend/mame/ui/utils.h30
-rw-r--r--src/frontend/mame/ui/videoopt.cpp100
-rw-r--r--src/frontend/mame/ui/viewgfx.cpp125
-rw-r--r--src/frontend/mame/ui/widgets.cpp6
82 files changed, 8864 insertions, 2792 deletions
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..05026b318f6 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 origx1, float origy1, float origx2, float origy2) 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..b126d22cfec 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 origx1, float origy1, float origx2, float origy2)
{
// 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, origy1 - top, origy1 - 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 = origy1 - top + line_height() + (tb_border() * 3.0F);
+ firstliney = origy1 - top + line_height() + (tb_border() * 4.0F);
+ m_visible_fields = std::min<int>(m_field_data.size(), int((origy2 + 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 = origy2 + tb_border();
+ m_box_bottom = origy2 + bottom;
+ firstliney = origy2 + (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,32 +434,60 @@ 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)
{
item_data &data(*reinterpret_cast<item_data *>(ev->itemref));
int newval(data.cur);
+ bool const ctrl_pressed = machine().input().code_pressed(KEYCODE_LCONTROL) || machine().input().code_pressed(KEYCODE_RCONTROL);
switch (ev->iptkey)
{
@@ -296,12 +504,12 @@ bool menu_analog::handle(event const *ev)
// left decrements
case IPT_UI_LEFT:
- newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ newval -= ctrl_pressed ? 10 : 1;
break;
// right increments
case IPT_UI_RIGHT:
- newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ newval += ctrl_pressed ? 10 : 1;
break;
// move to first item for previous device
@@ -384,15 +592,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 +668,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 +720,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..8caba0e8f7d 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 origx1, float origy1, float origx2, float origy2) 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/audio_effect_eq.cpp b/src/frontend/mame/ui/audio_effect_eq.cpp
new file mode 100644
index 00000000000..d758480ad00
--- /dev/null
+++ b/src/frontend/mame/ui/audio_effect_eq.cpp
@@ -0,0 +1,365 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*********************************************************************
+
+ ui/audio_effect_eq.cpp
+
+ Equalizer configuration
+
+*********************************************************************/
+
+#include "emu.h"
+#include "ui/audio_effect_eq.h"
+#include "audio_effects/aeffect.h"
+#include "audio_effects/eq.h"
+
+#include "ui/ui.h"
+
+namespace ui {
+
+const u32 menu_audio_effect_eq::freqs[3][43] = {
+ { 0, 32, 36, 40, 45, 50, 56, 63, 70, 80, 90, 100, 110, 125, 140, 160, 180, 200, 225, 250, 280, 315, 355, 400, 450, 500, 560, 630, 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800, 2000 },
+ { 0, 100, 110, 125, 140, 160, 180, 200, 225, 250, 280, 315, 355, 400, 450, 500, 560, 630, 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800, 2000, 2200, 2500, 2800, 3200, 3600, 4000, 4500, 5000, 5600, 6300, 7000, 8000, 9000, 10000 },
+ { 0, 500, 560, 630, 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800, 2000, 2200, 2500, 2800, 3200, 3600, 4000, 4500, 5000, 5600, 6300, 7000, 8000, 9000, 10000, 11000, 12000, 14000, 16000 },
+};
+
+menu_audio_effect_eq::menu_audio_effect_eq(mame_ui_manager &mui, render_container &container, u16 chain, u16 entry, audio_effect *effect)
+ : menu(mui, container)
+{
+ m_chain = chain;
+ m_entry = entry;
+ m_effect = static_cast<audio_effect_eq *>(effect);
+ set_heading(util::string_format("%s #%u", chain == 0xffff ? _("Default") : machine().sound().effect_chain_tag(chain), entry+1));
+ set_process_flags(PROCESS_LR_REPEAT | PROCESS_LR_ALWAYS);
+}
+
+menu_audio_effect_eq::~menu_audio_effect_eq()
+{
+}
+
+std::pair<u32, u32> menu_audio_effect_eq::find_f(u32 band) const
+{
+ u32 variant = band == 0 ? 0 : band < 4 ? 1 : 2;
+ u32 bi = 0;
+ s32 dt = 40000;
+ s32 f = s32(m_effect->f(band) + 0.5);
+ for(u32 index = 1; freqs[variant][index]; index++) {
+ s32 d1 = f - freqs[variant][index];
+ if(d1 < 0)
+ d1 = -d1;
+ if(d1 < dt) {
+ dt = d1;
+ bi = index;
+ }
+ }
+ return std::make_pair(variant, bi);
+}
+
+void menu_audio_effect_eq::change_f(u32 band, s32 direction)
+{
+ auto [variant, bi] = find_f(band);
+ bi += direction;
+ if(!freqs[variant][bi])
+ bi -= direction;
+ m_effect->set_f(band, freqs[variant][bi]);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+}
+
+bool menu_audio_effect_eq::handle(event const *ev)
+{
+ if(!ev)
+ return false;
+
+ u32 band = (uintptr_t(ev->itemref)) >> 16;
+ u32 entry = (uintptr_t(ev->itemref)) & 0xffff;
+
+ switch(ev->iptkey) {
+ case IPT_UI_LEFT: {
+ switch(entry) {
+ case MODE:
+ m_effect->set_mode(0);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case SHELF:
+ if(band == 0)
+ m_effect->set_low_shelf(true);
+ else
+ m_effect->set_high_shelf(true);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F:
+ change_f(band, -1);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q: {
+ float q = m_effect->q(band);
+ q = (int(q*10 + 0.5) - 1) / 10.0;
+ if(q < 0.1)
+ q = 0.1;
+ m_effect->set_q(band, q);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+
+ case DB: {
+ float db = m_effect->db(band);
+ db -= 1;
+ if(db < -12)
+ db = -12;
+ m_effect->set_db(band, db);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ }
+ break;
+ }
+
+ case IPT_UI_RIGHT: {
+ switch(entry) {
+ case MODE:
+ m_effect->set_mode(1);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case SHELF:
+ if(band == 0)
+ m_effect->set_low_shelf(false);
+ else
+ m_effect->set_high_shelf(false);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F:
+ change_f(band, +1);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q: {
+ float q = m_effect->q(band);
+ q = (int(q*10 + 0.5) + 1) / 10.0;
+ if(q > 12)
+ q = 12;
+ m_effect->set_q(band, q);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+
+ case DB: {
+ float db = m_effect->db(band);
+ db += 1;
+ if(db > 12)
+ db = 12;
+ m_effect->set_db(band, db);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ }
+ break;
+ }
+ case IPT_UI_CLEAR: {
+ switch(entry) {
+ case MODE:
+ m_effect->reset_mode();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case SHELF:
+ if(band == 0)
+ m_effect->reset_low_shelf();
+ else
+ m_effect->reset_high_shelf();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F:
+ m_effect->reset_f(band);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q: {
+ m_effect->reset_q(band);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+
+ case DB: {
+ m_effect->reset_db(band);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+std::string menu_audio_effect_eq::format_f(float f)
+{
+ return f >= 1000 ? util::string_format("%.1fkHz", f/1000) : util::string_format("%.0fHz", f);
+}
+
+std::string menu_audio_effect_eq::format_q(float q)
+{
+ return util::string_format("%.1f", q);
+}
+
+std::string menu_audio_effect_eq::format_db(float db)
+{
+ return util::string_format("%+.0fdB", db);
+}
+
+u32 menu_audio_effect_eq::flag_mode() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_mode())
+ flag |= FLAG_INVERT;
+ if(m_effect->mode() == 1)
+ flag |= FLAG_LEFT_ARROW;
+ if(m_effect->mode() == 0)
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_eq::flag_low_shelf() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_low_shelf())
+ flag |= FLAG_INVERT;
+ if(m_effect->low_shelf())
+ flag |= FLAG_RIGHT_ARROW;
+ else
+ flag |= FLAG_LEFT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_eq::flag_high_shelf() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_high_shelf())
+ flag |= FLAG_INVERT;
+ if(m_effect->high_shelf())
+ flag |= FLAG_RIGHT_ARROW;
+ else
+ flag |= FLAG_LEFT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_eq::flag_f(u32 band) const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_f(band))
+ flag |= FLAG_INVERT;
+ auto [variant, bi] = find_f(band);
+ if(freqs[variant][bi-1])
+ flag |= FLAG_LEFT_ARROW;
+ if(freqs[variant][bi+1])
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_eq::flag_q(u32 band) const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_q(band))
+ flag |= FLAG_INVERT;
+ float q = m_effect->q(band);
+ if(q < 10)
+ flag |= FLAG_LEFT_ARROW;
+ if(q > 0.1)
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_eq::flag_db(u32 band) const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_db(band))
+ flag |= FLAG_INVERT;
+ float db = m_effect->db(band);
+ if(db < 12)
+ flag |= FLAG_LEFT_ARROW;
+ if(db > -12)
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+void menu_audio_effect_eq::populate()
+{
+ item_append(_(audio_effect::effect_names[audio_effect::EQ]), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ item_append(_("Mode"), m_effect->mode() ? _("5-Band EQ") : _("Bypass"), flag_mode(), (void *)MODE);
+ item_append(_("Low band mode"), m_effect->low_shelf() ? _("Shelf") : _("Peak"), flag_low_shelf(), (void *)uintptr_t(SHELF | (0 << 16)));
+ item_append(_("Low band freq."), format_f(m_effect->f(0)), flag_f(0), (void *)uintptr_t(F | (0 << 16)));
+ if(!m_effect->low_shelf())
+ item_append(_("Low band Q"), format_q(m_effect->q(0)), flag_q(0), (void *)uintptr_t(Q | (0 << 16)));
+ item_append(_("Low band dB"), format_db(m_effect->db(0)), flag_db(0), (void *)uintptr_t(DB | (0 << 16)));
+
+ item_append(_("Lo mid band freq."), format_f(m_effect->f(1)), flag_f(1), (void *)uintptr_t(F | (1 << 16)));
+ item_append(_("Lo mid band Q"), format_q(m_effect->q(1)), flag_q(1), (void *)uintptr_t(Q | (1 << 16)));
+ item_append(_("Lo mid band dB"), format_db(m_effect->db(1)), flag_db(1), (void *)uintptr_t(DB | (1 << 16)));
+
+ item_append(_("Mid band freq."), format_f(m_effect->f(2)), flag_f(2), (void *)uintptr_t(F | (2 << 16)));
+ item_append(_("Mid band Q"), format_q(m_effect->q(2)), flag_q(2), (void *)uintptr_t(Q | (2 << 16)));
+ item_append(_("Mid band dB"), format_db(m_effect->db(2)), flag_db(2), (void *)uintptr_t(DB | (2 << 16)));
+
+ item_append(_("Hi mid band freq."), format_f(m_effect->f(3)), flag_f(3), (void *)uintptr_t(F | (3 << 16)));
+ item_append(_("Hi mid band Q"), format_q(m_effect->q(3)), flag_q(3), (void *)uintptr_t(Q | (3 << 16)));
+ item_append(_("Hi mid band dB"), format_db(m_effect->db(3)), flag_db(3), (void *)uintptr_t(DB | (3 << 16)));
+
+
+ item_append(_("High band mode"), m_effect->high_shelf() ? _("Shelf") : _("Peak"), flag_high_shelf(), (void *)uintptr_t(SHELF | (4 << 16)));
+ item_append(_("High band freq."), format_f(m_effect->f(4)), flag_f(4), (void *)uintptr_t(F | (4 << 16)));
+ if(!m_effect->high_shelf())
+ item_append(_("High band Q"), format_q(m_effect->q(4)), flag_q(4), (void *)uintptr_t(Q | (4 << 16)));
+ item_append(_("High band dB"), format_db(m_effect->db(4)), flag_db(4), (void *)uintptr_t(DB | (4 << 16)));
+ item_append(menu_item_type::SEPARATOR);
+}
+
+void menu_audio_effect_eq::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+}
+
+void menu_audio_effect_eq::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+}
+
+void menu_audio_effect_eq::menu_activated()
+{
+ // scripts or the other form of the menu could have changed something in the mean time
+ reset(reset_options::REMEMBER_POSITION);
+}
+
+void menu_audio_effect_eq::menu_deactivated()
+{
+}
+
+}
diff --git a/src/frontend/mame/ui/audio_effect_eq.h b/src/frontend/mame/ui/audio_effect_eq.h
new file mode 100644
index 00000000000..c11d4f3996c
--- /dev/null
+++ b/src/frontend/mame/ui/audio_effect_eq.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ ui/audio_effect_eq.h
+
+ Equalizer configuration
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_AUDIO_EFFECT_EQ_H
+#define MAME_FRONTEND_UI_AUDIO_EFFECT_EQ_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+class audio_effect_eq;
+
+namespace ui {
+
+class menu_audio_effect_eq : public menu
+{
+public:
+ menu_audio_effect_eq(mame_ui_manager &mui, render_container &container, u16 chain, u16 entry, audio_effect *effect);
+ virtual ~menu_audio_effect_eq() override;
+
+protected:
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) 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;
+
+private:
+ enum { MODE = 1, SHELF, F, Q, DB };
+
+ static const u32 freqs[3][43];
+
+ u16 m_chain, m_entry;
+ audio_effect_eq *m_effect;
+
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+
+ static std::string format_f(float f);
+ static std::string format_q(float q);
+ static std::string format_db(float db);
+ u32 flag_mode() const;
+ u32 flag_low_shelf() const;
+ u32 flag_high_shelf() const;
+ u32 flag_f(u32 band) const;
+ u32 flag_q(u32 band) const;
+ u32 flag_db(u32 band) const;
+
+ std::pair<u32, u32> find_f(u32 band) const;
+ void change_f(u32 band, s32 direction);
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_AUDIO_EFFECT_EQ_H
diff --git a/src/frontend/mame/ui/audio_effect_filter.cpp b/src/frontend/mame/ui/audio_effect_filter.cpp
new file mode 100644
index 00000000000..9b6a90df980
--- /dev/null
+++ b/src/frontend/mame/ui/audio_effect_filter.cpp
@@ -0,0 +1,351 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*********************************************************************
+
+ ui/audio_effect_filter.cpp
+
+ Filter configuration
+
+*********************************************************************/
+
+#include "emu.h"
+#include "ui/audio_effect_filter.h"
+#include "audio_effects/aeffect.h"
+#include "audio_effects/filter.h"
+
+#include "ui/ui.h"
+
+namespace ui {
+const u32 menu_audio_effect_filter::freqs[2][38] = {
+ { 0, 20, 22, 24, 26, 28, 30, 32, 36, 40, 45, 50, 56, 63, 70, 80, 90, 100, 110, 125, 140, 160, 180, 200, 225, 250, 280, 315, 355, 400, 450, 500, 560, 630, 700, 800, 900, 1000 },
+ { 0, 1000, 1100, 1200, 1400, 1600, 1800, 2000, 2200, 2500, 2800, 3200, 3600, 4000, 4500, 5000, 5600, 6300, 7000, 8000, 9000, 10000, 11000, 12000, 14000, 16000, 18000, 20000 },
+};
+
+menu_audio_effect_filter::menu_audio_effect_filter(mame_ui_manager &mui, render_container &container, u16 chain, u16 entry, audio_effect *effect)
+ : menu(mui, container)
+{
+ m_chain = chain;
+ m_entry = entry;
+ m_effect = static_cast<audio_effect_filter *>(effect);
+ set_heading(util::string_format("%s #%u", chain == 0xffff ? _("Default") : machine().sound().effect_chain_tag(chain), entry+1));
+ set_process_flags(PROCESS_LR_REPEAT | PROCESS_LR_ALWAYS);
+}
+
+menu_audio_effect_filter::~menu_audio_effect_filter()
+{
+}
+
+std::pair<u32, u32> menu_audio_effect_filter::find_f(bool lp) const
+{
+ u32 variant = lp ? 1 : 0;
+ u32 bi = 0;
+ s32 dt = 40000;
+ s32 f = s32((lp ? m_effect->fl() : m_effect->fh()) + 0.5);
+ for(u32 index = 1; freqs[variant][index]; index++) {
+ s32 d1 = f - freqs[variant][index];
+ if(d1 < 0)
+ d1 = -d1;
+ if(d1 < dt) {
+ dt = d1;
+ bi = index;
+ }
+ }
+ return std::make_pair(variant, bi);
+}
+
+void menu_audio_effect_filter::change_f(bool lp, s32 direction)
+{
+ auto [variant, bi] = find_f(lp);
+ bi += direction;
+ if(!freqs[variant][bi])
+ bi -= direction;
+ if(lp)
+ m_effect->set_fl(freqs[variant][bi]);
+ else
+ m_effect->set_fh(freqs[variant][bi]);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+}
+
+bool menu_audio_effect_filter::handle(event const *ev)
+{
+ if(!ev)
+ return false;
+
+ switch(ev->iptkey) {
+ case IPT_UI_LEFT: {
+ switch(uintptr_t(ev->itemref)) {
+ case ACTIVE | HP:
+ m_effect->set_highpass_active(false);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F | HP:
+ change_f(false, -1);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q | HP: {
+ float q = m_effect->qh();
+ q = (int(q*10 + 0.5) - 1) / 10.0;
+ if(q < 0.1)
+ q = 0.1;
+ m_effect->set_qh(q);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+
+ case ACTIVE | LP:
+ m_effect->set_lowpass_active(false);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F | LP:
+ change_f(true, -1);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q | LP: {
+ float q = m_effect->ql();
+ q = (int(q*10 + 0.5) - 1) / 10.0;
+ if(q < 0.1)
+ q = 0.1;
+ m_effect->set_ql(q);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ }
+ break;
+ }
+
+ case IPT_UI_RIGHT: {
+ switch(uintptr_t(ev->itemref)) {
+ case ACTIVE | HP:
+ m_effect->set_highpass_active(true);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F | HP:
+ change_f(false, +1);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q | HP: {
+ float q = m_effect->qh();
+ q = (int(q*10 + 0.5) + 1) / 10.0;
+ if(q > 10)
+ q = 10;
+ m_effect->set_qh(q);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+
+ case ACTIVE | LP:
+ m_effect->set_lowpass_active(true);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F | LP:
+ change_f(true, +1);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q | LP: {
+ float q = m_effect->ql();
+ q = (int(q*10 + 0.5) + 1) / 10.0;
+ if(q > 10)
+ q = 10;
+ m_effect->set_ql(q);
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ }
+ break;
+ }
+
+ case IPT_UI_CLEAR: {
+ switch(uintptr_t(ev->itemref)) {
+ case ACTIVE | HP:
+ m_effect->reset_highpass_active();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F | HP:
+ m_effect->reset_fh();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q | HP:
+ m_effect->reset_qh();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case ACTIVE | LP:
+ m_effect->reset_lowpass_active();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case F | LP:
+ m_effect->reset_fl();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case Q | LP:
+ m_effect->reset_ql();
+ if(m_chain == 0xffff)
+ machine().sound().default_effect_changed(m_entry);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+std::string menu_audio_effect_filter::format_f(float f)
+{
+ return f >= 1000 ? util::string_format("%.1fkHz", f/1000) : util::string_format("%.0fHz", f);
+}
+
+std::string menu_audio_effect_filter::format_q(float q)
+{
+ return util::string_format("%.1f", q);
+}
+
+u32 menu_audio_effect_filter::flag_highpass_active() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_highpass_active())
+ flag |= FLAG_INVERT;
+ if(m_effect->highpass_active())
+ flag |= FLAG_LEFT_ARROW;
+ else
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_filter::flag_fh() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_fh())
+ flag |= FLAG_INVERT;
+ auto [variant, bi] = find_f(false);
+ if(freqs[variant][bi-1])
+ flag |= FLAG_LEFT_ARROW;
+ if(freqs[variant][bi+1])
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_filter::flag_qh() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_qh())
+ flag |= FLAG_INVERT;
+ float q = m_effect->qh();
+ if(q > 0.1)
+ flag |= FLAG_LEFT_ARROW;
+ if(q < 10)
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_filter::flag_lowpass_active() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_lowpass_active())
+ flag |= FLAG_INVERT;
+ if(m_effect->lowpass_active())
+ flag |= FLAG_LEFT_ARROW;
+ else
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_filter::flag_fl() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_fl())
+ flag |= FLAG_INVERT;
+ auto [variant, bi] = find_f(true);
+ if(freqs[variant][bi-1])
+ flag |= FLAG_LEFT_ARROW;
+ if(freqs[variant][bi+1])
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effect_filter::flag_ql() const
+{
+ u32 flag = 0;
+ if(!m_effect->isset_ql())
+ flag |= FLAG_INVERT;
+ float q = m_effect->ql();
+ if(q > 0.1)
+ flag |= FLAG_LEFT_ARROW;
+ if(q < 10)
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+void menu_audio_effect_filter::populate()
+{
+ item_append(_(audio_effect::effect_names[audio_effect::FILTER]), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ item_append(_("Highpass (DC removal)"), m_effect->highpass_active() ? _("Active") : _("Bypass"), flag_highpass_active(), (void *)(ACTIVE | HP));
+ item_append(_("Highpass cutoff"), format_f(m_effect->fh()), flag_fh(), (void *)(F | HP));
+ item_append(_("Highpass Q"), format_q(m_effect->qh()), flag_qh(), (void *)(Q | HP));
+
+ item_append(_("Lowpass"), m_effect->lowpass_active() ? _("Active") : _("Bypass"), flag_lowpass_active(), (void *)(ACTIVE | LP));
+ item_append(_("Lowpass cutoff"), format_f(m_effect->fl()), flag_fl(), (void *)(F | LP));
+ item_append(_("Lowpass Q"), format_q(m_effect->ql()), flag_ql(), (void *)(Q | LP));
+
+ item_append(menu_item_type::SEPARATOR);
+}
+
+void menu_audio_effect_filter::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+}
+
+void menu_audio_effect_filter::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+}
+
+void menu_audio_effect_filter::menu_activated()
+{
+ // scripts or the other form of the menu could have changed something in the mean time
+ reset(reset_options::REMEMBER_POSITION);
+}
+
+void menu_audio_effect_filter::menu_deactivated()
+{
+}
+
+}
diff --git a/src/frontend/mame/ui/audio_effect_filter.h b/src/frontend/mame/ui/audio_effect_filter.h
new file mode 100644
index 00000000000..7a4d84e4122
--- /dev/null
+++ b/src/frontend/mame/ui/audio_effect_filter.h
@@ -0,0 +1,60 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ ui/audio_effect_filter.h
+
+ Filter configuration
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_AUDIO_EFFECT_FILTER_H
+#define MAME_FRONTEND_UI_AUDIO_EFFECT_FILTER_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+class audio_effect_filter;
+
+namespace ui {
+
+class menu_audio_effect_filter : public menu
+{
+public:
+ menu_audio_effect_filter(mame_ui_manager &mui, render_container &container, u16 chain, u16 entry, audio_effect *effect);
+ virtual ~menu_audio_effect_filter() override;
+
+protected:
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) 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;
+
+private:
+ enum { ACTIVE = 1, F = 2, Q = 3, HP = 0, LP = 8 };
+
+ static const u32 freqs[2][38];
+
+ u16 m_chain, m_entry;
+ audio_effect_filter *m_effect;
+
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+
+ static std::string format_f(float f);
+ static std::string format_q(float q);
+ u32 flag_highpass_active() const;
+ u32 flag_fh() const;
+ u32 flag_qh() const;
+ u32 flag_lowpass_active() const;
+ u32 flag_fl() const;
+ u32 flag_ql() const;
+
+ std::pair<u32, u32> find_f(bool lp) const;
+ void change_f(bool lp, s32 direction);
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_AUDIO_EFFECT_FILTER_H
diff --git a/src/frontend/mame/ui/audioeffects.cpp b/src/frontend/mame/ui/audioeffects.cpp
new file mode 100644
index 00000000000..eb94667f99f
--- /dev/null
+++ b/src/frontend/mame/ui/audioeffects.cpp
@@ -0,0 +1,292 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*********************************************************************
+
+ ui/audioeffects.cpp
+
+ Audio effects control
+
+*********************************************************************/
+
+#include "emu.h"
+#include "ui/audioeffects.h"
+#include "audio_effects/aeffect.h"
+
+#include "audio_effect_eq.h"
+#include "audio_effect_filter.h"
+
+#include "ui/ui.h"
+
+#include "osdepend.h"
+#include "speaker.h"
+
+namespace ui {
+
+menu_audio_effects::menu_audio_effects(mame_ui_manager &mui, render_container &container)
+ : menu(mui, container)
+{
+ set_heading(_("Audio Effects"));
+}
+
+menu_audio_effects::~menu_audio_effects()
+{
+}
+
+double menu_audio_effects::change_f(const double *table, double value, int change)
+{
+ u32 bi = 0;
+ double dt = 1e300;
+ u32 index;
+ for(index = 0; table[index]; index++) {
+ double d1 = value - table[index];
+ if(d1 < 0)
+ d1 = -d1;
+ if(d1 < dt) {
+ dt = d1;
+ bi = index;
+ }
+ }
+ if((change != -1 || bi != 0) && (change != 1 || bi != index-1))
+ bi += change;
+ return table[bi];
+}
+
+u32 menu_audio_effects::change_u32(const u32 *table, u32 value, int change)
+{
+ u32 bi = 0;
+ s32 dt = 2e9;
+ u32 index;
+ for(index = 0; table[index]; index++) {
+ s32 d1 = value - table[index];
+ if(d1 < 0)
+ d1 = -d1;
+ if(d1 < dt) {
+ dt = d1;
+ bi = index;
+ }
+ }
+ if((change != -1 || bi != 0) && (change != 1 || bi != index-1))
+ bi += change;
+ return table[bi];
+}
+
+bool menu_audio_effects::handle(event const *ev)
+{
+ static const double latencies[] = {
+ 0.0005, 0.0010, 0.0025, 0.0050, 0.0100, 0.0250, 0.0500, 0
+ };
+
+ static const u32 lengths[] = {
+ 10, 20, 30, 40, 50, 75, 100, 200, 300, 400, 500, 0
+ };
+
+ static const u32 phases[] = {
+ 10, 20, 30, 40, 50, 75, 100, 200, 300, 400, 500, 1000, 0
+ };
+
+
+ if(!ev)
+ return false;
+
+ switch(ev->iptkey) {
+ case IPT_UI_SELECT: {
+ u16 chain = (uintptr_t(ev->itemref)) >> 16;
+ u16 entry = (uintptr_t(ev->itemref)) & 0xffff;
+ audio_effect *eff = chain == 0xffff ? machine().sound().default_effect_chain()[entry] : machine().sound().effect_chain(chain)[entry];
+ switch(eff->type()) {
+ case audio_effect::FILTER:
+ menu::stack_push<menu_audio_effect_filter>(ui(), container(), chain, entry, eff);
+ break;
+
+ case audio_effect::EQ:
+ menu::stack_push<menu_audio_effect_eq>(ui(), container(), chain, entry, eff);
+ break;
+ }
+ return true;
+ }
+
+ case IPT_UI_CLEAR: {
+ switch(uintptr_t(ev->itemref)) {
+ case RS_TYPE:
+ machine().sound().set_resampler_type(sound_manager::RESAMPLER_LOFI);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_LATENCY:
+ machine().sound().set_resampler_hq_latency(0.005);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_LENGTH:
+ machine().sound().set_resampler_hq_length(400);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_PHASES:
+ machine().sound().set_resampler_hq_phases(200);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ break;
+ }
+
+ case IPT_UI_LEFT: {
+ switch(uintptr_t(ev->itemref)) {
+ case RS_TYPE:
+ machine().sound().set_resampler_type(sound_manager::RESAMPLER_LOFI);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_LATENCY:
+ machine().sound().set_resampler_hq_latency(change_f(latencies, machine().sound().resampler_hq_latency(), -1));
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_LENGTH:
+ machine().sound().set_resampler_hq_length(change_u32(lengths, machine().sound().resampler_hq_length(), -1));
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_PHASES:
+ machine().sound().set_resampler_hq_phases(change_u32(phases, machine().sound().resampler_hq_phases(), -1));
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ break;
+ }
+
+ case IPT_UI_RIGHT: {
+ switch(uintptr_t(ev->itemref)) {
+ case RS_TYPE:
+ machine().sound().set_resampler_type(sound_manager::RESAMPLER_HQ);
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_LATENCY:
+ machine().sound().set_resampler_hq_latency(change_f(latencies, machine().sound().resampler_hq_latency(), 1));
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_LENGTH:
+ machine().sound().set_resampler_hq_length(change_u32(lengths, machine().sound().resampler_hq_length(), 1));
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+
+ case RS_PHASES:
+ machine().sound().set_resampler_hq_phases(change_u32(phases, machine().sound().resampler_hq_phases(), 1));
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+
+std::string menu_audio_effects::format_lat(double latency)
+{
+ return util::string_format("%3.1fms", 1000*latency);
+}
+
+std::string menu_audio_effects::format_u32(u32 val)
+{
+ return util::string_format("%u", val);
+}
+
+u32 menu_audio_effects::flag_type() const
+{
+ u32 flag = 0;
+ u32 type = machine().sound().resampler_type();
+ if(type != sound_manager::RESAMPLER_LOFI)
+ flag |= FLAG_LEFT_ARROW;
+ if(type != sound_manager::RESAMPLER_HQ)
+ flag |= FLAG_RIGHT_ARROW;
+ return flag;
+}
+
+u32 menu_audio_effects::flag_lat() const
+{
+ u32 flag = 0;
+ double latency = machine().sound().resampler_hq_latency();
+ if(latency > 0.0005)
+ flag |= FLAG_LEFT_ARROW;
+ if(latency < 0.0500)
+ flag |= FLAG_RIGHT_ARROW;
+ if(machine().sound().resampler_type() != sound_manager::RESAMPLER_HQ)
+ flag |= FLAG_INVERT | FLAG_DISABLE;
+ return flag;
+}
+
+u32 menu_audio_effects::flag_length() const
+{
+ u32 flag = 0;
+ double latency = machine().sound().resampler_hq_length();
+ if(latency > 10)
+ flag |= FLAG_LEFT_ARROW;
+ if(latency < 500)
+ flag |= FLAG_RIGHT_ARROW;
+ if(machine().sound().resampler_type() != sound_manager::RESAMPLER_HQ)
+ flag |= FLAG_INVERT | FLAG_DISABLE;
+ return flag;
+}
+
+u32 menu_audio_effects::flag_phases() const
+{
+ u32 flag = 0;
+ double latency = machine().sound().resampler_hq_phases();
+ if(latency > 10)
+ flag |= FLAG_LEFT_ARROW;
+ if(latency < 1000)
+ flag |= FLAG_RIGHT_ARROW;
+ if(machine().sound().resampler_type() != sound_manager::RESAMPLER_HQ)
+ flag |= FLAG_INVERT | FLAG_DISABLE;
+ return flag;
+}
+
+void menu_audio_effects::populate()
+{
+ auto &sound = machine().sound();
+ for(s32 chain = 0; chain != sound.effect_chains(); chain++) {
+ std::string tag = sound.effect_chain_tag(chain);
+ item_append(tag, FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ auto eff = sound.effect_chain(chain);
+ for(u32 e = 0; e != eff.size(); e++)
+ item_append(_(audio_effect::effect_names[eff[e]->type()]), 0, (void *)intptr_t((chain << 16) | e));
+ }
+ item_append(_("Default"), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ auto eff = sound.default_effect_chain();
+ for(u32 e = 0; e != eff.size(); e++)
+ item_append(_(audio_effect::effect_names[eff[e]->type()]), 0, (void *)intptr_t((0xffff << 16) | e));
+
+ item_append(_("Resampler"), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ item_append(_("Type"), sound.resampler_type_names(sound.resampler_type()), flag_type(), (void *)RS_TYPE);
+ item_append(_("HQ latency"), format_lat(sound.resampler_hq_latency()), flag_lat(), (void *)RS_LATENCY);
+ item_append(_("HQ filter max size"), format_u32(sound.resampler_hq_length()), flag_length(), (void *)RS_LENGTH);
+ item_append(_("HQ filter max phases"), format_u32(sound.resampler_hq_phases()), flag_phases(), (void *)RS_PHASES);
+ item_append(menu_item_type::SEPARATOR);
+}
+
+void menu_audio_effects::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+}
+
+void menu_audio_effects::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+}
+
+void menu_audio_effects::menu_activated()
+{
+ // scripts or the other form of the menu could have changed something in the mean time
+ reset(reset_options::REMEMBER_POSITION);
+}
+
+void menu_audio_effects::menu_deactivated()
+{
+}
+
+
+} // namespace ui
+
diff --git a/src/frontend/mame/ui/audioeffects.h b/src/frontend/mame/ui/audioeffects.h
new file mode 100644
index 00000000000..f61d82fe825
--- /dev/null
+++ b/src/frontend/mame/ui/audioeffects.h
@@ -0,0 +1,53 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ ui/audioeffects.h
+
+ Audio effects control
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_AUDIOEFFECTS_H
+#define MAME_FRONTEND_UI_AUDIOEFFECTS_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+
+namespace ui {
+
+class menu_audio_effects : public menu
+{
+public:
+ menu_audio_effects(mame_ui_manager &mui, render_container &container);
+ virtual ~menu_audio_effects() override;
+
+protected:
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) 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;
+
+private:
+ enum { RS_TYPE, RS_LATENCY, RS_LENGTH, RS_PHASES };
+
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+
+ u32 flag_type() const;
+ u32 flag_lat() const;
+ u32 flag_length() const;
+ u32 flag_phases() const;
+
+ static double change_f(const double *table, double value, int change);
+ static u32 change_u32(const u32 *table, u32 value, int change);
+
+ static std::string format_lat(double latency);
+ static std::string format_u32(u32 val);
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_AUDIOEFFECTS_H
diff --git a/src/frontend/mame/ui/audiomix.cpp b/src/frontend/mame/ui/audiomix.cpp
new file mode 100644
index 00000000000..75b5ee334d5
--- /dev/null
+++ b/src/frontend/mame/ui/audiomix.cpp
@@ -0,0 +1,1053 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*********************************************************************
+
+ ui/audiomix.cpp
+
+ Audio mixing/mapping control
+
+*********************************************************************/
+
+#include "emu.h"
+#include "ui/audiomix.h"
+
+#include "ui/ui.h"
+
+#include "osdepend.h"
+#include "speaker.h"
+
+namespace ui {
+
+menu_audio_mixer::menu_audio_mixer(mame_ui_manager &mui, render_container &container)
+ : menu(mui, container)
+{
+ set_heading(_("Audio Mixer"));
+ m_generation = 0;
+ m_current_selection.m_maptype = MT_UNDEFINED;
+ m_current_selection.m_dev = nullptr;
+ m_current_selection.m_guest_channel = 0;
+ m_current_selection.m_node = 0;
+ m_current_selection.m_node_channel = 0;
+ m_current_group = GRP_NODE;
+
+ set_process_flags(PROCESS_LR_ALWAYS);
+}
+
+menu_audio_mixer::~menu_audio_mixer()
+{
+}
+
+bool menu_audio_mixer::handle(event const *ev)
+{
+ if(!ev) {
+ if(m_generation != machine().sound().get_osd_info().m_generation) {
+ reset(reset_options::REMEMBER_POSITION);
+ return true;
+ }
+ return false;
+ }
+
+ bool const alt_pressed = machine().input().code_pressed(KEYCODE_LALT) || machine().input().code_pressed(KEYCODE_RALT);
+ bool const ctrl_pressed = machine().input().code_pressed(KEYCODE_LCONTROL) || machine().input().code_pressed(KEYCODE_RCONTROL);
+ bool const shift_pressed = machine().input().code_pressed(KEYCODE_LSHIFT) || machine().input().code_pressed(KEYCODE_RSHIFT);
+
+ switch(ev->iptkey) {
+ case IPT_UI_MIXER_ADD_FULL:
+ if(m_current_selection.m_maptype == MT_INTERNAL)
+ return false;
+
+ if(full_mapping_available(m_current_selection.m_dev, 0)) {
+ m_current_selection.m_node = 0;
+ machine().sound().config_add_sound_io_connection_default(m_current_selection.m_dev, 0.0);
+
+ } else {
+ uint32_t node = find_next_available_node(m_current_selection.m_dev, 0);
+ if(node == 0xffffffff)
+ return false;
+ m_current_selection.m_node = node;
+ machine().sound().config_add_sound_io_connection_node(m_current_selection.m_dev, find_node_name(node), 0.0);
+ }
+
+ m_current_selection.m_maptype = MT_FULL;
+ m_current_selection.m_guest_channel = 0;
+ m_current_selection.m_node_channel = 0;
+ m_current_selection.m_db = 0.0;
+ m_generation --;
+ return true;
+
+ case IPT_UI_MIXER_ADD_CHANNEL: {
+ if(m_current_selection.m_maptype == MT_INTERNAL)
+ return false;
+
+ // Find a possible triplet, any triplet
+ const auto &info = machine().sound().get_osd_info();
+ u32 guest_channel;
+ u32 node_index, node_id;
+ u32 node_channel;
+ u32 default_osd_id = m_current_selection.m_dev->is_output() ? info.m_default_sink : info.m_default_source;
+ for(node_index = default_osd_id == 0 ? 0 : 0xffffffff; node_index != info.m_nodes.size(); node_index++) {
+ node_id = node_index == 0xffffffff ? 0 : info.m_nodes[node_index].m_id;
+ u32 guest_channel_count = m_current_selection.m_dev->inputs();
+ u32 node_channel_count = 0;
+ if(node_index == 0xffffffff) {
+ for(u32 i = 0; i != info.m_nodes.size(); i++)
+ if(info.m_nodes[i].m_id == default_osd_id) {
+ node_channel_count = m_current_selection.m_dev->is_output() ? info.m_nodes[i].m_sinks : info.m_nodes[i].m_sources;
+ break;
+ }
+ } else
+ node_channel_count = m_current_selection.m_dev->is_output() ? info.m_nodes[node_index].m_sinks : info.m_nodes[node_index].m_sources;
+
+ for(guest_channel = 0; guest_channel != guest_channel_count; guest_channel ++)
+ for(node_channel = 0; node_channel != node_channel_count; node_channel ++)
+ if(channel_mapping_available(m_current_selection.m_dev, guest_channel, node_id, node_channel))
+ goto found;
+ }
+ return false;
+
+ found:
+ if(node_id)
+ machine().sound().config_add_sound_io_channel_connection_node(m_current_selection.m_dev, guest_channel, info.m_nodes[node_index].name(), node_channel, 0.0);
+ else
+ machine().sound().config_add_sound_io_channel_connection_default(m_current_selection.m_dev, guest_channel, node_channel, 0.0);
+ m_current_selection.m_maptype = MT_CHANNEL;
+ m_current_selection.m_guest_channel = guest_channel;
+ m_current_selection.m_node = node_id;
+ m_current_selection.m_node_channel = node_channel;
+ m_current_selection.m_db = 0.0;
+ m_generation --;
+ return true;
+ }
+
+ case IPT_UI_CLEAR: {
+ if(m_current_selection.m_maptype == MT_NONE || m_current_selection.m_maptype == MT_INTERNAL)
+ return false;
+
+ if(m_current_selection.m_maptype == MT_FULL) {
+ if(m_current_selection.m_node == 0)
+ machine().sound().config_remove_sound_io_connection_default(m_current_selection.m_dev);
+ else
+ machine().sound().config_remove_sound_io_connection_node(m_current_selection.m_dev, find_node_name(m_current_selection.m_node));
+ } else {
+ if(m_current_selection.m_node == 0)
+ machine().sound().config_remove_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel);
+ else
+ machine().sound().config_remove_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, find_node_name(m_current_selection.m_node), m_current_selection.m_node_channel);
+ }
+
+ // Find where the selection was
+ uint32_t cursel_index = 0;
+ for(uint32_t i = 0; i != m_selections.size(); i++)
+ if(m_selections[i] == m_current_selection) {
+ cursel_index = i;
+ break;
+ }
+
+ // If the next item exists and is the same speaker, go there (visually, the cursor stays on the same line)
+ // Otherwise if the previous item exists and is the same speaker, go there (visually, the cursor goes up once)
+ // Otherwise create a MT_NONE, because one is going to appear at the same place
+
+ if(cursel_index + 1 < m_selections.size() && m_selections[cursel_index+1].m_dev == m_current_selection.m_dev)
+ m_current_selection = m_selections[cursel_index+1];
+ else if(cursel_index != 0 && m_selections[cursel_index-1].m_dev == m_current_selection.m_dev)
+ m_current_selection = m_selections[cursel_index-1];
+ else {
+ m_current_selection.m_maptype = MT_NONE;
+ m_current_selection.m_guest_channel = 0;
+ m_current_selection.m_node = 0;
+ m_current_selection.m_node_channel = 0;
+ m_current_selection.m_db = 0.0;
+ }
+
+ m_generation --;
+ return true;
+ }
+
+ case IPT_UI_UP:
+ case IPT_UI_DOWN:
+ if(!ev->itemref) {
+ m_current_selection.m_maptype = MT_INTERNAL;
+ m_generation --;
+ return true;
+ }
+
+ m_current_selection = *(select_entry *)(ev->itemref);
+ if(m_current_selection.m_maptype == MT_FULL) {
+ if(m_current_group == GRP_GUEST_CHANNEL || m_current_group == GRP_NODE_CHANNEL)
+ m_current_group = GRP_NODE;
+ }
+ m_generation --;
+ return true;
+
+ case IPT_UI_NEXT_GROUP:
+ if(m_current_selection.m_maptype == MT_NONE || m_current_selection.m_maptype == MT_INTERNAL)
+ return false;
+
+ if(m_current_selection.m_maptype == MT_FULL) {
+ if(m_current_group == GRP_NODE)
+ m_current_group = GRP_DB;
+ else
+ m_current_group = GRP_NODE;
+
+ } else if(m_current_selection.m_maptype == MT_CHANNEL) {
+ if(m_current_group == GRP_NODE)
+ m_current_group = GRP_NODE_CHANNEL;
+ else if(m_current_group == GRP_NODE_CHANNEL)
+ m_current_group = GRP_DB;
+ else if(m_current_group == GRP_DB)
+ m_current_group = GRP_GUEST_CHANNEL;
+ else
+ m_current_group = GRP_NODE;
+ }
+ m_generation --;
+ return true;
+
+ case IPT_UI_PREV_GROUP:
+ if(m_current_selection.m_maptype == MT_NONE || m_current_selection.m_maptype == MT_INTERNAL)
+ return false;
+
+ if(m_current_selection.m_maptype == MT_FULL) {
+ if(m_current_group == GRP_NODE)
+ m_current_group = GRP_DB;
+ else
+ m_current_group = GRP_NODE;
+
+ } else if(m_current_selection.m_maptype == MT_CHANNEL) {
+ if(m_current_group == GRP_NODE)
+ m_current_group = GRP_GUEST_CHANNEL;
+ else if(m_current_group == GRP_GUEST_CHANNEL)
+ m_current_group = GRP_DB;
+ else if(m_current_group == GRP_DB)
+ m_current_group = GRP_NODE_CHANNEL;
+ else
+ m_current_group = GRP_NODE;
+ }
+ m_generation --;
+ return true;
+
+ case IPT_UI_LEFT: {
+ if(m_current_selection.m_maptype == MT_NONE || m_current_selection.m_maptype == MT_INTERNAL)
+ return false;
+
+ switch(m_current_group) {
+ case GRP_NODE: {
+ if(m_current_selection.m_maptype == MT_FULL) {
+ uint32_t prev_node = m_current_selection.m_node;
+ uint32_t next_node = find_previous_available_node(m_current_selection.m_dev, prev_node);
+ if(next_node != 0xffffffff) {
+ m_current_selection.m_node = next_node;
+ if(prev_node)
+ machine().sound().config_remove_sound_io_connection_node(m_current_selection.m_dev, find_node_name(prev_node));
+ else
+ machine().sound().config_remove_sound_io_connection_default(m_current_selection.m_dev);
+ if(next_node)
+ machine().sound().config_add_sound_io_connection_node(m_current_selection.m_dev, find_node_name(next_node), m_current_selection.m_db);
+ else
+ machine().sound().config_add_sound_io_connection_default(m_current_selection.m_dev, m_current_selection.m_db);
+ m_generation --;
+ return true;
+ }
+ } else if(m_current_selection.m_maptype == MT_CHANNEL) {
+ uint32_t prev_node = m_current_selection.m_node;
+ uint32_t next_node = find_previous_available_channel_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, prev_node, m_current_selection.m_node_channel);
+ if(next_node != 0xffffffff) {
+ m_current_selection.m_node = next_node;
+ if(prev_node)
+ machine().sound().config_remove_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, find_node_name(prev_node), m_current_selection.m_node_channel);
+ else
+ machine().sound().config_remove_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel);
+ if(next_node)
+ machine().sound().config_add_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, find_node_name(next_node), m_current_selection.m_node_channel, m_current_selection.m_db);
+ else
+ machine().sound().config_add_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel, m_current_selection.m_db);
+ m_generation --;
+ return true;
+ }
+ }
+ break;
+ }
+
+ case GRP_DB: {
+ if(shift_pressed)
+ m_current_selection.m_db -= 0.1f;
+ else if(ctrl_pressed)
+ m_current_selection.m_db -= 10.0f;
+ else if(alt_pressed) {
+ if(m_current_selection.m_db > 0.0f)
+ m_current_selection.m_db = 0.0f;
+ else
+ m_current_selection.m_db = -96.0f;
+ }
+ else
+ m_current_selection.m_db -= 1.0f;
+
+ m_current_selection.m_db = floorf(m_current_selection.m_db * 10.0f) / 10.0f;
+ m_current_selection.m_db = std::clamp(m_current_selection.m_db, -96.0f, 12.0f);
+
+ if(m_current_selection.m_maptype == MT_FULL) {
+ if(m_current_selection.m_node == 0)
+ machine().sound().config_set_volume_sound_io_connection_default(m_current_selection.m_dev, m_current_selection.m_db);
+ else
+ machine().sound().config_set_volume_sound_io_connection_node(m_current_selection.m_dev, find_node_name(m_current_selection.m_node), m_current_selection.m_db);
+ } else {
+ if(m_current_selection.m_node == 0)
+ machine().sound().config_set_volume_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel, m_current_selection.m_db);
+ else
+ machine().sound().config_set_volume_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, find_node_name(m_current_selection.m_node), m_current_selection.m_node_channel, m_current_selection.m_db);
+ }
+ m_generation --;
+ return true;
+ }
+
+ case GRP_GUEST_CHANNEL: {
+ if(m_current_selection.m_maptype != MT_CHANNEL)
+ return false;
+
+ u32 guest_channel_count = m_current_selection.m_dev->inputs();
+ if(guest_channel_count == 1)
+ return false;
+ u32 guest_channel = m_current_selection.m_guest_channel;
+ for(;;) {
+ if(guest_channel == 0)
+ guest_channel = guest_channel_count - 1;
+ else
+ guest_channel --;
+ if(guest_channel == m_current_selection.m_guest_channel)
+ return false;
+ if(channel_mapping_available(m_current_selection.m_dev, guest_channel, m_current_selection.m_node, m_current_selection.m_node_channel)) {
+ if(m_current_selection.m_node) {
+ std::string node = find_node_name(m_current_selection.m_node);
+ machine().sound().config_remove_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, node, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_node(m_current_selection.m_dev, guest_channel, node, m_current_selection.m_node_channel, m_current_selection.m_db);
+ } else {
+ machine().sound().config_remove_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_default(m_current_selection.m_dev, guest_channel, m_current_selection.m_node_channel, m_current_selection.m_db);
+ }
+ m_current_selection.m_guest_channel = guest_channel;
+ m_generation --;
+ return true;
+ }
+ }
+ break;
+ }
+
+ case GRP_NODE_CHANNEL: {
+ if(m_current_selection.m_maptype != MT_CHANNEL)
+ return false;
+
+ u32 node_channel_count = find_node_channel_count(m_current_selection.m_node, m_current_selection.m_dev->is_output());
+ if(node_channel_count == 1)
+ return false;
+ u32 node_channel = m_current_selection.m_node_channel;
+ for(;;) {
+ if(node_channel == 0)
+ node_channel = node_channel_count - 1;
+ else
+ node_channel --;
+ if(node_channel == m_current_selection.m_node_channel)
+ return false;
+ if(channel_mapping_available(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node, node_channel)) {
+ if(m_current_selection.m_node) {
+ std::string node = find_node_name(m_current_selection.m_node);
+ machine().sound().config_remove_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, node, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, node, node_channel, m_current_selection.m_db);
+ } else {
+ machine().sound().config_remove_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, node_channel, m_current_selection.m_db);
+ }
+ m_current_selection.m_node_channel = node_channel;
+ m_generation --;
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case IPT_UI_RIGHT: {
+ if(m_current_selection.m_maptype == MT_NONE || m_current_selection.m_maptype == MT_INTERNAL)
+ return false;
+
+ switch(m_current_group) {
+ case GRP_NODE: {
+ if(m_current_selection.m_maptype == MT_FULL) {
+ uint32_t prev_node = m_current_selection.m_node;
+ uint32_t next_node = find_next_available_node(m_current_selection.m_dev, prev_node);
+ if(next_node != 0xffffffff) {
+ m_current_selection.m_node = next_node;
+ if(prev_node)
+ machine().sound().config_remove_sound_io_connection_node(m_current_selection.m_dev, find_node_name(prev_node));
+ else
+ machine().sound().config_remove_sound_io_connection_default(m_current_selection.m_dev);
+ if(next_node)
+ machine().sound().config_add_sound_io_connection_node(m_current_selection.m_dev, find_node_name(next_node), m_current_selection.m_db);
+ else
+ machine().sound().config_add_sound_io_connection_default(m_current_selection.m_dev, m_current_selection.m_db);
+ m_generation --;
+ return true;
+ }
+ } else if(m_current_selection.m_maptype == MT_CHANNEL) {
+ uint32_t prev_node = m_current_selection.m_node;
+ uint32_t next_node = find_next_available_channel_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, prev_node, m_current_selection.m_node_channel);
+ if(next_node != 0xffffffff) {
+ m_current_selection.m_node = next_node;
+ if(prev_node)
+ machine().sound().config_remove_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, find_node_name(prev_node), m_current_selection.m_node_channel);
+ else
+ machine().sound().config_remove_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel);
+ if(next_node)
+ machine().sound().config_add_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, find_node_name(next_node), m_current_selection.m_node_channel, m_current_selection.m_db);
+ else
+ machine().sound().config_add_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel, m_current_selection.m_db);
+ m_generation --;
+ return true;
+ }
+ }
+ break;
+ }
+
+ case GRP_DB: {
+ if(shift_pressed)
+ m_current_selection.m_db += 0.1f;
+ else if(ctrl_pressed)
+ m_current_selection.m_db += 10.0f;
+ else if(alt_pressed) {
+ if(m_current_selection.m_db < 0.0f)
+ m_current_selection.m_db = 0.0f;
+ else
+ m_current_selection.m_db = 12.0f;
+ }
+ else
+ m_current_selection.m_db += 1.0f;
+
+ m_current_selection.m_db = floorf(m_current_selection.m_db * 10.0f) / 10.0f;
+ m_current_selection.m_db = std::clamp(m_current_selection.m_db, -96.0f, 12.0f);
+
+ if(m_current_selection.m_maptype == MT_FULL) {
+ if(m_current_selection.m_node == 0)
+ machine().sound().config_set_volume_sound_io_connection_default(m_current_selection.m_dev, m_current_selection.m_db);
+ else
+ machine().sound().config_set_volume_sound_io_connection_node(m_current_selection.m_dev, find_node_name(m_current_selection.m_node), m_current_selection.m_db);
+ } else {
+ if(m_current_selection.m_node == 0)
+ machine().sound().config_set_volume_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel, m_current_selection.m_db);
+ else
+ machine().sound().config_set_volume_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, find_node_name(m_current_selection.m_node), m_current_selection.m_node_channel, m_current_selection.m_db);
+ }
+ m_generation --;
+ return true;
+ }
+
+ case GRP_GUEST_CHANNEL: {
+ if(m_current_selection.m_maptype != MT_CHANNEL)
+ return false;
+
+ u32 guest_channel_count = m_current_selection.m_dev->inputs();
+ if(guest_channel_count == 1)
+ return false;
+ u32 guest_channel = m_current_selection.m_guest_channel;
+ for(;;) {
+ guest_channel ++;
+ if(guest_channel == guest_channel_count)
+ guest_channel = 0;
+ if(guest_channel == m_current_selection.m_guest_channel)
+ return false;
+ if(channel_mapping_available(m_current_selection.m_dev, guest_channel, m_current_selection.m_node, m_current_selection.m_node_channel)) {
+ if(m_current_selection.m_node) {
+ std::string node = find_node_name(m_current_selection.m_node);
+ machine().sound().config_remove_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, node, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_node(m_current_selection.m_dev, guest_channel, node, m_current_selection.m_node_channel, m_current_selection.m_db);
+ } else {
+ machine().sound().config_remove_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_default(m_current_selection.m_dev, guest_channel, m_current_selection.m_node_channel, m_current_selection.m_db);
+ }
+ m_current_selection.m_guest_channel = guest_channel;
+ m_generation --;
+ return true;
+ }
+ }
+ break;
+ }
+
+ case GRP_NODE_CHANNEL: {
+ if(m_current_selection.m_maptype != MT_CHANNEL)
+ return false;
+
+ u32 node_channel_count = find_node_channel_count(m_current_selection.m_node, m_current_selection.m_dev->is_output());
+ if(node_channel_count == 1)
+ return false;
+ u32 node_channel = m_current_selection.m_node_channel;
+ for(;;) {
+ node_channel ++;
+ if(node_channel == node_channel_count)
+ node_channel = 0;
+ if(node_channel == m_current_selection.m_node_channel)
+ return false;
+ if(channel_mapping_available(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node, node_channel)) {
+ if(m_current_selection.m_node) {
+ std::string node = find_node_name(m_current_selection.m_node);
+ machine().sound().config_remove_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, node, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_node(m_current_selection.m_dev, m_current_selection.m_guest_channel, node, node_channel, m_current_selection.m_db);
+ } else {
+ machine().sound().config_remove_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, m_current_selection.m_node_channel);
+ machine().sound().config_add_sound_io_channel_connection_default(m_current_selection.m_dev, m_current_selection.m_guest_channel, node_channel, m_current_selection.m_db);
+ }
+ m_current_selection.m_node_channel = node_channel;
+ m_generation --;
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+
+//-------------------------------------------------
+// menu_audio_mixer_populate - populate the audio_mixer
+// menu
+//-------------------------------------------------
+
+void menu_audio_mixer::populate()
+{
+ const auto &mapping = machine().sound().get_mappings();
+ const auto &info = machine().sound().get_osd_info();
+ m_generation = info.m_generation;
+
+ auto find_node = [&info](u32 node_id) -> const osd::audio_info::node_info * {
+ for(const auto &node : info.m_nodes)
+ if(node.m_id == node_id)
+ return &node;
+ // Never happens
+ return nullptr;
+ };
+
+ // Rebuild the selections list
+ m_selections.clear();
+ for(const auto &omap : mapping) {
+ for(const auto &nmap : omap.m_node_mappings)
+ m_selections.emplace_back(select_entry { MT_FULL, omap.m_dev, 0, nmap.m_is_system_default ? 0 : nmap.m_node, 0, nmap.m_db });
+ for(const auto &cmap : omap.m_channel_mappings)
+ m_selections.emplace_back(select_entry { MT_CHANNEL, omap.m_dev, cmap.m_guest_channel, cmap.m_is_system_default ? 0 : cmap.m_node, cmap.m_node_channel, cmap.m_db });
+ if(omap.m_node_mappings.empty() && omap.m_channel_mappings.empty())
+ m_selections.emplace_back(select_entry { MT_NONE, omap.m_dev, 0, 0, 0, 0 });
+ }
+
+ // If there's nothing, get out of there
+ if(m_selections.empty())
+ return;
+
+ // Find the line of the current selection, if any.
+ // Otherwise default to the first line
+
+ u32 cursel_line = 0xffffffff;
+
+ for(u32 i = 0; i != m_selections.size(); i++) {
+ if(m_current_selection == m_selections[i]) {
+ cursel_line = i;
+ break;
+ }
+ }
+
+ if(cursel_line == 0xffffffff)
+ for(u32 i = 0; i != m_selections.size(); i++) {
+ if(m_current_selection.m_dev == m_selections[i].m_dev) {
+ cursel_line = i;
+ break;
+ }
+ }
+
+ if(cursel_line == 0xffffffff)
+ cursel_line = 0;
+
+ if(m_current_selection.m_maptype == MT_INTERNAL)
+ cursel_line = 0xffffffff;
+ else
+ m_current_selection = m_selections[cursel_line];
+
+ if(m_current_selection.m_maptype == MT_FULL) {
+ if(m_current_group == GRP_GUEST_CHANNEL || m_current_group == GRP_NODE_CHANNEL)
+ m_current_group = GRP_NODE;
+ }
+
+ // (Re)build the menu
+ u32 curline = 0;
+ for(const auto &omap : mapping) {
+ item_append(omap.m_dev->tag(), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ for(const auto &nmap : omap.m_node_mappings) {
+ const auto &node = find_node(nmap.m_node);
+ std::string lnode = nmap.m_is_system_default || node->m_name == "" ? "[default]" : node->m_name;
+ if(!omap.m_dev->is_output() && node->m_sinks)
+ lnode = util::string_format("Monitor of %s", lnode);
+ if(curline == cursel_line && m_current_group == GRP_NODE)
+ lnode = u8"\u25c4" + lnode + u8"\u25ba";
+
+ std::string line = (omap.m_dev->is_output() ? "> " : "< ") + lnode;
+
+ std::string db = util::string_format("%g dB", nmap.m_db);
+ if(curline == cursel_line && m_current_group == GRP_DB)
+ db = u8"\u25c4" + db + u8"\u25ba";
+
+ item_append(line, db, 0, m_selections.data() + curline);
+ curline ++;
+ }
+ for(const auto &cmap : omap.m_channel_mappings) {
+ const auto &node = find_node(cmap.m_node);
+ std::string guest_channel = omap.m_dev->get_position_name(cmap.m_guest_channel);
+ if(curline == cursel_line && m_current_group == GRP_GUEST_CHANNEL)
+ guest_channel = u8"\u25c4" + guest_channel + u8"\u25ba";
+
+ std::string lnode = cmap.m_is_system_default || node->m_name == "" ? "[default]" : node->m_name;
+ if(!omap.m_dev->is_output() && node->m_sinks)
+ lnode = util::string_format("Monitor of %s", lnode);
+ if(curline == cursel_line && m_current_group == GRP_NODE)
+ lnode = u8"\u25c4" + lnode + u8"\u25ba";
+
+ std::string lnode_channel = node->m_port_names[cmap.m_node_channel];
+ if(curline == cursel_line && m_current_group == GRP_NODE_CHANNEL)
+ lnode_channel = u8"\u25c4" + lnode_channel + u8"\u25ba";
+
+ std::string line = guest_channel + " > " + lnode + ":" + lnode_channel;
+
+ std::string db = util::string_format("%g dB", cmap.m_db);
+ if(curline == cursel_line && m_current_group == GRP_DB)
+ db = u8"\u25c4" + db + u8"\u25ba";
+
+ item_append(line, db, 0, m_selections.data() + curline);
+ curline ++;
+ }
+ if(omap.m_node_mappings.empty() && omap.m_channel_mappings.empty()) {
+ item_append("[no mapping]", 0, m_selections.data() + curline);
+ curline ++;
+ }
+ }
+ item_append(menu_item_type::SEPARATOR);
+ item_append(util::string_format("%s: add a full mapping", ui().get_general_input_setting(IPT_UI_MIXER_ADD_FULL)), FLAG_DISABLE, nullptr);
+ item_append(util::string_format("%s: add a channel mapping", ui().get_general_input_setting(IPT_UI_MIXER_ADD_CHANNEL)), FLAG_DISABLE, nullptr);
+ item_append(util::string_format("%s: remove a mapping", ui().get_general_input_setting(IPT_UI_CLEAR)), FLAG_DISABLE, nullptr);
+ item_append(menu_item_type::SEPARATOR);
+
+ if(cursel_line != 0xffffffff)
+ set_selection(m_selections.data() + cursel_line);
+}
+
+
+//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void menu_audio_mixer::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ // set_custom_space(0.0f, 2.0f * line_height() + 2.0f * tb_border());
+}
+
+
+//-------------------------------------------------
+// menu_audio_mixer_custom_render - perform our special
+// rendering
+//-------------------------------------------------
+
+void menu_audio_mixer::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+}
+
+
+//-------------------------------------------------
+// menu_activated - handle menu gaining focus
+//-------------------------------------------------
+
+void menu_audio_mixer::menu_activated()
+{
+ // scripts or the other form of the menu could have changed something in the mean time
+ reset(reset_options::REMEMBER_POSITION);
+}
+
+
+//-------------------------------------------------
+// menu_deactivated - handle menu losing focus
+//-------------------------------------------------
+
+void menu_audio_mixer::menu_deactivated()
+{
+}
+
+uint32_t menu_audio_mixer::find_node_index(uint32_t node) const
+{
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t i = 0; i != info.m_nodes.size(); i++)
+ if(info.m_nodes[i].m_id == node)
+ return i;
+ // Can't happen in theory
+ return 0xffffffff;
+}
+
+std::string menu_audio_mixer::find_node_name(uint32_t node) const
+{
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t i = 0; i != info.m_nodes.size(); i++)
+ if(info.m_nodes[i].m_id == node)
+ return info.m_nodes[i].name();
+ // Can't happen in theory
+ return "";
+}
+
+uint32_t menu_audio_mixer::find_node_channel_count(uint32_t node, bool is_output) const
+{
+ const auto &info = machine().sound().get_osd_info();
+ if(!node)
+ node = info.m_default_sink;
+ for(uint32_t i = 0; i != info.m_nodes.size(); i++)
+ if(info.m_nodes[i].m_id == node)
+ return is_output ? info.m_nodes[i].m_sinks : info.m_nodes[i].m_sources;
+ // Can't happen in theory
+ return 0;
+}
+
+uint32_t menu_audio_mixer::find_next_sink_node_index(uint32_t index) const
+{
+ if(index == 0xffffffff)
+ return index;
+
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t idx = index + 1; idx != info.m_nodes.size(); idx++)
+ if(info.m_nodes[idx].m_sinks)
+ return idx;
+ return 0xffffffff;
+}
+
+uint32_t menu_audio_mixer::find_next_source_node_index(uint32_t index) const
+{
+ if(index == 0xffffffff)
+ return index;
+
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t idx = index + 1; idx != info.m_nodes.size(); idx++)
+ if(info.m_nodes[idx].m_sources)
+ return idx;
+ return 0xffffffff;
+}
+
+uint32_t menu_audio_mixer::find_previous_sink_node_index(uint32_t index) const
+{
+ if(index == 0xffffffff)
+ return index;
+
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t idx = index - 1; idx != 0xffffffff; idx--)
+ if(info.m_nodes[idx].m_sinks)
+ return idx;
+ return 0xffffffff;
+}
+
+uint32_t menu_audio_mixer::find_previous_source_node_index(uint32_t index) const
+{
+ if(index == 0xffffffff)
+ return index;
+
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t idx = index - 1; idx != 0xffffffff; idx--)
+ if(info.m_nodes[idx].m_sources)
+ return idx;
+ return 0xffffffff;
+}
+
+uint32_t menu_audio_mixer::find_first_sink_node_index() const
+{
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t index = 0; index != info.m_nodes.size(); index ++)
+ if(info.m_nodes[index].m_sinks)
+ return index;
+ return 0xffffffff;
+}
+
+uint32_t menu_audio_mixer::find_first_source_node_index() const
+{
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t index = 0; index != info.m_nodes.size(); index ++)
+ if(info.m_nodes[index].m_sources)
+ return index;
+ return 0xffffffff;
+}
+
+uint32_t menu_audio_mixer::find_last_sink_node_index() const
+{
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t index = info.m_nodes.size() - 1; index != 0xffffffff; index --)
+ if(info.m_nodes[index].m_sinks)
+ return index;
+ return 0xffffffff;
+}
+
+uint32_t menu_audio_mixer::find_last_source_node_index() const
+{
+ const auto &info = machine().sound().get_osd_info();
+ for(uint32_t index = info.m_nodes.size() - 1; index != 0xffffffff; index --)
+ if(info.m_nodes[index].m_sources)
+ return index;
+ return 0xffffffff;
+}
+
+bool menu_audio_mixer::full_mapping_available(sound_io_device *dev, uint32_t node) const
+{
+ if(dev->is_output() && !node && machine().sound().get_osd_info().m_default_sink == 0)
+ return false;
+ if(!dev->is_output() && !node && machine().sound().get_osd_info().m_default_source == 0)
+ return false;
+
+ const auto &mapping = machine().sound().get_mappings();
+ for(const auto &omap : mapping)
+ if(omap.m_dev == dev) {
+ for(const auto &nmap : omap.m_node_mappings)
+ if((node != 0 && nmap.m_node == node && !nmap.m_is_system_default) || (node == 0 && nmap.m_is_system_default))
+ return false;
+ return true;
+ }
+ return true;
+}
+
+bool menu_audio_mixer::channel_mapping_available(sound_io_device *dev, uint32_t guest_channel, uint32_t node, uint32_t node_channel) const
+{
+ if(dev->is_output() && !node && machine().sound().get_osd_info().m_default_sink == 0)
+ return false;
+ if(!dev->is_output() && !node && machine().sound().get_osd_info().m_default_source == 0)
+ return false;
+
+ const auto &mapping = machine().sound().get_mappings();
+ for(const auto &omap : mapping)
+ if(omap.m_dev == dev) {
+ for(const auto &cmap : omap.m_channel_mappings)
+ if(cmap.m_guest_channel == guest_channel &&
+ ((node != 0 && cmap.m_node == node && !cmap.m_is_system_default) || (node == 0 && cmap.m_is_system_default))
+ && cmap.m_node_channel == node_channel)
+ return false;
+ return true;
+ }
+ return true;
+}
+
+uint32_t menu_audio_mixer::find_next_available_node(sound_io_device *dev, uint32_t node) const
+{
+ const auto &info = machine().sound().get_osd_info();
+
+ if(dev->is_output()) {
+ if(node == 0) {
+ uint32_t index = find_first_sink_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_next_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_next_sink_node_index(index);
+ if(index != 0xffffffff && full_mapping_available(dev, info.m_nodes[index].m_id))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(info.m_default_sink != 0 && full_mapping_available(dev, 0))
+ return 0;
+
+ index = find_first_sink_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_next_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ } else {
+ if(node == 0) {
+ uint32_t index = find_first_source_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_next_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_next_source_node_index(index);
+ if(index != 0xffffffff && full_mapping_available(dev, info.m_nodes[index].m_id))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(info.m_default_source != 0 && full_mapping_available(dev, 0))
+ return 0;
+
+ index = find_first_source_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_next_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+}
+
+uint32_t menu_audio_mixer::find_previous_available_node(sound_io_device *dev, uint32_t node) const
+{
+ const auto &info = machine().sound().get_osd_info();
+
+ if(dev->is_output()) {
+ if(node == 0) {
+ uint32_t index = find_last_sink_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_previous_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_previous_sink_node_index(index);
+ if(index != 0xffffffff && full_mapping_available(dev, info.m_nodes[index].m_id))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(info.m_default_sink != 0 && full_mapping_available(dev, 0))
+ return 0;
+
+ index = find_last_sink_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_previous_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+
+ } else {
+ if(node == 0) {
+ uint32_t index = find_last_source_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_previous_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_previous_source_node_index(index);
+ if(index != 0xffffffff && full_mapping_available(dev, info.m_nodes[index].m_id))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(info.m_default_source != 0 && full_mapping_available(dev, 0))
+ return 0;
+
+ index = find_last_source_node_index();
+ while(index != 0xffffffff && !full_mapping_available(dev, info.m_nodes[index].m_id))
+ index = find_previous_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+}
+
+uint32_t menu_audio_mixer::find_next_available_channel_node(sound_io_device *dev, uint32_t guest_channel, uint32_t node, uint32_t node_channel) const
+{
+ const auto &info = machine().sound().get_osd_info();
+
+ if(dev->is_output()) {
+ if(node == 0) {
+ uint32_t index = find_first_sink_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_next_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_next_sink_node_index(index);
+ if(index != 0xffffffff && channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(dev->is_output() && info.m_default_sink != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+ if(!dev->is_output() && info.m_default_source != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+
+ index = find_first_sink_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_next_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+
+ } else {
+ if(node == 0) {
+ uint32_t index = find_first_source_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_next_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_next_source_node_index(index);
+ if(index != 0xffffffff && channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(dev->is_output() && info.m_default_source != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+ if(!dev->is_output() && info.m_default_source != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+
+ index = find_first_source_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_next_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+}
+
+uint32_t menu_audio_mixer::find_previous_available_channel_node(sound_io_device *dev, uint32_t guest_channel, uint32_t node, uint32_t node_channel) const
+{
+ const auto &info = machine().sound().get_osd_info();
+
+ if(dev->is_output()) {
+ if(node == 0) {
+ uint32_t index = find_last_sink_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_previous_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_previous_sink_node_index(index);
+ if(index != 0xffffffff && channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(dev->is_output() && info.m_default_sink != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+ if(!dev->is_output() && info.m_default_source != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+
+ index = find_last_sink_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_previous_sink_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+
+ } else {
+ if(node == 0) {
+ uint32_t index = find_last_source_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_previous_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+
+ uint32_t index = find_node_index(node);
+ while(index != 0xffffffff) {
+ index = find_previous_source_node_index(index);
+ if(index != 0xffffffff && channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ return info.m_nodes[index].m_id;
+ }
+
+ if(dev->is_output() && info.m_default_source != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+ if(!dev->is_output() && info.m_default_source != 0 && channel_mapping_available(dev, guest_channel, 0, node_channel))
+ return 0;
+
+ index = find_last_source_node_index();
+ while(index != 0xffffffff && !channel_mapping_available(dev, guest_channel, info.m_nodes[index].m_id, node_channel))
+ index = find_previous_source_node_index(index);
+ return index == 0xffffffff ? 0xffffffff : info.m_nodes[index].m_id;
+ }
+}
+
+} // namespace ui
+
diff --git a/src/frontend/mame/ui/audiomix.h b/src/frontend/mame/ui/audiomix.h
new file mode 100644
index 00000000000..5f8468f01f0
--- /dev/null
+++ b/src/frontend/mame/ui/audiomix.h
@@ -0,0 +1,95 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ ui/audiomix.h
+
+ Audio mixing/mapping control
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_AUDIOMIX_H
+#define MAME_FRONTEND_UI_AUDIOMIX_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+
+namespace ui {
+
+class menu_audio_mixer : public menu
+{
+public:
+ menu_audio_mixer(mame_ui_manager &mui, render_container &container);
+ virtual ~menu_audio_mixer() override;
+
+protected:
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) 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;
+
+private:
+ enum {
+ MT_UNDEFINED, // At startup
+ MT_NONE, // [no mapping]
+ MT_FULL, // Full mapping to node
+ MT_CHANNEL, // Channel-to-channel mapping
+ MT_INTERNAL // Go back to previous menu or other non-mapping entry
+ };
+
+ enum {
+ GRP_GUEST_CHANNEL,
+ GRP_NODE,
+ GRP_NODE_CHANNEL,
+ GRP_DB
+ };
+
+ struct select_entry {
+ u32 m_maptype;
+ sound_io_device *m_dev;
+ u32 m_guest_channel;
+ u32 m_node;
+ u32 m_node_channel;
+ float m_db;
+
+ inline bool operator ==(const select_entry &sel) {
+ return sel.m_maptype == m_maptype && sel.m_dev == m_dev && sel.m_guest_channel == m_guest_channel && sel.m_node == m_node && sel.m_node_channel == m_node_channel;
+ }
+ };
+
+ uint32_t m_generation;
+ select_entry m_current_selection;
+ uint32_t m_current_group;
+ std::vector<select_entry> m_selections;
+
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+
+ uint32_t find_node_index(uint32_t node) const;
+ std::string find_node_name(uint32_t node) const;
+ uint32_t find_node_channel_count(uint32_t node, bool is_output) const;
+
+ uint32_t find_next_sink_node_index(uint32_t index) const;
+ uint32_t find_next_source_node_index(uint32_t index) const;
+ uint32_t find_previous_sink_node_index(uint32_t index) const;
+ uint32_t find_previous_source_node_index(uint32_t index) const;
+
+ uint32_t find_first_sink_node_index() const;
+ uint32_t find_first_source_node_index() const;
+ uint32_t find_last_sink_node_index() const;
+ uint32_t find_last_source_node_index() const;
+
+ bool full_mapping_available(sound_io_device *dev, uint32_t node) const;
+ bool channel_mapping_available(sound_io_device *dev, uint32_t guest_channel, uint32_t node, uint32_t node_channel) const;
+
+ uint32_t find_next_available_node(sound_io_device *dev, uint32_t node) const;
+ uint32_t find_previous_available_node(sound_io_device *dev, uint32_t node) const;
+ uint32_t find_next_available_channel_node(sound_io_device *dev, uint32_t guest_channel, uint32_t node, uint32_t node_channel) const;
+ uint32_t find_previous_available_channel_node(sound_io_device *dev, uint32_t guest_channel, uint32_t node, uint32_t node_channel) const;
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_AUDIOMIX_H
diff --git a/src/frontend/mame/ui/auditmenu.cpp b/src/frontend/mame/ui/auditmenu.cpp
index d225535c4e7..22d4eb9e8c2 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 origx1, float origy1, float origx2, float origy2)
{
switch (m_phase)
{
@@ -92,7 +92,7 @@ void menu_audit::custom_render(void *selectedref, float top, float bottom, float
{
draw_text_box(
&m_prompt, &m_prompt + 1,
- x, x2, y2 + tb_border(), y2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
ui().colors().text_color(), UI_GREEN_COLOR);
}
diff --git a/src/frontend/mame/ui/auditmenu.h b/src/frontend/mame/ui/auditmenu.h
index 5d71b63fd2f..7341340804a 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 origx1, float origy1, float origx2, float origy2) override;
virtual bool custom_ui_back() override;
private:
diff --git a/src/frontend/mame/ui/cheatopt.cpp b/src/frontend/mame/ui/cheatopt.cpp
index 054df4aed81..c8457a1403b 100644
--- a/src/frontend/mame/ui/cheatopt.cpp
+++ b/src/frontend/mame/ui/cheatopt.cpp
@@ -20,9 +20,10 @@
namespace ui {
// itemrefs for key menu items
-#define ITEMREF_CHEATS_RESET_ALL ((void *) 0x0001)
-#define ITEMREF_CHEATS_RELOAD_ALL ((void *) 0x0002)
-#define ITEMREF_CHEATS_FIRST_ITEM ((void *) 0x0003)
+#define ITEMREF_CHEATS_ENABLE ((void *) 0x0001)
+#define ITEMREF_CHEATS_RESET_ALL ((void *) 0x0002)
+#define ITEMREF_CHEATS_RELOAD_ALL ((void *) 0x0003)
+#define ITEMREF_CHEATS_FIRST_ITEM ((void *) 0x0004)
/*-------------------------------------------------
@@ -39,6 +40,15 @@ bool menu_cheat::handle(event const *ev)
// clear cheat comment on any movement or keypress
machine().popmessage();
+ if (ev->itemref == ITEMREF_CHEATS_ENABLE)
+ {
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
+ {
+ // handle global enable toggle
+ mame_machine_manager::instance()->cheat().set_enable(ev->iptkey == IPT_UI_RIGHT || (ev->iptkey == IPT_UI_CLEAR), false);
+ changed = true;
+ }
+ }
if ((ev->itemref == ITEMREF_CHEATS_RESET_ALL || ev->itemref == ITEMREF_CHEATS_RELOAD_ALL) && ev->iptkey == IPT_UI_SELECT)
{
// handle reset all + reset all cheats for reload all option
@@ -91,8 +101,8 @@ bool menu_cheat::handle(event const *ev)
mame_machine_manager::instance()->cheat().reload();
// display the reloaded cheats
- reset(reset_options::REMEMBER_REF);
machine().popmessage(_("All cheats reloaded"));
+ changed = true;
}
// if things changed, update
@@ -109,17 +119,25 @@ bool menu_cheat::handle(event const *ev)
menu_cheat::menu_cheat(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("Cheat Options"));
set_process_flags(PROCESS_LR_REPEAT);
}
+void menu_cheat::menu_activated()
+{
+ reset(reset_options::REMEMBER_REF);
+}
+
void menu_cheat::populate()
{
- /* iterate over cheats */
- std::string text;
- std::string subtext;
+ const bool empty = mame_machine_manager::instance()->cheat().entries().empty();
+
+ // iterate over cheats
+ if (!empty)
+ {
+ std::string text;
+ std::string subtext;
- // add cheats
- if (!mame_machine_manager::instance()->cheat().entries().empty()) {
for (auto &curcheat : mame_machine_manager::instance()->cheat().entries())
{
uint32_t flags;
@@ -129,16 +147,27 @@ void menu_cheat::populate()
else
item_append(text, subtext, flags, curcheat.get());
}
+ }
+ else
+ {
+ // indicate that none were found
+ item_append(_("[no cheats found]"), FLAG_DISABLE, nullptr);
+ }
- /* add a separator */
+ item_append(menu_item_type::SEPARATOR);
+
+ if (!empty)
+ {
+ // add global enable toggle
+ item_append_on_off(_("Enable Cheats"), mame_machine_manager::instance()->cheat().enabled(), 0, (void *)ITEMREF_CHEATS_ENABLE);
item_append(menu_item_type::SEPARATOR);
- /* add a reset all option */
+ // add a reset all option
item_append(_("Reset All"), 0, (void *)ITEMREF_CHEATS_RESET_ALL);
-
- /* add a reload all cheats option */
- item_append(_("Reload All"), 0, (void *)ITEMREF_CHEATS_RELOAD_ALL);
}
+
+ // add a reload all cheats option
+ item_append(_("Reload All"), 0, (void *)ITEMREF_CHEATS_RELOAD_ALL);
}
menu_cheat::~menu_cheat()
diff --git a/src/frontend/mame/ui/cheatopt.h b/src/frontend/mame/ui/cheatopt.h
index b704562202b..77847f80950 100644
--- a/src/frontend/mame/ui/cheatopt.h
+++ b/src/frontend/mame/ui/cheatopt.h
@@ -25,6 +25,7 @@ public:
private:
virtual void populate() override;
+ virtual void menu_activated() override;
virtual bool handle(event const *ev) override;
};
diff --git a/src/frontend/mame/ui/confswitch.cpp b/src/frontend/mame/ui/confswitch.cpp
index 946062b9a1d..cf69bb7cfbb 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>
@@ -73,6 +75,7 @@ menu_confswitch::menu_confswitch(mame_ui_manager &mui, render_container &contain
, m_switch_groups()
, m_active_switch_groups(0U)
, m_type(type)
+ , m_changed(false)
{
}
@@ -173,10 +176,18 @@ void menu_confswitch::populate()
bool menu_confswitch::handle(event const *ev)
{
- if (!ev || !ev->itemref)
- return false;
-
- if (uintptr_t(ev->itemref) == 1U)
+ bool const was_changed(std::exchange(m_changed, false));
+ bool need_update(false);
+ if (ev && (IPT_CUSTOM == ev->iptkey))
+ {
+ // clicked a switch
+ m_changed = true;
+ }
+ else if (!ev || !ev->itemref)
+ {
+ // no user input
+ }
+ else if (uintptr_t(ev->itemref) == 1U)
{
// reset
if (ev->iptkey == IPT_UI_SELECT)
@@ -186,21 +197,20 @@ bool menu_confswitch::handle(event const *ev)
{
// actual settings
ioport_field &field(*reinterpret_cast<ioport_field *>(ev->itemref));
- bool changed(false);
switch (ev->iptkey)
{
// left goes to previous setting
case IPT_UI_LEFT:
field.select_previous_setting();
- changed = true;
+ m_changed = true;
break;
// right goes to next setting
case IPT_UI_SELECT:
case IPT_UI_RIGHT:
field.select_next_setting();
- changed = true;
+ m_changed = true;
break;
// if cleared, reset to default value
@@ -212,7 +222,7 @@ bool menu_confswitch::handle(event const *ev)
{
settings.value = field.defvalue();
field.set_user_settings(settings);
- changed = true;
+ m_changed = true;
}
}
break;
@@ -233,7 +243,8 @@ bool menu_confswitch::handle(event const *ev)
{
set_selected_index(current);
set_top_line(current - 1);
- return true;
+ need_update = true;
+ break;
}
else
{
@@ -255,7 +266,7 @@ bool menu_confswitch::handle(event const *ev)
{
set_selected_index(current + 1);
set_top_line(current);
- return true;
+ need_update = true;
}
break;
}
@@ -263,14 +274,12 @@ bool menu_confswitch::handle(event const *ev)
}
break;
}
-
- // if anything changed, rebuild the menu, trying to stay on the same field
- if (changed)
- reset(reset_options::REMEMBER_REF);
}
// changing settings triggers an item rebuild because it can affect whether things are enabled
- return false;
+ if (m_changed || was_changed)
+ reset(reset_options::REMEMBER_REF);
+ return need_update;
}
@@ -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 origx1, float origy1, float origx2, float origy2)
{
// 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,24 +366,24 @@ 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);
}
}
// draw extra menu area
- float const boxwidth((std::max)(width + (lr_border() * 2.0f), x2 - x1));
+ float const boxwidth((std::max)(width + (lr_border() * 2.0f), origx2 - origx1));
float const boxleft((1.0f - boxwidth) * 0.5f);
- ui().draw_outlined_box(container(), boxleft, y2 + tb_border(), boxleft + boxwidth, y2 + bottom, ui().colors().background_color());
+ ui().draw_outlined_box(container(), boxleft, origy2 + tb_border(), boxleft + boxwidth, origy2 + bottom, ui().colors().background_color());
// 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;
@@ -396,7 +404,7 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
}
// draw the name
- float const liney(y2 + (tb_border() * 2.0f) + (line_height() * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) * line));
+ float const liney(origy2 + (tb_border() * 2.0f) + (line_height() * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) * line));
draw_text_normal(
group.name,
nameleft, liney + (line_height() * (DIP_SWITCH_HEIGHT - 1.0f) / 2.0f), namewidth,
@@ -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..3a3aba3c97e 100644
--- a/src/frontend/mame/ui/confswitch.h
+++ b/src/frontend/mame/ui/confswitch.h
@@ -72,6 +72,7 @@ private:
switch_group_vector m_switch_groups;
unsigned m_active_switch_groups;
int const m_type;
+ bool m_changed;
};
@@ -83,8 +84,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 origx1, float origy1, float origx2, float origy2) 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..0a916b82f2a 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 origx1, float origy1, float origx2, float origy2) 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 origx1, float origy1, float origx2, float origy2) 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 origx1, float origy1, float origx2, float origy2) 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..a55f659a84d 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 origx1, float origy1, float origx2, float origy2) 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..fa8bb4fadce 100644
--- a/src/frontend/mame/ui/devopt.cpp
+++ b/src/frontend/mame/ui/devopt.cpp
@@ -18,6 +18,7 @@
#include "util/unicode.h"
#include <locale>
+#include <sstream>
namespace ui {
@@ -166,7 +167,7 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
std::unordered_set<std::string> soundtags;
for (device_sound_interface &sound : snditer)
{
- if (!sound.issound() || !soundtags.insert(sound.device().tag()).second)
+ if (!soundtags.insert(sound.device().tag()).second)
continue;
// count how many identical sound chips we have
@@ -235,11 +236,13 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
int input = 0, input_mj = 0, input_hana = 0, input_gamble = 0, input_analog = 0, input_adjust = 0;
int input_keypad = 0, input_keyboard = 0, dips = 0, confs = 0;
- std::string errors;
std::ostringstream dips_opt, confs_opt;
ioport_list portlist;
- for (device_t &iptdev : device_enumerator(*dev))
- portlist.append(iptdev, errors);
+ {
+ std::ostringstream errors;
+ for (device_t &iptdev : device_enumerator(*dev))
+ portlist.append(iptdev, errors);
+ }
// check if the device adds inputs to the system
for (auto &port : portlist)
@@ -368,12 +371,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..fba8f7fcb0c 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 origx1, float origy1, float origx2, float origy2) 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 origx1, float origy1, float origx2, float origy2) 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..aeb6bcfb159 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 origx1, float origy1, float origx2, float origy2) 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..abbe2e29a8b 100644
--- a/src/frontend/mame/ui/filemngr.cpp
+++ b/src/frontend/mame/ui/filemngr.cpp
@@ -38,9 +38,9 @@ namespace ui {
// ctor
//-------------------------------------------------
-menu_file_manager::menu_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings)
+menu_file_manager::menu_file_manager(mame_ui_manager &mui, render_container &container, std::string &&warnings)
: menu(mui, container)
- , m_warnings(warnings ? warnings : "")
+ , m_warnings(std::move(warnings))
, m_selected_device(nullptr)
{
// The warning string is used when accessing from the force_file_manager call, i.e.
@@ -66,7 +66,20 @@ void menu_file_manager::recompute_metrics(uint32_t width, uint32_t height, float
{
menu::recompute_metrics(width, height, aspect);
- set_custom_space(0.0F, line_height() + 3.0F * tb_border());
+ if (!m_warnings.empty())
+ {
+ m_warnings_layout.reset();
+
+ float const max_width(1.0F - (4.0F * lr_border()));
+ m_warnings_layout.emplace(create_layout(max_width, text_layout::text_justify::LEFT));
+ m_warnings_layout->add_text(m_warnings, ui().colors().text_color());
+
+ set_custom_space(0.0F, (float(m_warnings_layout->lines() + 1) * line_height()) + (6.0F * tb_border()));
+ }
+ else
+ {
+ set_custom_space(0.0F, line_height() + (3.0F * tb_border()));
+ }
}
@@ -74,11 +87,26 @@ 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();
- extra_text_render(top, bottom, origx1, origy1, origx2, origy2, std::string_view(), path);
+ if (m_selected_device && m_selected_device->exists())
+ extra_text_render(top, (3.0F * tb_border()) + line_height(), origx1, origy1, origx2, origy2, std::string_view(), m_selected_device->filename());
+
+ // show the warnings if any
+ if (m_warnings_layout)
+ {
+ ui().draw_outlined_box(
+ container(),
+ ((1.0F + m_warnings_layout->actual_width()) * 0.5F) + lr_border(), origy2 + (4.0F * tb_border()) + line_height(),
+ ((1.0F - m_warnings_layout->actual_width()) * 0.5F) - lr_border(), origy2 + bottom,
+ ui().colors().background_color());
+ m_warnings_layout->emit(
+ container(),
+ (1.0F - m_warnings_layout->actual_width()) * 0.5F,
+ origy2 + (5.0F * tb_border()) + line_height());
+ }
+
}
@@ -111,7 +139,9 @@ void menu_file_manager::fill_image_line(device_image_interface &img, std::string
}
}
else
+ {
filename.assign("---");
+ }
}
//-------------------------------------------------
@@ -122,9 +152,6 @@ void menu_file_manager::populate()
{
m_notifiers.clear();
- if (!m_warnings.empty())
- item_append(m_warnings, FLAG_DISABLE, nullptr);
-
// cycle through all devices for this system
bool missing_mandatory = false;
std::unordered_set<std::string> devtags;
@@ -246,11 +273,11 @@ bool menu_file_manager::handle(event const *ev)
}
// force file manager menu
-void menu_file_manager::force_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings)
+void menu_file_manager::force_file_manager(mame_ui_manager &mui, render_container &container, std::string &&warnings)
{
// drop any existing menus and start the file manager
menu::stack_reset(mui);
- menu::stack_push_special_main<menu_file_manager>(mui, container, warnings);
+ menu::stack_push_special_main<menu_file_manager>(mui, container, std::move(warnings));
mui.show_menu();
// make sure MAME is paused
diff --git a/src/frontend/mame/ui/filemngr.h b/src/frontend/mame/ui/filemngr.h
index bae4546201e..2c303d2cc97 100644
--- a/src/frontend/mame/ui/filemngr.h
+++ b/src/frontend/mame/ui/filemngr.h
@@ -13,9 +13,11 @@
#pragma once
#include "ui/menu.h"
+#include "ui/text.h"
#include "notifier.h"
+#include <optional>
#include <string>
#include <vector>
@@ -25,14 +27,14 @@ namespace ui {
class menu_file_manager : public menu
{
public:
- static void force_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings);
+ static void force_file_manager(mame_ui_manager &mui, render_container &container, std::string &&warnings);
- menu_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings);
+ menu_file_manager(mame_ui_manager &mui, render_container &container, std::string &&warnings);
virtual ~menu_file_manager();
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 origx1, float origy1, float origx2, float origy2) override;
private:
virtual void populate() override;
@@ -42,6 +44,7 @@ private:
std::string const m_warnings;
std::vector<util::notifier_subscription> m_notifiers;
+ std::optional<text_layout> m_warnings_layout;
device_image_interface *m_selected_device;
};
diff --git a/src/frontend/mame/ui/filesel.cpp b/src/frontend/mame/ui/filesel.cpp
index 14d62c206b3..ac780c60d31 100644
--- a/src/frontend/mame/ui/filesel.cpp
+++ b/src/frontend/mame/ui/filesel.cpp
@@ -19,9 +19,14 @@
#include "imagedev/floppy.h"
+#include "uiinput.h"
+
#include "util/corestr.h"
#include "util/zippath.h"
+#include "bus/midi/midiinport.h"
+#include "bus/midi/midioutport.h"
+
#include <cstring>
#include <locale>
@@ -48,15 +53,27 @@ 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_is_midi(image->device().type() == MIDIIN || image->device().type() == MIDIOUT)
+ , m_clicked_directory(std::string::npos, std::string::npos)
{
(void)m_image;
set_process_flags(PROCESS_IGNOREPAUSE);
@@ -69,6 +86,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 +99,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 +110,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);
}
@@ -234,6 +323,10 @@ void menu_file_selector::append_entry_menu_item(const file_selector_entry *entry
text = _("[empty slot]");
break;
+ case SELECTOR_ENTRY_TYPE_MIDI:
+ text = _("[midi port]");
+ break;
+
case SELECTOR_ENTRY_TYPE_CREATE:
text = _("[create]");
break;
@@ -275,6 +368,12 @@ void menu_file_selector::select_item(const file_selector_entry &entry)
stack_pop();
break;
+ case SELECTOR_ENTRY_TYPE_MIDI:
+ // create
+ m_result = result::MIDI;
+ stack_pop();
+ break;
+
case SELECTOR_ENTRY_TYPE_CREATE:
// create
m_result = result::CREATE;
@@ -299,7 +398,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 +415,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 +458,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
//-------------------------------------------------
@@ -375,6 +505,10 @@ void menu_file_selector::populate()
if (m_has_empty)
append_entry(SELECTOR_ENTRY_TYPE_EMPTY, "", "");
+ // add the "[midi port]" entry if available
+ if (m_is_midi)
+ append_entry(SELECTOR_ENTRY_TYPE_MIDI, "", "");
+
// add the "[create]" entry
if (m_has_create && directory && !directory->is_archive())
append_entry(SELECTOR_ENTRY_TYPE_CREATE, "", "");
@@ -479,6 +613,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 +648,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 +667,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 +678,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..653adc77c05 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
@@ -28,31 +37,36 @@ public:
EMPTY = 0x1000,
SOFTLIST,
CREATE,
- FILE
+ FILE,
+ MIDI
};
+ 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 origx1, float origy1, float origx2, float origy2) 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
{
SELECTOR_ENTRY_TYPE_EMPTY,
+ SELECTOR_ENTRY_TYPE_MIDI,
SELECTOR_ENTRY_TYPE_CREATE,
SELECTOR_ENTRY_TYPE_SOFTWARE_LIST,
SELECTOR_ENTRY_TYPE_DRIVE,
@@ -72,16 +86,20 @@ 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;
+ bool const m_is_midi;
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 +111,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 +128,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/icorender.cpp b/src/frontend/mame/ui/icorender.cpp
index 2b431592a09..7b7a58fa923 100644
--- a/src/frontend/mame/ui/icorender.cpp
+++ b/src/frontend/mame/ui/icorender.cpp
@@ -19,6 +19,7 @@
#include "emu.h"
#include "icorender.h"
+#include "util/ioprocs.h"
#include "util/msdib.h"
#include "util/png.h"
@@ -26,6 +27,7 @@
#include <cassert>
#include <cstdint>
#include <cstring>
+#include <tuple>
// need to set LOG_OUTPUT_FUNC or LOG_OUTPUT_STREAM because there's no logerror outside devices
#define LOG_OUTPUT_FUNC osd_printf_verbose
@@ -72,7 +74,7 @@ struct icon_dir_entry_t
};
-bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
+bool load_ico_png(util::random_read &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
{
// skip out if the data isn't a reasonable size - PNG magic alone is eight bytes
if (9U >= dir.size)
@@ -118,7 +120,7 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3
}
-bool load_ico_dib(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
+bool load_ico_dib(util::random_read &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
{
fp.seek(dir.offset, SEEK_SET);
util::msdib_error const err(util::msdib_read_bitmap_data(fp, bitmap, dir.size, dir.get_height()));
@@ -154,7 +156,7 @@ bool load_ico_dib(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3
}
-bool load_ico_image(util::core_file &fp, unsigned index, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
+bool load_ico_image(util::random_read &fp, unsigned index, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap)
{
// try loading PNG image data (contains PNG file magic if used), and then fall back
if (load_ico_png(fp, dir, bitmap))
@@ -173,7 +175,7 @@ bool load_ico_image(util::core_file &fp, unsigned index, icon_dir_entry_t const
}
-bool load_ico_image(util::core_file &fp, unsigned count, unsigned index, bitmap_argb32 &bitmap)
+bool load_ico_image(util::random_read &fp, unsigned count, unsigned index, bitmap_argb32 &bitmap)
{
// read the directory entry
std::error_condition err;
@@ -181,7 +183,7 @@ bool load_ico_image(util::core_file &fp, unsigned count, unsigned index, bitmap_
icon_dir_entry_t dir;
err = fp.seek(sizeof(icon_dir_t) + (sizeof(icon_dir_entry_t) * index), SEEK_SET);
if (!err)
- err = fp.read(&dir, sizeof(dir), actual);
+ std::tie(err, actual) = read(fp, &dir, sizeof(dir));
if (err || (sizeof(dir) != actual))
{
LOG("Failed to read ICO file directory entry %u\n", index);
@@ -206,7 +208,7 @@ bool load_ico_image(util::core_file &fp, unsigned count, unsigned index, bitmap_
} // anonymous namespace
-int images_in_ico(util::core_file &fp)
+int images_in_ico(util::random_read &fp)
{
// read and check the icon file header
std::error_condition err;
@@ -214,7 +216,7 @@ int images_in_ico(util::core_file &fp)
icon_dir_t header;
err = fp.seek(0, SEEK_SET);
if (!err)
- err = fp.read(&header, sizeof(header), actual);
+ std::tie(err, actual) = read(fp, &header, sizeof(header));
if (err || (sizeof(header) != actual))
{
LOG("Failed to read ICO file header\n");
@@ -237,7 +239,7 @@ int images_in_ico(util::core_file &fp)
}
-void render_load_ico(util::core_file &fp, unsigned index, bitmap_argb32 &bitmap)
+void render_load_ico(util::random_read &fp, unsigned index, bitmap_argb32 &bitmap)
{
// check that these things haven't been padded somehow
static_assert(sizeof(icon_dir_t) == 6U, "compiler has applied padding to icon_dir_t");
@@ -261,7 +263,7 @@ void render_load_ico(util::core_file &fp, unsigned index, bitmap_argb32 &bitmap)
}
-void render_load_ico_first(util::core_file &fp, bitmap_argb32 &bitmap)
+void render_load_ico_first(util::random_read &fp, bitmap_argb32 &bitmap)
{
int const count(images_in_ico(fp));
for (int i = 0; count > i; ++i)
@@ -273,58 +275,70 @@ void render_load_ico_first(util::core_file &fp, bitmap_argb32 &bitmap)
}
-void render_load_ico_highest_detail(util::core_file &fp, bitmap_argb32 &bitmap)
+void render_load_ico_highest_detail(util::random_read &fp, bitmap_argb32 &bitmap)
{
// read and check the icon file header - logs a message on error
int const count(images_in_ico(fp));
- if (0 <= count)
+ if (0 > count)
+ {
+ bitmap.reset();
+ return;
+ }
+
+ // now load all the directory entries
+ size_t const dir_bytes(sizeof(icon_dir_entry_t) * count);
+ std::unique_ptr<icon_dir_entry_t []> dir(new (std::nothrow) icon_dir_entry_t [count]);
+ std::unique_ptr<unsigned []> index(new (std::nothrow) unsigned [count]);
+ if (count)
{
- // now load all the directory entries
- size_t const dir_bytes(sizeof(icon_dir_entry_t) * count);
- std::unique_ptr<icon_dir_entry_t []> dir(new (std::nothrow) icon_dir_entry_t [count]);
- std::unique_ptr<unsigned []> index(new (std::nothrow) unsigned [count]);
- size_t actual;
- if (count && (!dir || !index || fp.read(dir.get(), dir_bytes, actual) || (dir_bytes != actual)))
+ if (!dir || !index)
{
- LOG("Failed to read ICO file directory entries\n");
+ LOG("Failed to allocate memory for ICO file directory entries\n");
+ bitmap.reset();
+ return;
}
- else
+ auto const [err, actual] = read(fp, dir.get(), dir_bytes);
+ if (err || (dir_bytes != actual))
{
- // byteswap and sort by (pixels, depth)
- for (int i = 0; count > i; ++i)
- {
- dir[i].byteswap();
- index[i] = i;
- }
- std::stable_sort(
- index.get(),
- index.get() + count,
- [&dir] (unsigned x, unsigned y)
- {
- unsigned const x_pixels(dir[x].get_width() * dir[x].get_height());
- unsigned const y_pixels(dir[y].get_width() * dir[y].get_height());
- if (x_pixels > y_pixels)
- return true;
- else if (x_pixels < y_pixels)
- return false;
- else
- return dir[x].bpp > dir[y].bpp;
- });
-
- // walk down until something works
- for (int i = 0; count > i; ++i)
- {
- LOG(
- "Try loading ICO file entry %u: %u*%u, %u bits per pixel\n",
- index[i],
- dir[index[i]].get_width(),
- dir[index[i]].get_height(),
- dir[index[i]].bpp);
- if (load_ico_image(fp, index[i], dir[index[i]], bitmap))
- return;
- }
+ LOG("Failed to read ICO file directory entries\n");
+ bitmap.reset();
+ return;
}
}
+
+ // byteswap and sort by (pixels, depth)
+ for (int i = 0; count > i; ++i)
+ {
+ dir[i].byteswap();
+ index[i] = i;
+ }
+ std::stable_sort(
+ index.get(),
+ index.get() + count,
+ [&dir] (unsigned x, unsigned y)
+ {
+ unsigned const x_pixels(dir[x].get_width() * dir[x].get_height());
+ unsigned const y_pixels(dir[y].get_width() * dir[y].get_height());
+ if (x_pixels > y_pixels)
+ return true;
+ else if (x_pixels < y_pixels)
+ return false;
+ else
+ return dir[x].bpp > dir[y].bpp;
+ });
+
+ // walk down until something works
+ for (int i = 0; count > i; ++i)
+ {
+ LOG(
+ "Try loading ICO file entry %u: %u*%u, %u bits per pixel\n",
+ index[i],
+ dir[index[i]].get_width(),
+ dir[index[i]].get_height(),
+ dir[index[i]].bpp);
+ if (load_ico_image(fp, index[i], dir[index[i]], bitmap))
+ return;
+ }
bitmap.reset();
}
diff --git a/src/frontend/mame/ui/icorender.h b/src/frontend/mame/ui/icorender.h
index bb3f1a083be..32fa67a3424 100644
--- a/src/frontend/mame/ui/icorender.h
+++ b/src/frontend/mame/ui/icorender.h
@@ -18,16 +18,16 @@
namespace ui {
// get number of images in icon file (-1 on error)
-int images_in_ico(util::core_file &fp);
+int images_in_ico(util::random_read &fp);
// load specified icon from file (zero-based)
-void render_load_ico(util::core_file &fp, unsigned index, bitmap_argb32 &bitmap);
+void render_load_ico(util::random_read &fp, unsigned index, bitmap_argb32 &bitmap);
// load first supported icon from file
-void render_load_ico_first(util::core_file &fp, bitmap_argb32 &bitmap);
+void render_load_ico_first(util::random_read &fp, bitmap_argb32 &bitmap);
// load highest detail supported icon from file
-void render_load_ico_highest_detail(util::core_file &fp, bitmap_argb32 &bitmap);
+void render_load_ico_highest_detail(util::random_read &fp, bitmap_argb32 &bitmap);
} // namespace ui
diff --git a/src/frontend/mame/ui/imgcntrl.cpp b/src/frontend/mame/ui/imgcntrl.cpp
index 8f8aaffd3bb..01106a7ba48 100644
--- a/src/frontend/mame/ui/imgcntrl.cpp
+++ b/src/frontend/mame/ui/imgcntrl.cpp
@@ -9,14 +9,17 @@
***************************************************************************/
#include "emu.h"
-
#include "ui/imgcntrl.h"
#include "ui/filecreate.h"
#include "ui/filesel.h"
+#include "ui/midiinout.h"
#include "ui/swlist.h"
#include "ui/ui.h"
+#include "bus/midi/midiinport.h"
+#include "bus/midi/midioutport.h"
+
#include "audit.h"
#include "drivenum.h"
#include "emuopts.h"
@@ -243,7 +246,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 +254,39 @@ 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;
+
+ case menu_file_selector::result::MIDI:
+ m_state = START_MIDI;
+ break;
+
+ default: // return to system
+ stack_pop();
+ break;
+ }
+ });
break;
case START_SOFTLIST:
@@ -262,6 +295,22 @@ void menu_control_device_image::menu_activated()
m_state = SELECT_SOFTLIST;
break;
+ case START_MIDI:
+ m_midi = "";
+ menu::stack_push<menu_midi_inout>(ui(), container(), m_image.device().type() == MIDIIN, &m_midi);
+ m_state = SELECT_MIDI;
+ break;
+
+ case SELECT_MIDI:
+ if(!m_midi.empty())
+ {
+ auto [err, msg] = m_image.load(m_midi);
+ if (err)
+ machine().popmessage(_("Error connecting to midi port: %1$s"), !msg.empty() ? msg : err.message());
+ }
+ stack_pop();
+ break;
+
case START_OTHER_PART:
m_submenu_result.swparts = menu_software_parts::result::INVALID;
menu::stack_push<menu_software_parts>(ui(), container(), m_swi, m_image.image_interface(), &m_swp, true, m_submenu_result.swparts);
@@ -344,34 +393,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..c41f6a25993 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"
@@ -31,9 +30,9 @@ public:
protected:
enum
{
- START_FILE, START_OTHER_PART, START_SOFTLIST,
+ START_FILE, START_OTHER_PART, START_SOFTLIST, START_MIDI,
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, SELECT_MIDI,
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;
@@ -66,6 +63,7 @@ private:
const software_part * m_swp;
class software_list_device * m_sld;
std::string m_software_info_name;
+ std::string m_midi;
// methods
virtual void populate() override;
diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp
index 813bbd4b8c8..8db48046bc4 100644
--- a/src/frontend/mame/ui/info.cpp
+++ b/src/frontend/mame/ui/info.cpp
@@ -36,9 +36,10 @@ namespace ui {
namespace {
-constexpr machine_flags::type MACHINE_ERRORS = machine_flags::NOT_WORKING | machine_flags::MECHANICAL;
+constexpr machine_flags::type MACHINE_ERRORS = machine_flags::MECHANICAL;
constexpr machine_flags::type MACHINE_WARNINGS = machine_flags::NO_COCKTAIL | machine_flags::REQUIRES_ARTWORK;
constexpr machine_flags::type MACHINE_BTANB = machine_flags::NO_SOUND_HW | machine_flags::IS_INCOMPLETE;
+constexpr device_t::flags_type DEVICE_ERRORS = device_t::flags::NOT_WORKING;
constexpr std::pair<device_t::feature_type, char const *> FEATURE_NAMES[] = {
{ device_t::feature::PROTECTION, N_p("emulation-feature", "protection") },
@@ -63,7 +64,14 @@ constexpr std::pair<device_t::feature_type, char const *> FEATURE_NAMES[] = {
{ device_t::feature::LAN, N_p("emulation-feature", "LAN") },
{ device_t::feature::WAN, N_p("emulation-feature", "WAN") } };
-void get_general_warnings(std::ostream &buf, running_machine &machine, machine_flags::type flags, device_t::feature_type unemulated, device_t::feature_type imperfect)
+void get_general_warnings(
+ std::ostream &buf,
+ running_machine &machine,
+ machine_flags::type machflags,
+ device_t::flags_type devflags,
+ device_t::feature_type unemulated,
+ device_t::feature_type imperfect,
+ bool has_nonworking_devices)
{
// add a warning if any ROMs were loaded with warnings
bool bad_roms(false);
@@ -79,7 +87,7 @@ void get_general_warnings(std::ostream &buf, running_machine &machine, machine_f
}
// if we have at least one warning flag, print the general header
- if ((machine.rom_load().knownbad() > 0) || (flags & (MACHINE_ERRORS | MACHINE_WARNINGS | MACHINE_BTANB)) || unemulated || imperfect)
+ if ((machine.rom_load().knownbad() > 0) || (machflags & (MACHINE_ERRORS | MACHINE_WARNINGS | MACHINE_BTANB)) || (devflags & DEVICE_ERRORS) || unemulated || imperfect || has_nonworking_devices)
{
if (bad_roms)
buf << '\n';
@@ -91,8 +99,12 @@ void get_general_warnings(std::ostream &buf, running_machine &machine, machine_f
buf << _("One or more ROMs/disk images for this system have not been correctly dumped.\n");
}
-void get_device_warnings(std::ostream &buf, device_t::feature_type unemulated, device_t::feature_type imperfect)
+void get_device_warnings(std::ostream &buf, device_t::flags_type flags, device_t::feature_type unemulated, device_t::feature_type imperfect)
{
+ // add line for not working
+ if (flags & device_t::flags::NOT_WORKING)
+ buf << _("THIS DEVICE DOES NOT WORK.\n");
+
// add line for unemulated features
if (unemulated)
{
@@ -126,48 +138,74 @@ void get_device_warnings(std::ostream &buf, device_t::feature_type unemulated, d
}
}
-void get_system_warnings(std::ostream &buf, running_machine &machine, machine_flags::type flags, device_t::feature_type unemulated, device_t::feature_type imperfect)
+void get_system_warnings(
+ std::ostream &buf,
+ running_machine &machine,
+ machine_flags::type machflags,
+ device_t::flags_type devflags,
+ device_t::feature_type unemulated,
+ device_t::feature_type imperfect,
+ bool has_nonworking_devices)
{
std::streampos start_position = buf.tellp();
// start with the unemulated/imperfect features
- get_device_warnings(buf, unemulated, imperfect);
+ get_device_warnings(buf, device_t::flags::NONE, unemulated, imperfect);
// add one line per machine warning flag
- if (flags & ::machine_flags::NO_COCKTAIL)
+ if (machflags & ::machine_flags::NO_COCKTAIL)
buf << _("Screen flipping in cocktail mode is not supported.\n");
- if (flags & ::machine_flags::REQUIRES_ARTWORK)
+ if (machflags & ::machine_flags::REQUIRES_ARTWORK)
buf << _("This system requires external artwork files.\n");
// add the 'BTANB' warnings
- if (flags & ::machine_flags::IS_INCOMPLETE)
+ if (machflags & ::machine_flags::IS_INCOMPLETE)
{
if (buf.tellp() > start_position)
buf << '\n';
buf << _("This system was never completed. It may exhibit strange behavior or missing elements that are not bugs in the emulation.\n");
}
- if (flags & ::machine_flags::NO_SOUND_HW)
+ if (machflags & ::machine_flags::NO_SOUND_HW)
{
if (buf.tellp() > start_position)
buf << '\n';
buf << _("This system has no sound hardware, MAME will produce no sounds, this is expected behavior.\n");
}
+ // list devices that don't work
+ if (has_nonworking_devices)
+ {
+ if (buf.tellp() > start_position)
+ buf << '\n';
+ buf << _("The following devices do not work: ");
+ bool first = true;
+ std::set<std::add_pointer_t<device_type> > seen;
+ for (device_t &device : device_enumerator(machine.root_device()))
+ {
+ if ((&machine.root_device() != &device) && (device.type().emulation_flags() & device_t::flags::NOT_WORKING) && seen.insert(&device.type()).second)
+ {
+ util::stream_format(buf, first ? _("%s") : _(", %s"), device.type().fullname());
+ first = false;
+ }
+ }
+ buf << '\n';
+ }
+
// these are more severe warnings
- if (flags & ::machine_flags::MECHANICAL)
+ if (machflags & ::machine_flags::MECHANICAL)
{
if (buf.tellp() > start_position)
buf << '\n';
buf << _("Elements of this system cannot be emulated accurately as they require physical interaction or consist of mechanical devices. It is not possible to fully experience this system.\n");
}
- if (flags & ::machine_flags::NOT_WORKING)
+ if (devflags & device_t::flags::NOT_WORKING)
{
if (buf.tellp() > start_position)
buf << '\n';
buf << _("THIS SYSTEM DOESN'T WORK. The emulation for this system is not yet complete. There is nothing you can do to fix this problem except wait for the developers to improve the emulation.\n");
}
- if ((flags & MACHINE_ERRORS) || ((machine.system().type.unemulated_features() | machine.system().type.imperfect_features()) & device_t::feature::PROTECTION))
+ if ((machflags & MACHINE_ERRORS) || (devflags & DEVICE_ERRORS) || ((machine.system().type.unemulated_features() | machine.system().type.imperfect_features()) & device_t::feature::PROTECTION))
{
// find the parent of this driver
driver_enumerator drivlist(machine.options());
@@ -183,7 +221,7 @@ void get_system_warnings(std::ostream &buf, running_machine &machine, machine_fl
if (drivlist.current() == maindrv || drivlist.clone() == maindrv)
{
game_driver const &driver(drivlist.driver());
- if (!(driver.flags & MACHINE_ERRORS) && !((driver.type.unemulated_features() | driver.type.imperfect_features()) & device_t::feature::PROTECTION))
+ if (!(driver.flags & MACHINE_ERRORS) && !(driver.type.emulation_flags() & DEVICE_ERRORS) && !((driver.type.unemulated_features() | driver.type.imperfect_features()) & device_t::feature::PROTECTION))
{
// this one works, add a header and display the name of the clone
if (!foundworking)
@@ -220,8 +258,10 @@ machine_static_info::machine_static_info(const ui_options &options, machine_conf
machine_static_info::machine_static_info(const ui_options &options, machine_config const &config, ioport_list const *ports)
: m_options(options)
, m_flags(config.gamedrv().flags)
+ , m_emulation_flags(config.gamedrv().type.emulation_flags())
, m_unemulated_features(config.gamedrv().type.unemulated_features())
, m_imperfect_features(config.gamedrv().type.imperfect_features())
+ , m_has_nonworking_devices(false)
, m_has_bioses(false)
, m_has_dips(false)
, m_has_configs(false)
@@ -230,7 +270,7 @@ machine_static_info::machine_static_info(const ui_options &options, machine_conf
, m_has_analog(false)
{
ioport_list local_ports;
- std::string sink;
+ std::ostringstream sink;
for (device_t &device : device_enumerator(config.root_device()))
{
// the "no sound hardware" warning doesn't make sense when you plug in a sound card
@@ -238,8 +278,11 @@ machine_static_info::machine_static_info(const ui_options &options, machine_conf
m_flags &= ~::machine_flags::NO_SOUND_HW;
// build overall emulation status
+ m_emulation_flags |= device.type().emulation_flags() & ~device_t::flags::NOT_WORKING;
m_unemulated_features |= device.type().unemulated_features();
m_imperfect_features |= device.type().imperfect_features();
+ if (&config.root_device() != &device)
+ m_has_nonworking_devices = m_has_nonworking_devices || (device.type().emulation_flags() & device_t::flags::NOT_WORKING);
// look for BIOS options
device_t const *const parent(device.owner());
@@ -297,9 +340,14 @@ machine_static_info::machine_static_info(const ui_options &options, machine_conf
// issues that warrant a yellow/red message
//-------------------------------------------------
-bool machine_static_info::has_warnings() const
+bool machine_static_info::has_warnings() const noexcept
{
- return (machine_flags() & (MACHINE_ERRORS | MACHINE_WARNINGS)) || unemulated_features() || imperfect_features();
+ return
+ (machine_flags() & (MACHINE_ERRORS | MACHINE_WARNINGS)) ||
+ (emulation_flags() & DEVICE_ERRORS) ||
+ unemulated_features() ||
+ imperfect_features() ||
+ has_nonworking_devices();
}
@@ -308,10 +356,11 @@ bool machine_static_info::has_warnings() const
// system has issues that warrant a red message
//-------------------------------------------------
-bool machine_static_info::has_severe_warnings() const
+bool machine_static_info::has_severe_warnings() const noexcept
{
return
(machine_flags() & MACHINE_ERRORS) ||
+ (emulation_flags() & DEVICE_ERRORS) ||
(unemulated_features() & (device_t::feature::PROTECTION | device_t::feature::GRAPHICS | device_t::feature::SOUND)) ||
(imperfect_features() & device_t::feature::PROTECTION);
}
@@ -322,7 +371,7 @@ bool machine_static_info::has_severe_warnings() const
// driver status box
//-------------------------------------------------
-rgb_t machine_static_info::status_color() const
+rgb_t machine_static_info::status_color() const noexcept
{
if (has_severe_warnings())
return UI_RED_COLOR;
@@ -338,7 +387,7 @@ rgb_t machine_static_info::status_color() const
// warning message based on severity
//-------------------------------------------------
-rgb_t machine_static_info::warnings_color() const
+rgb_t machine_static_info::warnings_color() const noexcept
{
if (has_severe_warnings())
return UI_RED_COLOR;
@@ -373,8 +422,8 @@ machine_info::machine_info(running_machine &machine)
std::string machine_info::warnings_string() const
{
std::ostringstream buf;
- get_general_warnings(buf, m_machine, machine_flags(), unemulated_features(), imperfect_features());
- get_system_warnings(buf, m_machine, machine_flags(), unemulated_features(), imperfect_features());
+ get_general_warnings(buf, m_machine, machine_flags(), emulation_flags(), unemulated_features(), imperfect_features(), has_nonworking_devices());
+ get_system_warnings(buf, m_machine, machine_flags(), emulation_flags(), unemulated_features(), imperfect_features(), has_nonworking_devices());
return buf.str();
}
@@ -446,7 +495,7 @@ std::string machine_info::game_info_string() const
bool found_sound = false;
for (device_sound_interface &sound : snditer)
{
- if (!sound.issound() || !soundtags.insert(sound.device().tag()).second)
+ if (!soundtags.insert(sound.device().tag()).second)
continue;
// append the Sound: string
@@ -576,11 +625,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
@@ -605,26 +649,26 @@ void menu_warn_info::populate_text(std::optional<text_layout> &layout, float &wi
machine_info const &info(ui().machine_info());
device_t &root(machine().root_device());
- get_general_warnings(buf, machine(), info.machine_flags(), info.unemulated_features(), info.imperfect_features());
- if ((info.machine_flags() & (MACHINE_ERRORS | MACHINE_WARNINGS | MACHINE_BTANB)) || root.type().unemulated_features() || root.type().imperfect_features())
+ get_general_warnings(buf, machine(), info.machine_flags(), info.emulation_flags(), info.unemulated_features(), info.imperfect_features(), info.has_nonworking_devices());
+ if ((info.machine_flags() & (MACHINE_ERRORS | MACHINE_WARNINGS | MACHINE_BTANB)) || (root.type().emulation_flags() & DEVICE_ERRORS) || root.type().unemulated_features() || root.type().imperfect_features())
{
seen.insert(&root.type());
if (!first)
buf << '\n';
first = false;
util::stream_format(buf, _("%1$s:\n"), root.name());
- get_system_warnings(buf, machine(), info.machine_flags(), root.type().unemulated_features(), root.type().imperfect_features());
+ get_system_warnings(buf, machine(), info.machine_flags(), root.type().emulation_flags(), root.type().unemulated_features(), root.type().imperfect_features(), false);
}
for (device_t const &device : device_enumerator(root))
{
- if ((device.type().unemulated_features() || device.type().imperfect_features()) && seen.insert(&device.type()).second)
+ if (((device.type().emulation_flags() & DEVICE_ERRORS) || device.type().unemulated_features() || device.type().imperfect_features()) && seen.insert(&device.type()).second)
{
if (!first)
buf << '\n';
first = false;
util::stream_format(buf, _("%1$s:\n"), device.name());
- get_device_warnings(buf, device.type().unemulated_features(), device.type().imperfect_features());
+ get_device_warnings(buf, device.type().emulation_flags(), device.type().unemulated_features(), device.type().imperfect_features());
}
}
@@ -640,11 +684,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..49e67dea5ae 100644
--- a/src/frontend/mame/ui/info.h
+++ b/src/frontend/mame/ui/info.h
@@ -30,25 +30,27 @@ public:
machine_static_info(const ui_options &options, machine_config const &config);
// overall emulation status
- ::machine_flags::type machine_flags() const { return m_flags; }
- device_t::feature_type unemulated_features() const { return m_unemulated_features; }
- device_t::feature_type imperfect_features() const { return m_imperfect_features; }
+ ::machine_flags::type machine_flags() const noexcept { return m_flags; }
+ device_t::flags_type emulation_flags() const noexcept { return m_emulation_flags; }
+ device_t::feature_type unemulated_features() const noexcept { return m_unemulated_features; }
+ device_t::feature_type imperfect_features() const noexcept { return m_imperfect_features; }
// has... getters
- bool has_bioses() const { return m_has_bioses; }
+ bool has_nonworking_devices() const noexcept { return m_has_nonworking_devices; }
+ bool has_bioses() const noexcept { return m_has_bioses; }
// has input types getters
- bool has_dips() const { return m_has_dips; }
- bool has_configs() const { return m_has_configs; }
- bool has_keyboard() const { return m_has_keyboard; }
- bool has_test_switch() const { return m_has_test_switch; }
- bool has_analog() const { return m_has_analog; }
+ bool has_dips() const noexcept { return m_has_dips; }
+ bool has_configs() const noexcept { return m_has_configs; }
+ bool has_keyboard() const noexcept { return m_has_keyboard; }
+ bool has_test_switch() const noexcept { return m_has_test_switch; }
+ bool has_analog() const noexcept { return m_has_analog; }
// warning severity indications
- bool has_warnings() const;
- bool has_severe_warnings() const;
- rgb_t status_color() const;
- rgb_t warnings_color() const;
+ bool has_warnings() const noexcept;
+ bool has_severe_warnings() const noexcept;
+ rgb_t status_color() const noexcept;
+ rgb_t warnings_color() const noexcept;
protected:
machine_static_info(const ui_options &options, machine_config const &config, ioport_list const &ports);
@@ -60,10 +62,12 @@ private:
// overall feature status
::machine_flags::type m_flags;
+ device_t::flags_type m_emulation_flags;
device_t::feature_type m_unemulated_features;
device_t::feature_type m_imperfect_features;
// has...
+ bool m_has_nonworking_devices;
bool m_has_bioses;
// has input types
@@ -104,7 +108,6 @@ protected:
private:
virtual void populate() override;
- virtual bool handle(event const *ev) override;
};
@@ -119,7 +122,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..8f006ea55e7 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 origx1, float origy1, float origx2, float origy2) override
{
if (selectedref)
{
@@ -57,20 +57,20 @@ protected:
// measure the name of the token string
float const tokenwidth = (std::min)(get_string_width(token) + (gutter_width() * 2.0F), 1.0F);
- float const boxwidth = (std::max)(tokenwidth, x2 - x);
+ float const boxwidth = (std::max)(tokenwidth, origx2 - origx1);
rgb_t const fgcolor(ui().colors().text_color());
// draw the outer box
ui().draw_outlined_box(
container(),
- (1.0F - boxwidth) * 0.5F, y2 + tb_border(),
- (1.0F + boxwidth) * 0.5F, y2 + bottom,
+ (1.0F - boxwidth) * 0.5F, origy2 + tb_border(),
+ (1.0F + boxwidth) * 0.5F, origy2 + bottom,
ui().colors().background_color());
// show the token
draw_text_normal(
token,
- (1.0F - boxwidth) * 0.5F, y2 + (tb_border() * 2.0F), boxwidth,
+ (1.0F - boxwidth) * 0.5F, origy2 + (tb_border() * 2.0F), boxwidth,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
fgcolor);
@@ -81,11 +81,11 @@ protected:
case ITEM_CLASS_RELATIVE:
{
// draw the indicator
- float const indleft = x + gutter_width();
- float const indright = x2 - gutter_width();
- float const indtop = y2 + (tb_border() * 2.0F) + (line_height() * 1.2F);
- float const indbottom = y2 + (tb_border() * 2.0F) + (line_height() * 1.8F);
- float const indcentre = (x + x2) * 0.5F;
+ float const indleft = origx1 + gutter_width();
+ float const indright = origx2 - gutter_width();
+ float const indtop = origy2 + (tb_border() * 2.0F) + (line_height() * 1.2F);
+ float const indbottom = origy2 + (tb_border() * 2.0F) + (line_height() * 1.8F);
+ float const indcentre = (origx1 + origx2) * 0.5F;
s32 const value = (input.itemclass() == ITEM_CLASS_ABSOLUTE) ? input.read_as_absolute(ITEM_MODIFIER_NONE) : input.read_as_relative(ITEM_MODIFIER_NONE);
if (0 < value)
{
diff --git a/src/frontend/mame/ui/inputmap.cpp b/src/frontend/mame/ui/inputmap.cpp
index b5b56b6e1fb..b135780a08f 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 origx1, float origy1, float origx2, float origy2)
{
if (pollingitem)
{
@@ -330,7 +330,7 @@ void menu_input::custom_render(void *selectedref, float top, float bottom, float
char const *const text[] = { seqname.c_str() };
draw_text_box(
std::begin(text), std::end(text),
- x1, x2, y2 + tb_border(), y2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
ui().colors().text_color(), ui().colors().background_color());
}
@@ -347,7 +347,7 @@ void menu_input::custom_render(void *selectedref, float top, float bottom, float
char const *const text[] = { errormsg.c_str() };
draw_text_box(
std::begin(text), std::end(text),
- x1, x2, y2 + tb_border(), y2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
ui().colors().text_color(), UI_RED_COLOR);
}
@@ -359,7 +359,7 @@ void menu_input::custom_render(void *selectedref, float top, float bottom, float
char const *const text[] = { _("Pressed") };
draw_text_box(
std::begin(text), std::end(text),
- x1, x2, y2 + tb_border(), y2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
ui().colors().text_color(), ui().colors().background_color());
}
@@ -370,7 +370,7 @@ void menu_input::custom_render(void *selectedref, float top, float bottom, float
(!item.seq.empty() || item.defseq->empty()) ? clearprompt.c_str() : defaultprompt.c_str() };
draw_text_box(
std::begin(text), std::end(text),
- x1, x2, y2 + tb_border(), y2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
ui().colors().text_color(), ui().colors().background_color());
}
diff --git a/src/frontend/mame/ui/inputmap.h b/src/frontend/mame/ui/inputmap.h
index e5947a9b649..e27e58e67fc 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 origx1, float origy1, float origx2, float origy2) 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/mainmenu.cpp b/src/frontend/mame/ui/mainmenu.cpp
index 3e0acb5059a..84c6b3d1500 100644
--- a/src/frontend/mame/ui/mainmenu.cpp
+++ b/src/frontend/mame/ui/mainmenu.cpp
@@ -12,6 +12,8 @@
#include "ui/mainmenu.h"
#include "ui/about.h"
+#include "ui/audiomix.h"
+#include "ui/audioeffects.h"
#include "ui/barcode.h"
#include "ui/cheatopt.h"
#include "ui/confswitch.h"
@@ -56,6 +58,8 @@ enum : unsigned {
TAPE_CONTROL,
SLOT_DEVICES,
NETWORK_DEVICES,
+ AUDIO_MIXER,
+ AUDIO_EFFECTS,
SLIDERS,
VIDEO_TARGETS,
CROSSHAIR,
@@ -65,8 +69,7 @@ enum : unsigned {
BARCODE_READ,
PTY_INFO,
EXTERNAL_DATS,
- ADD_FAVORITE,
- REMOVE_FAVORITE,
+ FAVORITE,
ABOUT,
QUIT_GAME,
DISMISS,
@@ -123,7 +126,7 @@ void menu_main::populate()
item_append(_("menu-main", "System Information"), 0, (void *)GAME_INFO);
- if (ui().found_machine_warnings())
+ if (ui().machine_info().has_warnings())
item_append(_("menu-main", "Warning Information"), 0, (void *)WARN_INFO);
for (device_image_interface &image : image_interface_enumerator(machine().root_device()))
@@ -158,6 +161,10 @@ void menu_main::populate()
if (network_interface_enumerator(machine().root_device()).first() != nullptr)
item_append(_("menu-main", "Network Devices"), 0, (void*)NETWORK_DEVICES);
+ item_append(_("menu-main", "Audio Mixer"), 0, (void *)AUDIO_MIXER);
+
+ item_append(_("menu-main", "Audio Effects"), 0, (void *)AUDIO_EFFECTS);
+
item_append(_("menu-main", "Slider Controls"), 0, (void *)SLIDERS);
item_append(_("menu-main", "Video Options"), 0, (void *)VIDEO_TARGETS);
@@ -166,7 +173,7 @@ void menu_main::populate()
item_append(_("menu-main", "Crosshair Options"), 0, (void *)CROSSHAIR);
if (machine().options().cheat())
- item_append(_("menu-main", "Cheat"), 0, (void *)CHEAT);
+ item_append(_("menu-main", "Cheat Options"), 0, (void *)CHEAT);
if (machine_phase::RESET <= m_phase)
{
@@ -180,9 +187,9 @@ void menu_main::populate()
item_append(menu_item_type::SEPARATOR);
if (!mame_machine_manager::instance()->favorite().is_favorite(machine()))
- item_append(_("menu-main", "Add To Favorites"), 0, (void *)ADD_FAVORITE);
+ item_append(_("menu-main", "Add To Favorites"), 0, (void *)FAVORITE);
else
- item_append(_("menu-main", "Remove From Favorites"), 0, (void *)REMOVE_FAVORITE);
+ item_append(_("menu-main", "Remove From Favorites"), 0, (void *)FAVORITE);
item_append(menu_item_type::SEPARATOR);
@@ -244,7 +251,7 @@ bool menu_main::handle(event const *ev)
break;
case IMAGE_MENU_FILE_MANAGER:
- menu::stack_push<menu_file_manager>(ui(), container(), nullptr);
+ menu::stack_push<menu_file_manager>(ui(), container(), std::string());
break;
case TAPE_CONTROL:
@@ -263,6 +270,14 @@ bool menu_main::handle(event const *ev)
menu::stack_push<menu_network_devices>(ui(), container());
break;
+ case AUDIO_MIXER:
+ menu::stack_push<menu_audio_mixer>(ui(), container());
+ break;
+
+ case AUDIO_EFFECTS:
+ menu::stack_push<menu_audio_effects>(ui(), container());
+ break;
+
case SLIDERS:
menu::stack_push<menu_sliders>(ui(), container(), false);
break;
@@ -306,15 +321,16 @@ bool menu_main::handle(event const *ev)
menu::stack_push<menu_dats_view>(ui(), container());
break;
- case ADD_FAVORITE:
- mame_machine_manager::instance()->favorite().add_favorite(machine());
- reset(reset_options::REMEMBER_REF);
- break;
-
- case REMOVE_FAVORITE:
- mame_machine_manager::instance()->favorite().remove_favorite(machine());
+ case FAVORITE:
+ {
+ favorite_manager &mfav = mame_machine_manager::instance()->favorite();
+ if (mfav.is_favorite(machine()))
+ mfav.remove_favorite(machine());
+ else
+ mfav.add_favorite(machine());
reset(reset_options::REMEMBER_REF);
break;
+ }
case QUIT_GAME:
stack_pop();
diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp
index 9c2ffebd951..6f588bcc17f 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,10 @@
#include "osdepend.h"
-#include <algorithm>
#include <cassert>
#include <cmath>
+#include <cstdlib>
+#include <limits>
#include <type_traits>
@@ -77,6 +77,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 +128,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 +163,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 +223,152 @@ 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->activate_menu();
+ 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 +397,32 @@ 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_accumulated_wheel(0)
, 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 +447,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 +556,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 +580,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();
@@ -525,6 +642,9 @@ void menu::draw(uint32_t flags)
{
heading_layout.emplace(create_layout(max_width - (gutter_width() * 2.0F), text_layout::text_justify::CENTER));
heading_layout->add_text(*m_heading, ui().colors().text_color());
+
+ // readjust visible width if heading width exceeds that of the menu
+ visible_width = std::max(gutter_width() + heading_layout->actual_width() + gutter_width(), visible_width);
}
// account for extra space at the top and bottom
@@ -547,28 +667,27 @@ 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 = ((1.0F - visible_main_menu_height - visible_extra_menu_height) * 0.5F) + top_extra_menu_height;
+ if (m_last_size.second != 0)
+ m_items_top = std::round(m_items_top * 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)
{
- float const heading_width = heading_layout->actual_width();
- float const heading_left = (1.0F - heading_width) * 0.5F;
- float const hx1 = std::min(x1, heading_left - gutter_width() - lr_border());
- float const hx2 = std::max(x2, heading_left + heading_width + gutter_width() + lr_border());
ui().draw_outlined_box(
container(),
- hx1, y1 - top_extra_menu_height,
- hx2, y1 - m_customtop - tb_border(),
+ x1, y1 - top_extra_menu_height,
+ x2, y1 - m_customtop - tb_border(),
UI_GREEN_COLOR);
heading_layout->emit(container(), (1.0F - heading_layout->width()) * 0.5F, y1 - top_extra_menu_height + tb_border());
}
+
ui().draw_outlined_box(
container(),
x1, y1,
@@ -586,29 +705,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 +751,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 +901,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 +928,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 +959,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 +974,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)
-{
-}
-
-//-------------------------------------------------
-// map_mouse - map mouse pointer location to menu
-// coordinates
-//-------------------------------------------------
+ // don't accept pointer input until the menu has been redrawn
+ m_items_drawn = false;
+ m_pointer_state = track_pointer::IDLE;
-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 origx1, float origy1, float origx2, float origy2)
{
- m_mouse_hit = false;
- m_mouse_button = false;
- m_mouse_x = -1.0F;
- m_mouse_y = -1.0F;
}
@@ -887,8 +991,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 +1002,100 @@ 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())
+ auto const [key, redraw] = handle_pointer_update(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- 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)
- {
- 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)
+ // the value is scaled to 120 units per "click"
+ m_accumulated_wheel += local_menu_event.zdelta * local_menu_event.num_lines;
+ int const lines((m_accumulated_wheel + ((0 < local_menu_event.zdelta) ? 36 : -36)) / 120);
+ if (!lines)
+ break;
+ m_accumulated_wheel -= lines * 120;
+
+ if (!custom_mouse_scroll(-lines) && !(flags & (PROCESS_ONLYCHAR | PROCESS_CUSTOM_NAV)))
{
- if (is_first_selected())
+ if (lines > 0)
{
- select_last_item();
+ if (is_first_selected())
+ {
+ select_last_item();
+ }
+ else
+ {
+ m_selected -= 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 -= lines;
}
else
{
- m_selected -= local_menu_event.num_lines;
- validate_selection(-1);
+ if (is_last_selected())
+ {
+ select_first_item();
+ }
+ else
+ {
+ m_selected -= 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 -= 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 +1103,537 @@ 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))
+ {
+ bool const wascustom(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 (wascustom)
+ {
+ 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 +1642,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 +1660,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 +1670,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 +1687,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 +1699,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 +1725,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 +1733,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 +1747,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
@@ -1178,10 +1804,6 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
machine().pause();
}
- // handle a toggle cheats request
- if (machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0))
- mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled());
-
// see if any other UI keys are pressed
if (iptkey == IPT_INVALID)
{
@@ -1206,6 +1828,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()
+{
}
@@ -1253,6 +1908,100 @@ void menu::validate_selection(int scandir)
}
+//-------------------------------------------------
+// activate_menu - handle becoming top of the
+// menu stack
+//-------------------------------------------------
+
+void menu::activate_menu()
+{
+ m_items_drawn = false;
+ m_pointer_state = track_pointer::IDLE;
+ m_accumulated_wheel = 0;
+ m_active = true;
+ menu_activated();
+}
+
+
+//-------------------------------------------------
+// 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) && (std::fabs(1.0F - (aspect / m_last_aspect)) < 1e-6F))
+ 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
@@ -1266,43 +2015,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;
}
@@ -1332,7 +2112,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..b59913df1a7 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 origx1, float origy1, float origx2, float origy2);
- // 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,12 @@ private:
void extra_text_draw_box(float origx1, float origx2, float origy, float yspan, std::string_view text, int direction);
+ void activate_menu();
+ 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 +547,36 @@ 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;
+ int m_accumulated_wheel; // accumulated scroll wheel/gesture movement
+
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/midiinout.cpp b/src/frontend/mame/ui/midiinout.cpp
new file mode 100644
index 00000000000..558d3f0fb0f
--- /dev/null
+++ b/src/frontend/mame/ui/midiinout.cpp
@@ -0,0 +1,103 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*********************************************************************
+
+ ui/midiinout.cpp
+
+ Midi channel selection
+
+*********************************************************************/
+
+#include "emu.h"
+#include "ui/midiinout.h"
+
+#include "ui/ui.h"
+
+#include "osdepend.h"
+
+namespace ui {
+
+menu_midi_inout::menu_midi_inout(mame_ui_manager &mui, render_container &container, bool is_input, std::string *channel)
+ : menu(mui, container)
+ , m_channel(channel)
+ , m_is_input(is_input)
+{
+ set_heading(m_is_input ? _("MIDI input channel") : _("MIDI output channel"));
+}
+
+menu_midi_inout::~menu_midi_inout()
+{
+}
+
+bool menu_midi_inout::handle(event const *ev)
+{
+ if(!ev)
+ return false;
+
+ if(ev->iptkey == IPT_UI_SELECT) {
+ *m_channel = m_port_names[uintptr_t(ev->itemref)];
+ stack_pop();
+ return true;
+ }
+
+ return false;
+}
+
+
+//-------------------------------------------------
+// menu_midi_inout_populate - populate the midi_inout
+// menu
+//-------------------------------------------------
+
+void menu_midi_inout::populate()
+{
+ auto ports = machine().osd().list_midi_ports();
+ for(auto &p : ports)
+ if((m_is_input && p.input) || (!m_is_input && p.output)) {
+ item_append(p.name, "", 0, (void *)(m_port_names.size()));
+ m_port_names.push_back(p.name);
+ }
+}
+
+
+//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void menu_midi_inout::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+}
+
+
+//-------------------------------------------------
+// menu_midi_inout_custom_render - perform our special
+// rendering
+//-------------------------------------------------
+
+void menu_midi_inout::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+}
+
+
+//-------------------------------------------------
+// menu_activated - handle menu gaining focus
+//-------------------------------------------------
+
+void menu_midi_inout::menu_activated()
+{
+ // scripts or the other form of the menu could have changed something in the mean time
+ reset(reset_options::REMEMBER_POSITION);
+}
+
+
+//-------------------------------------------------
+// menu_deactivated - handle menu losing focus
+//-------------------------------------------------
+
+void menu_midi_inout::menu_deactivated()
+{
+}
+
+} // namespace ui
+
diff --git a/src/frontend/mame/ui/midiinout.h b/src/frontend/mame/ui/midiinout.h
new file mode 100644
index 00000000000..1cc7f360d35
--- /dev/null
+++ b/src/frontend/mame/ui/midiinout.h
@@ -0,0 +1,44 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/***************************************************************************
+
+ ui/midiinout.h
+
+ Midi channel selection
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_MIDIINOUT_H
+#define MAME_FRONTEND_UI_MIDIINOUT_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+
+namespace ui {
+
+class menu_midi_inout : public menu
+{
+public:
+ menu_midi_inout(mame_ui_manager &mui, render_container &container, bool is_input, std::string *channel);
+ virtual ~menu_midi_inout() override;
+
+protected:
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) 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;
+
+private:
+ std::vector<std::string> m_port_names;
+ std::string *m_channel;
+ bool m_is_input;
+
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_MIDIINOUT_H
diff --git a/src/frontend/mame/ui/miscmenu.cpp b/src/frontend/mame/ui/miscmenu.cpp
index c05d01159fa..377c34ed2d8 100644
--- a/src/frontend/mame/ui/miscmenu.cpp
+++ b/src/frontend/mame/ui/miscmenu.cpp
@@ -20,7 +20,6 @@
#include "infoxml.h"
#include "mame.h"
-#include "osdnet.h"
#include "mameopts.h"
#include "pluginopts.h"
#include "dinetwork.h"
@@ -29,6 +28,8 @@
#include "romload.h"
#include "uiinput.h"
+#include "osdepend.h"
+
#include "path.h"
#include <algorithm>
@@ -172,17 +173,17 @@ void menu_network_devices::populate()
for (device_network_interface &network : network_interface_enumerator(machine().root_device()))
{
int curr = network.get_interface();
- const char *title = nullptr;
- for (auto &entry : get_netdev_list())
+ std::string_view title;
+ for (auto &entry : machine().osd().list_network_devices())
{
- if (entry->id == curr)
+ if (entry.id == curr)
{
- title = entry->description;
+ title = entry.description;
break;
}
}
- item_append(network.device().tag(), title ? title : "------", FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&network);
+ item_append(network.device().tag(), std::string(!title.empty() ? title : "------"), FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&network);
}
item_append(menu_item_type::SEPARATOR);
@@ -201,27 +202,33 @@ bool menu_network_devices::handle(event const *ev)
else if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
{
device_network_interface *const network = (device_network_interface *)ev->itemref;
+ auto const interfaces = machine().osd().list_network_devices();
int curr = network->get_interface();
+ auto const found = std::find_if(
+ std::begin(interfaces),
+ std::end(interfaces),
+ [curr] (osd::network_device_info const &info) { return info.id == curr; });
+ auto index = std::distance(interfaces.begin(), found);
if (ev->iptkey == IPT_UI_LEFT)
- curr--;
- else
- curr++;
- if (curr == -2)
- curr = netdev_count() - 1;
- network->set_interface(curr);
+ --index;
+ else if (std::end(interfaces) == found)
+ index = 0;
+ else if (std::size(interfaces) <= ++index)
+ index = -1;
+ network->set_interface((0 <= index) ? interfaces[index].id : -1);
curr = network->get_interface();
- const char *title = nullptr;
- for (auto &entry : get_netdev_list())
+ std::string_view title;
+ for (auto &entry : interfaces)
{
- if (entry->id == curr)
+ if (entry.id == curr)
{
- title = entry->description;
+ title = entry.description;
break;
}
}
- ev->item->set_subtext(title ? title : "------");
+ ev->item->set_subtext(!title.empty() ? title : "------");
return true;
}
else
@@ -309,7 +316,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/prscntrl.cpp b/src/frontend/mame/ui/prscntrl.cpp
index aab071cfc0e..1d5d2fb4f81 100644
--- a/src/frontend/mame/ui/prscntrl.cpp
+++ b/src/frontend/mame/ui/prscntrl.cpp
@@ -46,9 +46,9 @@ menu_control_device_preset::~menu_control_device_preset()
void menu_control_device_preset::populate()
{
auto presets = m_image.preset_images_list();
- for(int i = 0; i != int(presets.size()); i++)
+ for(uintptr_t i = 0; i != uintptr_t(presets.size()); i++)
item_append(presets[i], 0, reinterpret_cast<void *>(i));
- set_selection(reinterpret_cast<void *>(m_image.current_preset_image_id()));
+ set_selection(reinterpret_cast<void *>(uintptr_t(m_image.current_preset_image_id())));
}
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..d52c081da05 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 origx1, float origy1, float origx2, float origy2) 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..dda281f684a 100644
--- a/src/frontend/mame/ui/selgame.cpp
+++ b/src/frontend/mame/ui/selgame.cpp
@@ -196,7 +196,7 @@ void menu_select_game::menu_deactivated()
bool menu_select_game::handle(event const *ev)
{
- if (!m_prev_selected && item_count() > 0)
+ if (!m_prev_selected && (item_count() > 0))
m_prev_selected = item(0).ref();
// if I have to select software, force software list submenu
@@ -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;
}
@@ -479,7 +405,7 @@ void menu_select_game::populate()
item_append(_("System Settings"), 0, (void *)(uintptr_t)CONF_MACHINE);
m_skip_main_items = 3;
- if (m_prev_selected && !have_prev_selected && item_count() > 0)
+ if (m_prev_selected && !have_prev_selected && (item_count() > 0))
m_prev_selected = item(0).ref();
}
else
@@ -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 d846b21ba57..a9d4c9e9bed 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,12 +216,13 @@ 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)
: m_machine_flags(info.machine_flags())
+ , m_emulation_flags(info.emulation_flags())
, m_unemulated_features(info.unemulated_features())
, m_imperfect_features(info.imperfect_features())
, m_has_keyboard(info.has_keyboard())
@@ -253,10 +264,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 +431,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 +462,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 +474,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 +529,34 @@ 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_wheel_target(focused_menu::MAIN)
+ , m_wheel_movement(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 +569,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 +767,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 +798,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(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
std::string tempbuf[4];
@@ -682,7 +812,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;
@@ -744,12 +874,12 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto
// next line is overall driver status
system_flags const &flags(get_system_flags(driver));
- if (flags.machine_flags() & machine_flags::NOT_WORKING)
- tempbuf[2] = _("Overall: NOT WORKING");
+ if (flags.emulation_flags() & device_t::flags::NOT_WORKING)
+ tempbuf[2] = _("Status: NOT WORKING");
else if ((flags.unemulated_features() | flags.imperfect_features()) & device_t::feature::PROTECTION)
- tempbuf[2] = _("Overall: Unemulated Protection");
+ tempbuf[2] = _("Status: Unemulated Protection");
else
- tempbuf[2] = _("Overall: Working");
+ tempbuf[2] = _("Status: Working");
// next line is graphics, sound status
if (flags.unemulated_features() & device_t::feature::GRAPHICS)
@@ -772,14 +902,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);
- 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);
+ // first line is version string
+ tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version);
+
+ // 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
@@ -798,6 +941,8 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto
void menu_select_launch::menu_activated()
{
m_panels_status = ui().options().hide_panels();
+ m_wheel_target = focused_menu::MAIN;
+ m_wheel_movement = 0;
}
@@ -812,14 +957,14 @@ 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))
+ if ((m_panels_status == HIDE_BOTH) || ((0 > dir) && m_available_items))
select_prev();
- else if (m_panels_status == HIDE_LEFT_PANEL)
- set_focus(focused_menu::RIGHTTOP);
+ else if (0 > dir)
+ set_focus((m_panels_status == HIDE_RIGHT_PANEL) ? focused_menu::LEFT : focused_menu::RIGHTBOTTOM);
else
- set_focus(focused_menu::LEFT);
+ set_focus((m_panels_status == HIDE_LEFT_PANEL) ? focused_menu::RIGHTTOP : focused_menu::LEFT);
}
else if (m_skip_main_items || (m_panels_status != HIDE_BOTH))
{
@@ -915,204 +1060,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();
-
- rgb_t fgcolor_right, fgcolor_left;
- fgcolor_right = fgcolor_left = ui().colors().text_color();
+ 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));
- // 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);
}
@@ -1210,10 +1362,10 @@ bool menu_select_launch::scale_icon(bitmap_argb32 &&src, texture_and_bitmap &dst
assert(dst.texture);
if (src.valid())
{
- // reduce the source bitmap if it's too big
+ // scale the source bitmap
bitmap_argb32 tmp;
- float const ratio((std::min)({ float(m_icon_height) / src.height(), float(m_icon_width) / src.width(), 1.0F }));
- if (1.0F > ratio)
+ float const ratio((std::min)(float(m_icon_height) / src.height(), float(m_icon_width) / src.width()));
+ if ((1.0F > ratio) || (1.2F < ratio))
{
float const pix_height(std::ceil(src.height() * ratio));
float const pix_width(std::ceil(src.width() * ratio));
@@ -1282,77 +1434,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 +1538,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 +1558,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 +1589,292 @@ 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)
+ {
+ // check whether it's over something scrollable
+ 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)
+ {
+ m_wheel_movement = 0;
+ break;
+ }
+ focused_menu hover;
+ 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()))))
+ {
+ hover = focused_menu::MAIN;
+ }
+ else if (show_left_panel() && (x >= m_left_items_hbounds.first) && (x < m_left_items_hbounds.second) && (y >= m_left_items_top) && (y < (m_left_items_top + (float(m_left_visible_lines) * m_info_line_height))))
+ {
+ hover = focused_menu::LEFT;
+ }
+ else if (show_right_panel() && (x >= m_right_content_hbounds.first) && (x < m_right_content_hbounds.second) && (y >= m_right_content_vbounds.first) && (y < m_right_content_vbounds.second))
+ {
+ hover = focused_menu::RIGHTBOTTOM;
+ }
+ else
+ {
+ m_wheel_movement = 0;
+ break;
+ }
+
+ // clear out leftovers if it isn't the last thing to be scrolled
+ if (hover != m_wheel_target)
+ m_wheel_movement = 0;
+ m_wheel_target = hover;
+
+ // the value is scaled to 120 units per "click"
+ m_wheel_movement += local_menu_event.zdelta * local_menu_event.num_lines;
+ int const lines((m_wheel_movement + ((0 < local_menu_event.zdelta) ? 36 : -36)) / 120);
+ if (!lines)
+ break;
+ m_wheel_movement -= lines * 120;
+
+ switch (hover)
+ {
+ case focused_menu::MAIN:
+ if (lines > 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() - lines);
+ if (selected_index() < top_line + (top_line != 0))
+ top_line -= 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() - lines, m_available_items - 1));
+ if (selected_index() >= top_line + m_visible_items + (top_line != 0))
+ top_line -= lines;
+ }
+ break;
+ case focused_menu::LEFT:
+ {
+ m_left_visible_top = std::clamp(m_left_visible_top - lines, 0, m_left_item_count - m_left_visible_lines);
+ int const first(left_at_top() ? 0 : (m_left_visible_top + 1));
+ int const last(m_left_visible_top + m_left_visible_lines - (left_at_bottom() ? 1 : 2));
+ m_filter_highlight = std::clamp(m_filter_highlight, first, last);
+ m_filter_highlight = std::clamp(m_filter_highlight - lines, 0, m_left_item_count - 1);
+ }
+ break;
+ case focused_menu::RIGHTBOTTOM:
+ if (RP_INFOS == m_right_panel)
+ m_topline_datsview -= lines;
+ break;
+ case focused_menu::RIGHTTOP:
+ break; // never gets here
+ }
+ }
+ break;
+
+ // text input goes to the search field unless there's an error message displayed
+ case ui_event::type::IME_CHAR:
+ if (have_pointer() && !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 +1882,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 +1897,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,19 +1907,23 @@ 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;
}
}
if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
{
- if (!m_search.empty())
+ if (m_ui_error)
+ {
+ // dismiss error
+ }
+ else if (!m_search.empty())
{
// escape pressed with non-empty search text clears it
m_search.clear();
@@ -1492,98 +1935,174 @@ 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))
{
- if (m_focus == focused_menu::RIGHTTOP)
+ if (m_ui_error)
+ {
+ // dismiss error
+ return false;
+ }
+ else 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;
+ }
}
}
// swallow left/right keys if they are not appropriate
if (exclusive_input_pressed(iptkey, IPT_UI_RIGHT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
{
- if (m_focus == focused_menu::RIGHTTOP)
+ if (m_ui_error)
+ {
+ // dismiss error
+ return false;
+ }
+ else 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 +2113,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,54 +2142,82 @@ 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
if (!m_ui_error && machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0))
- mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled());
+ mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled(), true);
// handle pasting text into the search
if (exclusive_input_pressed(iptkey, IPT_UI_PASTE, 0))
@@ -1691,238 +2249,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
+//-------------------------------------------------
+
+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;
+ }
- case ui_event::type::MOUSE_RDOWN:
- if (hover() >= 0 && hover() < item_count() - m_skip_main_items)
+ // 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 if (m_prev_selected)
+ {
+ 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 +3194,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 +3234,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 +3276,180 @@ 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)
+ 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 (is_selectable(pitem))
{
- fgcolor = ui().options().mouseover_color();
- bgcolor = ui().colors().mouseover_bg_color();
- highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
+ bool const hovered(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 (pointerline && hovered)
+ {
+ // 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(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);
-
- // 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);
+ custom_render(
+ flags,
+ get_selection_ref(),
+ get_customtop(), get_custombottom(),
+ lr_border(), m_primary_vbounds.first, 1.0F - lr_border(), m_primary_vbounds.second);
- // 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 +3457,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);
- }
- }
+ float const tableft(x1 + (float(cells - RP_FIRST) * tabwidth));
- 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();
- }
-
- 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()));
+
+ 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);
}
- return (y1 + line_height() + UI_LINE_WIDTH);
+ 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 +3655,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 +3668,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 +3691,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);
- }
-
- 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));
+ draw_snapx();
}
-
- 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 +3723,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 +3736,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 +3811,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 +3982,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 +4071,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 +4096,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));
}
@@ -2967,7 +4151,7 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
if (flags.has_keyboard())
str << _("Keyboard Inputs\tYes\n");
- if (flags.machine_flags() & machine_flags::NOT_WORKING)
+ if (flags.emulation_flags() & device_t::flags::NOT_WORKING)
str << _("Overall\tNOT WORKING\n");
else if ((flags.unemulated_features() | flags.imperfect_features()) & device_t::feature::PROTECTION)
str << _("Overall\tUnemulated Protection\n");
@@ -3079,14 +4263,13 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
else if (flags.imperfect_features() & device_t::feature::TIMING)
str << _("Timing\tImperfect\n");
- str << ((flags.machine_flags() & machine_flags::MECHANICAL) ? _("Mechanical System\tYes\n") : _("Mechanical System\tNo\n"));
- str << ((flags.machine_flags() & machine_flags::REQUIRES_ARTWORK) ? _("Requires Artwork\tYes\n") : _("Requires Artwork\tNo\n"));
- str << ((flags.machine_flags() & machine_flags::CLICKABLE_ARTWORK) ? _("Requires Clickable Artwork\tYes\n") : _("Requires Clickable Artwork\tNo\n"));
+ str << ((flags.machine_flags() & machine_flags::MECHANICAL) ? _("Mechanical System\tYes\n") : _("Mechanical System\tNo\n"));
+ str << ((flags.machine_flags() & machine_flags::REQUIRES_ARTWORK) ? _("Requires Artwork\tYes\n") : _("Requires Artwork\tNo\n"));
if (flags.machine_flags() & machine_flags::NO_COCKTAIL)
str << _("Support Cocktail\tNo\n");
- str << ((flags.machine_flags() & machine_flags::IS_BIOS_ROOT) ? _("System is BIOS\tYes\n") : _("System is BIOS\tNo\n"));
- str << ((flags.machine_flags() & machine_flags::SUPPORTS_SAVE) ? _("Support Save\tYes\n") : _("Support Save\tNo\n"));
- str << ((flags.machine_flags() & ORIENTATION_SWAP_XY) ? _("Screen Orientation\tVertical\n") : _("Screen Orientation\tHorizontal\n"));
+ str << ((flags.machine_flags() & machine_flags::IS_BIOS_ROOT) ? _("System is BIOS\tYes\n") : _("System is BIOS\tNo\n"));
+ str << ((flags.emulation_flags() & device_t::flags::SAVE_UNSUPPORTED) ? _("Support Save\tNo\n") : _("Support Save\tYes\n"));
+ str << ((flags.machine_flags() & ORIENTATION_SWAP_XY) ? _("Screen Orientation\tVertical\n") : _("Screen Orientation\tHorizontal\n"));
bool found = false;
for (romload::region const &region : romload::entries(driver.rom).get_regions())
{
diff --git a/src/frontend/mame/ui/selmenu.h b/src/frontend/mame/ui/selmenu.h
index 872901ff9b6..f95dd8fb163 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
@@ -75,6 +88,7 @@ protected:
system_flags &operator=(system_flags &&) = default;
::machine_flags::type machine_flags() const { return m_machine_flags; }
+ device_t::flags_type emulation_flags() const { return m_emulation_flags; }
device_t::feature_type unemulated_features() const { return m_unemulated_features; }
device_t::feature_type imperfect_features() const { return m_imperfect_features; }
bool has_keyboard() const { return m_has_keyboard; }
@@ -83,6 +97,7 @@ protected:
private:
::machine_flags::type m_machine_flags;
+ device_t::flags_type m_emulation_flags;
device_t::feature_type m_unemulated_features;
device_t::feature_type m_imperfect_features;
bool m_has_keyboard;
@@ -113,9 +128,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 +139,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(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) override;
virtual void menu_activated() override;
virtual void menu_deactivated() override;
@@ -137,15 +149,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 +171,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 +199,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,28 +269,55 @@ 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()
{
if (!m_prev_selected)
{
- set_selected_index(0);
+ if (m_available_items)
+ set_selected_index(0);
}
else
{
@@ -277,38 +331,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 +397,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 +432,40 @@ 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;
+ focused_menu m_wheel_target;
+ int m_wheel_movement;
+
+ 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..4e9a16bb196 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"
@@ -226,11 +227,15 @@ public:
std::locale const lcl;
std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
auto const compare_names =
- [&coll] (std::string const &x, std::string const &y) -> bool
+ [&coll] (std::string const &xl, std::string const &xd, std::string const &yl, std::string const &yd) -> bool
{
- std::wstring const wx = wstring_from_utf8(x);
- std::wstring const wy = wstring_from_utf8(y);
- return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size());
+ std::wstring const wx = wstring_from_utf8(xd);
+ std::wstring const wy = wstring_from_utf8(yd);
+ auto const cmp(coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size()));
+ if (cmp)
+ return 0 > cmp;
+ else
+ return xl < yl;
};
std::stable_sort(
m_swinfo.begin() + 1,
@@ -242,29 +247,29 @@ public:
if (!clonex && !cloney)
{
- return compare_names(a.longname, b.longname);
+ return compare_names(a.listname, a.longname, b.listname, b.longname);
}
else if (!clonex && cloney)
{
- if ((a.shortname == b.parentname) && (a.instance == b.instance))
+ if ((a.shortname == b.parentname) && (a.listname == b.listname))
return true;
else
- return compare_names(a.longname, b.parentlongname);
+ return compare_names(a.listname, a.longname, b.listname, b.parentlongname);
}
else if (clonex && !cloney)
{
- if ((a.parentname == b.shortname) && (a.instance == b.instance))
+ if ((a.parentname == b.shortname) && (a.listname == b.listname))
return false;
else
- return compare_names(a.parentlongname, b.longname);
+ return compare_names(a.listname, a.parentlongname, b.listname, b.longname);
}
- else if ((a.parentname == b.parentname) && (a.instance == b.instance))
+ else if ((a.parentname == b.parentname) && (a.listname == b.listname))
{
- return compare_names(a.longname, b.longname);
+ return compare_names(a.listname, a.longname, b.listname, b.longname);
}
else
{
- return compare_names(a.parentlongname, b.parentlongname);
+ return compare_names(a.listname, a.parentlongname, b.listname, b.parentlongname);
}
});
@@ -423,7 +428,7 @@ menu_select_software::~menu_select_software()
bool menu_select_software::handle(event const *ev)
{
- if (m_prev_selected == nullptr && item_count() > 0)
+ if (!m_prev_selected && (item_count() > 0))
m_prev_selected = item(0).ref();
// FIXME: everything above here used run before events were processed
@@ -444,68 +449,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 +481,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 +493,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 +645,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 +712,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 +741,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..1034a368f1b 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 {
@@ -42,7 +43,8 @@ simple_menu_select_game::simple_menu_select_game(mame_ui_manager &mui, render_co
, m_driverlist(driver_list::total() + 1)
, m_drivlist()
, m_cached_driver(nullptr)
- , m_cached_flags(machine_flags::NOT_WORKING)
+ , m_cached_machine_flags(machine_flags::ROT0)
+ , m_cached_emulation_flags(device_t::flags::NOT_WORKING)
, m_cached_unemulated(device_t::feature::NONE), m_cached_imperfect(device_t::feature::NONE)
, m_cached_color(ui().colors().background_color())
{
@@ -143,16 +145,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 +292,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 +300,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 +318,134 @@ 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_machine_flags = info.machine_flags();
+ m_cached_emulation_flags = info.emulation_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_emulation_flags & device_t::flags::NOT_WORKING)
+ tempbuf[3] = _("Status: NOT WORKING");
+ else if ((m_cached_unemulated | m_cached_imperfect) & device_t::feature::PROTECTION)
+ tempbuf[3] = _("Status: Unemulated Protection");
+ else
+ tempbuf[3] = _("Status: 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_machine_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..14e3b34d74b 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 origx1, float origy1, float origx2, float origy2) 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 };
@@ -56,7 +59,8 @@ private:
// cached driver flags
const game_driver * m_cached_driver;
- machine_flags::type m_cached_flags;
+ machine_flags::type m_cached_machine_flags;
+ device_t::flags_type m_cached_emulation_flags;
device_t::feature_type m_cached_unemulated;
device_t::feature_type m_cached_imperfect;
rgb_t m_cached_color;
diff --git a/src/frontend/mame/ui/sliders.cpp b/src/frontend/mame/ui/sliders.cpp
index b708b6af56a..2f92fe9e4fa 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 origx1, float origy1, float origx2, float origy2)
{
const slider_state *curslider = (const slider_state *)selectedref;
if (curslider != nullptr)
@@ -281,10 +280,10 @@ void menu_sliders::custom_render(void *selectedref, float top, float bottom, flo
tempstring.insert(0, " ").insert(0, curslider->description);
// move us to the bottom of the screen, and expand to full width
- y2 = 1.0f - tb_border();
- y1 = y2 - bottom;
- x1 = lr_border();
- x2 = 1.0f - lr_border();
+ float y2 = 1.0f - tb_border();
+ float y1 = y2 - bottom;
+ float x1 = lr_border();
+ float x2 = 1.0f - lr_border();
// draw extra menu area
ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
diff --git a/src/frontend/mame/ui/sliders.h b/src/frontend/mame/ui/sliders.h
index f9c0c69a176..a705204d082 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 origx1, float origy1, float origx2, float origy2) 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/sndmenu.cpp b/src/frontend/mame/ui/sndmenu.cpp
index e5516f41cfa..32a590b9730 100644
--- a/src/frontend/mame/ui/sndmenu.cpp
+++ b/src/frontend/mame/ui/sndmenu.cpp
@@ -65,7 +65,6 @@ void menu_sound_options::menu_dismissed()
if (moptions.bool_value(OPTION_SAMPLES) != m_samples)
moptions.set_value(OPTION_SAMPLES, m_samples, OPTION_PRIORITY_CMDLINE);
-
}
//-------------------------------------------------
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..cff9f3013ff 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 origx1, float origy1, float origx2, float origy2) 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..7ad92467d46 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 origx1, float origy1, float origx2, float origy2) override;
private:
virtual void populate() override;
diff --git a/src/frontend/mame/ui/swlist.cpp b/src/frontend/mame/ui/swlist.cpp
index 70428ade32f..989147f3bb4 100644
--- a/src/frontend/mame/ui/swlist.cpp
+++ b/src/frontend/mame/ui/swlist.cpp
@@ -149,6 +149,8 @@ bool menu_software_parts::handle(event const *ev)
menu_software_list::menu_software_list(mame_ui_manager &mui, render_container &container, software_list_device *swlist, const char *interface, std::string &result)
: menu(mui, container), m_result(result)
{
+ set_heading(swlist->description());
+
set_process_flags(PROCESS_IGNOREPAUSE);
m_swlist = swlist;
m_interface = interface;
@@ -251,10 +253,23 @@ void menu_software_list::populate()
for (const software_info &swinfo : m_swlist->get_info())
append_software_entry(swinfo);
+ if (m_entrylist.size() > 1)
+ {
+ // add an entry to change ordering
+ item_append(_("Switch Item Ordering"), 0, ITEMREF_SWITCH_ITEM_ORDERING);
+
+ // initial cursor to first entry in the list
+ set_selected_index(1);
+ }
+
if (m_ordered_by_shortname)
{
// short names are restricted to lowercase ASCII anyway, a dumb compare works
m_entrylist.sort([] (entry_info const &e1, entry_info const &e2) { return e1.short_name < e2.short_name; });
+
+ // append all of the menu entries
+ for (auto &entry : m_entrylist)
+ item_append(entry.short_name, entry.long_name, 0, &entry);
}
else
{
@@ -271,14 +286,11 @@ void menu_software_list::populate()
else
return e1.short_name < e2.short_name;
});
- }
-
- // add an entry to change ordering
- item_append(_("Switch Item Ordering"), 0, ITEMREF_SWITCH_ITEM_ORDERING);
- // append all of the menu entries
- for (auto &entry : m_entrylist)
- item_append(entry.long_name, entry.short_name, 0, &entry);
+ // append all of the menu entries
+ for (auto &entry : m_entrylist)
+ item_append(entry.long_name, entry.short_name, 0, &entry);
+ }
item_append(menu_item_type::SEPARATOR);
}
@@ -376,6 +388,8 @@ bool menu_software_list::handle(event const *ev)
menu_software::menu_software(mame_ui_manager &mui, render_container &container, const char *interface, software_list_device **result)
: menu(mui, container)
{
+ set_heading(_("Software List"));
+
m_interface = interface;
m_result = result;
}
@@ -432,7 +446,7 @@ void menu_software::populate()
if (found)
{
if (!have_compatible)
- item_append(_("[compatible lists]"), FLAG_DISABLE, nullptr);
+ item_append(_("[compatible lists]"), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
item_append(swlistdev.description(), 0, (void *)&swlistdev);
}
have_compatible = true;
diff --git a/src/frontend/mame/ui/tapectrl.cpp b/src/frontend/mame/ui/tapectrl.cpp
index c302577cc63..a80e5856ed1 100644
--- a/src/frontend/mame/ui/tapectrl.cpp
+++ b/src/frontend/mame/ui/tapectrl.cpp
@@ -9,22 +9,47 @@
***************************************************************************/
#include "emu.h"
-
#include "ui/tapectrl.h"
+#include <string_view>
+
+
namespace ui {
+
+namespace {
+
/***************************************************************************
CONSTANTS
***************************************************************************/
-#define TAPECMD_NULL ((void *) 0x0000)
-#define TAPECMD_STOP ((void *) 0x0001)
-#define TAPECMD_PLAY ((void *) 0x0002)
-#define TAPECMD_RECORD ((void *) 0x0003)
-#define TAPECMD_REWIND ((void *) 0x0004)
-#define TAPECMD_FAST_FORWARD ((void *) 0x0005)
-#define TAPECMD_SLIDER ((void *) 0x0006)
-#define TAPECMD_SELECT ((void *) 0x0007)
+#define TAPECMD_NULL ((void *)0x0000)
+#define TAPECMD_STOP ((void *)0x0001)
+#define TAPECMD_PLAY ((void *)0x0002)
+#define TAPECMD_RECORD ((void *)0x0003)
+#define TAPECMD_REWIND ((void *)0x0004)
+#define TAPECMD_FAST_FORWARD ((void *)0x0005)
+#define TAPECMD_SLIDER ((void *)0x0006)
+#define TAPECMD_SELECT ((void *)0x0007)
+
+
+inline std::string_view tape_state_string(cassette_image_device &device)
+{
+ cassette_state const state(device.get_state());
+ if ((state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED)
+ return _("stopped");
+ else if ((state & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY)
+ return ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED) ? _("playing") : _("(playing)");
+ else
+ return ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED) ? _("recording") : _("(recording)");
+}
+
+
+inline uint32_t tape_position_flags(double position, double length)
+{
+ return ((position > 0.0) ? menu::FLAG_LEFT_ARROW : 0U) | ((position < length) ? menu::FLAG_RIGHT_ARROW : 0U);
+}
+
+} // anonymous namespace
/***************************************************************************
@@ -37,9 +62,14 @@ namespace ui {
menu_tape_control::menu_tape_control(mame_ui_manager &mui, render_container &container, cassette_image_device *device)
: menu_device_control<cassette_image_device>(mui, container, device)
+ , m_slider_item_index(-1)
{
set_process_flags(PROCESS_LR_REPEAT);
set_heading(_("Tape Control"));
+
+ if (device)
+ {
+ }
}
@@ -58,40 +88,29 @@ menu_tape_control::~menu_tape_control()
void menu_tape_control::populate()
{
+ m_notifier.reset();
+ m_slider_item_index = -1;
if (current_device())
{
+ // repopulate the menu if an image is mounted or unmounted
+ m_notifier = current_device()->add_media_change_notifier(
+ [this] (device_image_interface::media_change_event ev)
+ {
+ reset(reset_options::REMEMBER_POSITION);
+ });
+
// name of tape
item_append(current_display_name(), current_device()->exists() ? current_device()->filename() : "No Tape Image loaded", current_display_flags(), TAPECMD_SELECT);
if (current_device()->exists())
{
- std::string timepos;
- cassette_state state;
- double t0 = current_device()->get_position();
- double t1 = current_device()->get_length();
- uint32_t tapeflags = 0;
-
- // state
- if (t1 > 0)
- {
- if (t0 > 0)
- tapeflags |= FLAG_LEFT_ARROW;
- if (t0 < t1)
- tapeflags |= FLAG_RIGHT_ARROW;
- }
-
- get_time_string(timepos, current_device(), nullptr, nullptr);
- state = current_device()->get_state();
- item_append(
- (state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED
- ? _("stopped")
- : ((state & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY
- ? ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? _("playing") : _("(playing)"))
- : ((state & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_ENABLED ? _("recording") : _("(recording)"))
- ),
- timepos,
- tapeflags,
- TAPECMD_SLIDER);
+ double const t0 = current_device()->get_position();
+ double const t1 = current_device()->get_length();
+ m_slider_item_index = item_append(
+ std::string(tape_state_string(*current_device())),
+ util::string_format("%04d/%04d", t1 ? int(t0) : 0, int(t1)),
+ tape_position_flags(t0, t1),
+ TAPECMD_SLIDER);
// pause or stop
item_append(_("Pause/Stop"), 0, TAPECMD_STOP);
@@ -127,16 +146,26 @@ bool menu_tape_control::handle(event const *ev)
{
case IPT_UI_LEFT:
if (ev->itemref == TAPECMD_SLIDER)
+ {
current_device()->seek(-1, SEEK_CUR);
+ }
else if (ev->itemref == TAPECMD_SELECT)
+ {
+ m_slider_item_index = -1;
previous();
+ }
break;
case IPT_UI_RIGHT:
if (ev->itemref == TAPECMD_SLIDER)
+ {
current_device()->seek(+1, SEEK_CUR);
+ }
else if (ev->itemref == TAPECMD_SELECT)
+ {
+ m_slider_item_index = -1;
next();
+ }
break;
case IPT_UI_SELECT:
@@ -156,33 +185,18 @@ bool menu_tape_control::handle(event const *ev)
}
}
- // hacky way to update the tape counter by repopulating every frame
- reset(reset_options::REMEMBER_POSITION);
- return false;
-}
-
-
-//-------------------------------------------------
-// get_time_string - returns a textual
-// representation of the time
-//-------------------------------------------------
-
-void menu_tape_control::get_time_string(std::string &dest, cassette_image_device *cassette, int *curpos, int *endpos)
-{
- double t0, t1;
-
- t0 = cassette->get_position();
- t1 = cassette->get_length();
-
- if (t1)
- dest = string_format("%04d/%04d", (int)t0, (int)t1);
- else
- dest = string_format("%04d/%04d", 0, (int)t1);
+ // uupdate counters
+ if ((0 <= m_slider_item_index) && current_device() && current_device()->exists())
+ {
+ menu_item &slider_item(item(m_slider_item_index));
+ double const t0(current_device()->get_position());
+ double const t1(current_device()->get_length());
+ slider_item.set_text(tape_state_string(*current_device()));
+ slider_item.set_subtext(util::string_format("%04d/%04d", t1 ? int(t0) : 0, int(t1)));
+ slider_item.set_flags(tape_position_flags(t0, t1));
+ }
- if (curpos != nullptr)
- *curpos = t0;
- if (endpos != nullptr)
- *endpos = t1;
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/tapectrl.h b/src/frontend/mame/ui/tapectrl.h
index f03fe371dda..a8eef4b3148 100644
--- a/src/frontend/mame/ui/tapectrl.h
+++ b/src/frontend/mame/ui/tapectrl.h
@@ -13,9 +13,13 @@
#pragma once
-#include "imagedev/cassette.h"
#include "ui/devctrl.h"
+#include "imagedev/cassette.h"
+
+#include "notifier.h"
+
+
namespace ui {
class menu_tape_control : public menu_device_control<cassette_image_device>
@@ -28,7 +32,8 @@ private:
virtual void populate() override;
virtual bool handle(event const *ev) override;
- static void get_time_string(std::string &dest, cassette_image_device *cassette, int *curpos, int *endpos);
+ util::notifier_subscription m_notifier;
+ int m_slider_item_index;
};
} // namespace ui
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..223af5e67cf 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,12 +492,12 @@ 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 origx1, float origy1, float origx2, float origy2)
{
std::string_view const toptext[] = { m_heading };
draw_text_box(
std::begin(toptext), std::end(toptext),
- x1, x2, y1 - top, y1 - tb_border(),
+ origx1, origx2, origy1 - top, origy1 - tb_border(),
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
ui().colors().text_color(), UI_GREEN_COLOR);
}
@@ -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..ff8c7a62930 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 origx1, float origy1, float origx2, float origy2) 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 11c9180ab01..a3977a310c0 100644
--- a/src/frontend/mame/ui/ui.cpp
+++ b/src/frontend/mame/ui/ui.cpp
@@ -38,6 +38,7 @@
#include "render.h"
#include "cheat.h"
#include "rendfont.h"
+#include "rendlay.h"
#include "romload.h"
#include "screen.h"
#include "speaker.h"
@@ -47,7 +48,6 @@
#include "../osd/modules/lib/osdlib.h"
#include "../osd/modules/lib/osdobj_common.h"
-#include <chrono>
#include <functional>
#include <type_traits>
@@ -153,6 +153,104 @@ 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)
+ , updated(std::chrono::steady_clock::time_point::min())
+ , 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;
+ std::chrono::steady_clock::time_point updated;
+ osd::ui_event_handler::pointer type;
+ u16 ptrid;
+ float x, y;
+};
+
+
+class mame_ui_manager::pointer_options
+{
+public:
+ pointer_options()
+ : m_initial_timeout(std::chrono::seconds(3))
+ , m_timeout(std::chrono::seconds(3))
+ , m_initial_hide_inactive(true)
+ , m_hide_inactive(true)
+ , m_timeout_set(false)
+ , m_hide_inactive_set(false)
+ {
+ }
+
+ std::chrono::steady_clock::duration timeout() const noexcept { return m_timeout; }
+ bool hide_inactive() const noexcept { return m_hide_inactive; }
+ bool timeout_set() const noexcept { return m_timeout_set; }
+ bool hide_inactive_set() const noexcept { return m_hide_inactive_set; }
+ bool options_set() const noexcept { return m_timeout_set || m_hide_inactive_set; }
+
+ void set_initial_timeout(std::chrono::steady_clock::duration value) noexcept
+ {
+ m_initial_timeout = value;
+ if (!m_timeout_set)
+ m_timeout = value;
+ }
+
+ void set_initial_hide_inactive(bool value) noexcept
+ {
+ m_initial_hide_inactive = value;
+ if (!m_hide_inactive_set)
+ m_hide_inactive = value;
+ }
+
+ void set_timeout(std::chrono::steady_clock::duration value) noexcept
+ {
+ m_timeout = value;
+ m_timeout_set = true;
+ }
+
+ void set_hide_inactive(bool value) noexcept
+ {
+ m_hide_inactive = value;
+ m_hide_inactive_set = true;
+ }
+
+ void restore_initial() noexcept
+ {
+ m_timeout = m_initial_timeout;
+ m_hide_inactive = m_initial_hide_inactive;
+ m_timeout_set = false;
+ m_hide_inactive_set = false;
+ }
+
+private:
+ std::chrono::steady_clock::duration m_initial_timeout;
+ std::chrono::steady_clock::duration m_timeout;
+ bool m_initial_hide_inactive;
+ bool m_hide_inactive;
+ bool m_timeout_set;
+ bool m_hide_inactive_set;
+};
+
+
//-------------------------------------------------
// ctor - set up the user interface
//-------------------------------------------------
@@ -161,7 +259,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)
@@ -170,11 +268,8 @@ mame_ui_manager::mame_ui_manager(running_machine &machine)
, m_popup_text_end(0)
, m_mouse_bitmap(32, 32)
, m_mouse_arrow_texture(nullptr)
- , m_mouse_show(false)
- , m_mouse_target(-1)
- , m_mouse_position(0, 0)
+ , m_pointers_changed(false)
, m_target_font_height(0)
- , m_has_warnings(false)
, m_unthrottle_mute(false)
, m_image_display_enabled(true)
, m_machine_info()
@@ -182,7 +277,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 +299,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
{
@@ -211,14 +307,18 @@ void mame_ui_manager::init()
return 0;
}));
m_non_char_keys_down = std::make_unique<uint8_t[]>((std::size(non_char_keys) + 7) / 8);
- 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",
- configuration_manager::load_delegate(&mame_ui_manager::config_load, this),
- configuration_manager::save_delegate(&mame_ui_manager::config_save, this));
+ configuration_manager::load_delegate(&mame_ui_manager::config_load_warnings, this),
+ configuration_manager::save_delegate(&mame_ui_manager::config_save_warnings, this));
+ machine().configuration().config_register(
+ "pointer_input",
+ configuration_manager::load_delegate(&mame_ui_manager::config_load_pointers, this),
+ configuration_manager::save_delegate(&mame_ui_manager::config_save_pointers, this));
// create mouse bitmap
uint32_t *dst = &m_mouse_bitmap.pix(0);
@@ -239,6 +339,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
//-------------------------------------------------
@@ -257,10 +376,14 @@ void mame_ui_manager::exit()
//-------------------------------------------------
-// config_load - load configuration data
+// config_load_warnings - load info on last time
+// emulation status warnings showed
//-------------------------------------------------
-void mame_ui_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode)
+void mame_ui_manager::config_load_warnings(
+ config_type cfg_type,
+ config_level cfg_level,
+ util::xml::data_node const *parentnode)
{
// make sure it's relevant and there's data available
if (config_type::SYSTEM == cfg_type)
@@ -298,10 +421,13 @@ void mame_ui_manager::config_load(config_type cfg_type, config_level cfg_level,
//-------------------------------------------------
-// config_save - save configuration data
+// config_save_warnings - save information on
+// last time emulation status warnings showed
//-------------------------------------------------
-void mame_ui_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode)
+void mame_ui_manager::config_save_warnings(
+ config_type cfg_type,
+ util::xml::data_node *parentnode)
{
// only save system-level configuration when times are valid
if ((config_type::SYSTEM == cfg_type) && (std::time_t(-1) != m_last_launch_time) && (std::time_t(-1) != m_last_warning_time))
@@ -329,6 +455,103 @@ void mame_ui_manager::config_save(config_type cfg_type, util::xml::data_node *pa
//-------------------------------------------------
+// config_load_pointers - load pointer input
+// settings
+//-------------------------------------------------
+
+void mame_ui_manager::config_load_pointers(
+ config_type cfg_type,
+ config_level cfg_level,
+ util::xml::data_node const *parentnode)
+{
+ switch (cfg_type)
+ {
+ case config_type::INIT:
+ {
+ int last(-1);
+ for (auto const &target : machine().render().targets())
+ {
+ assert(target.index() >= 0);
+ if (!target.hidden())
+ last = (std::max)(target.index(), last);
+ }
+ m_pointer_options.resize(last + 1);
+ }
+ break;
+
+ case config_type::CONTROLLER:
+ case config_type::SYSTEM:
+ if (!parentnode)
+ break;
+ for (auto const *targetnode = parentnode->get_child("target"); targetnode; targetnode = targetnode->get_next_sibling("target"))
+ {
+ auto const index(targetnode->get_attribute_int("index", -1));
+ if ((0 <= index) && (m_pointer_options.size() > index))
+ {
+ auto const timeout(targetnode->get_attribute_float("activity_timeout", -1.0F));
+ auto const ms(std::lround(timeout * 1000.0F));
+ if ((0 <= ms) && (10'000 >= ms))
+ {
+ if (config_type::SYSTEM == cfg_type)
+ m_pointer_options[index].set_timeout(std::chrono::milliseconds(ms));
+ else
+ m_pointer_options[index].set_initial_timeout(std::chrono::milliseconds(ms));
+ }
+
+ auto const hide(targetnode->get_attribute_int("hide_inactive", -1));
+ if (0 <= hide)
+ {
+ if (config_type::SYSTEM == cfg_type)
+ m_pointer_options[index].set_hide_inactive(hide != 0);
+ else
+ m_pointer_options[index].set_initial_hide_inactive(hide != 0);
+ }
+ }
+ }
+ break;
+
+ case config_type::DEFAULT:
+ case config_type::FINAL:
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// config_save_pointers - save pointer input
+// settings
+//-------------------------------------------------
+
+void mame_ui_manager::config_save_pointers(
+ config_type cfg_type,
+ util::xml::data_node *parentnode)
+{
+ if (config_type::SYSTEM == cfg_type)
+ {
+ for (std::size_t i = 0; m_pointer_options.size() > i; ++i)
+ {
+ pointer_options const &options(m_pointer_options[i]);
+ if (options.options_set())
+ {
+ util::xml::data_node *const targetnode = parentnode->add_child("target", nullptr);
+ if (targetnode)
+ {
+ targetnode->set_attribute_int("index", i);
+ if (options.timeout_set())
+ {
+ auto const ms(std::chrono::duration_cast<std::chrono::milliseconds>(options.timeout()));
+ targetnode->set_attribute_float("activity_timeout", float(ms.count()) * 0.001F);
+ }
+ if (options.hide_inactive_set())
+ targetnode->set_attribute_int("hide_inactive", options.hide_inactive());
+ }
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
// initialize - initialize ui lists
//-------------------------------------------------
@@ -366,6 +589,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;
}
@@ -399,15 +628,15 @@ static void output_joined_collection(const TColl &collection, TEmitMemberFunc em
void mame_ui_manager::display_startup_screens(bool first_time)
{
const int maxstate = 3;
- int str = machine().options().seconds_to_run();
+ int const str = machine().options().seconds_to_run();
bool show_gameinfo = !machine().options().skip_gameinfo();
- bool show_warnings = true, show_mandatory_fileman = true;
+ bool show_warnings = true;
bool video_none = strcmp(downcast<osd_options &>(machine().options()).video(), OSDOPTVAL_NONE) == 0;
// disable everything if we are using -str for 300 or fewer seconds, or if we're the empty driver,
// or if we are debugging, or if there's no mame window to send inputs to
- if (!first_time || (str > 0 && str < 60*5) || &machine().system() == &GAME_NAME(___empty) || (machine().debug_flags & DEBUG_FLAG_ENABLED) != 0 || video_none)
- show_gameinfo = show_warnings = show_mandatory_fileman = false;
+ if (!first_time || (str > 0 && str < 60*5) || &machine().system() == &GAME_NAME(___empty) || (machine().debug_flags & DEBUG_FLAG_ENABLED) || video_none)
+ show_gameinfo = show_warnings = false;
#if defined(__EMSCRIPTEN__)
// also disable for the JavaScript port since the startup screens do not run asynchronously
@@ -467,12 +696,10 @@ void mame_ui_manager::display_startup_screens(bool first_time)
break;
case 1:
- warning_text = machine_info().warnings_string();
- m_has_warnings = !warning_text.empty();
if (show_warnings)
{
- bool need_warning = m_has_warnings;
- if (machine_info().has_severe_warnings() || !m_has_warnings)
+ bool need_warning = machine_info().has_warnings();
+ if (machine_info().has_severe_warnings() || !machine_info().has_warnings())
{
// critical warnings - no need to persist stuff
m_unemulated_features.clear();
@@ -487,6 +714,8 @@ void mame_ui_manager::display_startup_screens(bool first_time)
for (device_t &device : device_enumerator(machine().root_device()))
{
device_t::feature_type unemulated = device.type().unemulated_features();
+ if ((&device != &machine().root_device()) && (device.type().emulation_flags() & device_t::flags::NOT_WORKING))
+ unemulated_features.emplace(device.type().shortname(), "functionality");
for (std::underlying_type_t<device_t::feature_type> feature = 1U; unemulated; feature <<= 1)
{
if (unemulated & feature)
@@ -534,6 +763,7 @@ void mame_ui_manager::display_startup_screens(bool first_time)
}
if (need_warning)
{
+ warning_text = machine_info().warnings_string();
warning_text.append(_("\n\nPress any key to continue"));
set_handler(ui_callback_type::MODAL, handler_callback_func(handler_messagebox_anykey));
warning_color = machine_info().warnings_color();
@@ -543,16 +773,25 @@ void mame_ui_manager::display_startup_screens(bool first_time)
case 2:
std::vector<std::reference_wrapper<const std::string>> mandatory_images = mame_machine_manager::instance()->missing_mandatory_images();
- if (!mandatory_images.empty() && show_mandatory_fileman)
+ if (!mandatory_images.empty())
{
std::ostringstream warning;
- warning << _("This system requires media images to be mounted for the following device(s): ");
+ if ((str > 0) || (machine().debug_flags & DEBUG_FLAG_ENABLED) || video_none)
+ {
+ warning << "Images must be mounted for the following devices: ";
+ output_joined_collection(mandatory_images,
+ [&warning] (const std::reference_wrapper<const std::string> &img) { warning << img.get(); },
+ [&warning] () { warning << ", "; });
+
+ throw emu_fatalerror(std::move(warning).str());
+ }
+ warning << _("This system requires media images to be mounted for the following device(s): ");
output_joined_collection(mandatory_images,
- [&warning](const std::reference_wrapper<const std::string> &img) { warning << "\"" << img.get() << "\""; },
- [&warning]() { warning << ","; });
+ [&warning] (const std::reference_wrapper<const std::string> &img) { warning << '"' << img.get() << '"'; },
+ [&warning] () { warning << ", "; });
- ui::menu_file_manager::force_file_manager(*this, machine().render().ui_container(), warning.str().c_str());
+ ui::menu_file_manager::force_file_manager(*this, machine().render().ui_container(), std::move(warning).str());
}
break;
}
@@ -576,7 +815,7 @@ void mame_ui_manager::display_startup_screens(bool first_time)
}
// update last launch time if this was a run that was eligible for emulation warnings
- if (m_has_warnings && show_warnings && !machine().scheduled_event_pending())
+ if (machine_info().has_warnings() && show_warnings && !machine().scheduled_event_pending())
m_last_launch_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// if we're the empty driver, force the menus on
@@ -625,7 +864,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()))
@@ -639,25 +882,16 @@ bool mame_ui_manager::update_and_render(render_container &container)
container.add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(alpha,0x00,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
- // show red if overdriving sound
- if (machine().options().speaker_report() != 0 && machine().phase() == machine_phase::RUNNING)
- {
- auto compressor = machine().sound().compressor_scale();
- if (compressor < 1.0)
- {
- float width = 0.05f + std::min(0.15f, (1.0f - compressor) * 0.4f);
- container.add_rect(0.0f, 0.0f, 1.0f, width, rgb_t(0xc0,0xff,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- container.add_rect(0.0f, 1.0f - width, 1.0f, 1.0f, rgb_t(0xc0,0xff,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- container.add_rect(0.0f, width, width, 1.0f - width, rgb_t(0xc0,0xff,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- container.add_rect(1.0f - width, width, 1.0f, 1.0f - width, rgb_t(0xc0,0xff,0x00,0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- }
- }
-
// render any cheat stuff at the bottom
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 +900,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;
- if (m_mouse_show || (is_menu_active() && machine().options().ui_mouse()))
+ // display the internal pointers
+ bool const pointer_update = m_pointers_changed;
+ m_pointers_changed = false;
+ if (!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;
- }
- }
- else if (0 <= m_mouse_target)
- {
- m_mouse_target = -1;
- 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;
- }
// 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 +1179,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 +1191,7 @@ void mame_ui_manager::set_show_fps(bool show)
{
m_showfps = show;
if (!show)
- {
- m_showfps = 0;
m_showfps_end = 0;
- }
}
@@ -990,7 +1212,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 +1247,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 +1262,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 +1274,113 @@ 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)
+ {
+ 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);
+ pos->updated = std::chrono::steady_clock::now();
+ 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_active_pointers.erase(pos);
+
+ 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;
- if (pressed && !(*key_down_ptr & key_down_mask))
- {
- // this key is now down
- *key_down_ptr |= key_down_mask;
+ 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_active_pointers.erase(pos);
+
+ 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;
- // post the key
- machine().natkeyboard().post_char(UCHAR_MAMEKEY_BEGIN + code.item_id());
+ 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 +1508,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 +1541,31 @@ 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();
+ display_pointer_vector pointers;
+ pointers.reserve(m_active_pointers.size());
+ auto const now(std::chrono::steady_clock::now());
+ auto expiry(now);
+ render_target *target(nullptr);
+ layout_view const *view(nullptr);
+ bool hide_inactive(true);
+ for (auto const &pointer : m_active_pointers)
+ {
+ if (pointer.target != target)
+ {
+ target = pointer.target;
+ view = &target->current_view();
+ hide_inactive = m_pointer_options[target->index()].hide_inactive() && view->hide_inactive_pointers();
+ expiry = now - m_pointer_options[target->index()].timeout();
+ }
+ if (view->show_pointers())
+ {
+ if (!hide_inactive || (osd::ui_event_handler::pointer::PEN == pointer.type) || (pointer.updated >= expiry))
+ pointers.emplace_back(display_pointer{ *target, pointer.type, pointer.x, pointer.y });
+ }
+ }
+ set_pointers(pointers.begin(), pointers.end());
if (!ui_disabled)
{
@@ -1309,11 +1609,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
{
@@ -1356,6 +1662,20 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
return 0;
}
+ // handle a quick save state request
+ if (machine().ui_input().pressed(IPT_UI_SAVE_STATE_QUICK))
+ {
+ machine().schedule_save("quick");
+ return 0;
+ }
+
+ // handle a quick load state request
+ if (machine().ui_input().pressed(IPT_UI_LOAD_STATE_QUICK))
+ {
+ machine().schedule_load("quick");
+ return 0;
+ }
+
// handle a save snapshot request
if (machine().ui_input().pressed(IPT_UI_SNAPSHOT))
machine().video().save_active_screen_snapshots();
@@ -1378,7 +1698,7 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
// handle a toggle cheats request
if (machine().ui_input().pressed(IPT_UI_TOGGLE_CHEAT))
- mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled());
+ mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled(), true);
// toggle MNG recording
if (machine().ui_input().pressed(IPT_UI_RECORD_MNG))
@@ -1444,6 +1764,76 @@ void mame_ui_manager::request_quit()
}
+//-------------------------------------------------
+// set_pointer_activity_timeout - set per-target
+// pointer activity timeout
+//-------------------------------------------------
+
+void mame_ui_manager::set_pointer_activity_timeout(int target, std::chrono::steady_clock::duration timeout) noexcept
+{
+ assert((0 <= target) && (m_pointer_options.size() > target));
+ if ((0 <= target) && (m_pointer_options.size() > target))
+ m_pointer_options[target].set_timeout(timeout);
+}
+
+
+//-------------------------------------------------
+// set_hide_inactive_pointers - set per-target
+// hide inactive pointers setting
+//-------------------------------------------------
+
+void mame_ui_manager::set_hide_inactive_pointers(int target, bool hide) noexcept
+{
+ assert((0 <= target) && (m_pointer_options.size() > target));
+ if ((0 <= target) && (m_pointer_options.size() > target))
+ m_pointer_options[target].set_hide_inactive(hide);
+}
+
+
+//-------------------------------------------------
+// restore_initial_pointer_options - restore
+// initial per-target pointer settings
+//-------------------------------------------------
+
+void mame_ui_manager::restore_initial_pointer_options(int target) noexcept
+{
+ assert((0 <= target) && (m_pointer_options.size() > target));
+ if ((0 <= target) && (m_pointer_options.size() > target))
+ m_pointer_options[target].restore_initial();
+}
+
+
+//-------------------------------------------------
+// pointer_activity_timeout - get per-target
+// pointer activity timeout
+//-------------------------------------------------
+
+std::chrono::steady_clock::duration mame_ui_manager::pointer_activity_timeout(int target) const noexcept
+{
+ assert((0 <= target) && (m_pointer_options.size() > target));
+ if ((0 <= target) && (m_pointer_options.size() > target))
+ return m_pointer_options[target].timeout();
+ else
+ return pointer_options().timeout();
+}
+
+
+//-------------------------------------------------
+// hide_inactive_pointers - get per-target hide
+// inactive pointers setting
+//-------------------------------------------------
+
+bool mame_ui_manager::hide_inactive_pointers(int target) const noexcept
+{
+ assert((0 <= target) && (m_pointer_options.size() > target));
+ if ((0 <= target) && (m_pointer_options.size() > target))
+ return m_pointer_options[target].hide_inactive();
+ else
+ return pointer_options().hide_inactive();
+}
+
+
+
/***************************************************************************
SLIDER CONTROLS
***************************************************************************/
@@ -1470,22 +1860,19 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
m_sliders.clear();
// add overall volume
- slider_alloc(_("Master Volume"), -32, 0, 0, 1, std::bind(&mame_ui_manager::slider_volume, this, _1, _2));
+ slider_alloc(_("Master Volume"), -960, 0, 120, 10, std::bind(&mame_ui_manager::slider_volume, this, _1, _2));
- // add per-channel volume
- mixer_input info;
- for (int item = 0; machine.sound().indexed_mixer_input(item, info); item++)
+ // add per-sound device and per-sound device channel volume
+ for (device_sound_interface &snd : sound_interface_enumerator(machine.root_device()))
{
- std::string str = string_format(_("%1$s Volume"), info.stream->input(info.inputnum).name());
- slider_alloc(std::move(str), 0, 1000, 4000, 20, std::bind(&mame_ui_manager::slider_mixervol, this, item, _1, _2));
- }
+ // Don't add microphones, speakers or devices without outputs
+ if (dynamic_cast<sound_io_device *>(&snd) || !snd.outputs())
+ continue;
- // add speaker panning
- for (speaker_device &speaker : speaker_device_enumerator(machine.root_device()))
- {
- int defpan = floorf(speaker.defpan() * 1000.0f + 0.5f);
- std::string str = string_format(_("%s '%s' Panning"), speaker.name(), speaker.tag());
- slider_alloc(std::move(str), -1000, defpan, 1000, 20, std::bind(&mame_ui_manager::slider_panning, this, std::ref(speaker), _1, _2));
+ slider_alloc(util::string_format(_("%1$s volume"), snd.device().tag()), -960, 0, 120, 10, std::bind(&mame_ui_manager::slider_devvol, this, &snd, _1, _2));
+ if (snd.outputs() != 1)
+ for (int channel = 0; channel != snd.outputs(); channel ++)
+ slider_alloc(util::string_format(_("%1$s channel %d volume"), snd.device().tag(), channel), -960, 0, 120, 10, std::bind(&mame_ui_manager::slider_devvol_chan, this, &snd, channel, _1, _2));
}
// add analog adjusters
@@ -1628,84 +2015,74 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
int32_t mame_ui_manager::slider_volume(std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- machine().sound().set_attenuation(newval);
+ machine().sound().set_master_gain(newval == -960 ? 0 : osd::db_to_linear(newval * 0.1f));
- int32_t curval = machine().sound().attenuation();
- if (str)
- *str = string_format(_(u8"%1$3d\u00a0dB"), curval);
+ int curval = machine().sound().master_gain() == 0 ? -960 : floorf(osd::linear_to_db(machine().sound().master_gain()) * 10.0f + 0.5f);
+ if (str)
+ {
+ if (curval == -960)
+ *str = _("Mute");
+ else if (curval % 10)
+ *str = string_format(_(u8"%1$5.1f\u00a0dB"), float(curval) * 0.1f);
+ else
+ *str = string_format(_(u8"%1$3d\u00a0dB"), curval / 10);
+ }
return curval;
}
//-------------------------------------------------
-// slider_mixervol - single channel volume
+// slider_devvol - device volume
// slider callback
//-------------------------------------------------
-int32_t mame_ui_manager::slider_mixervol(int item, std::string *str, int32_t newval)
+int32_t mame_ui_manager::slider_devvol(device_sound_interface *snd, std::string *str, int32_t newval)
{
- mixer_input info;
- if (!machine().sound().indexed_mixer_input(item, info))
- return 0;
-
if (newval != SLIDER_NOCHANGE)
- info.stream->input(info.inputnum).set_user_gain(float(newval) * 0.001f);
+ snd->set_user_output_gain(newval == -960 ? 0 : osd::db_to_linear(newval * 0.1f));
+
+ int curval = snd->user_output_gain() == 0 ? -960 : floorf(osd::linear_to_db(snd->user_output_gain()) * 10.0f + 0.5f);
- int32_t curval = floorf(info.stream->input(info.inputnum).user_gain() * 1000.0f + 0.5f);
if (str)
{
- if (curval == 0)
+ if (curval == -960)
*str = _("Mute");
else if (curval % 10)
- *str = string_format(_("%1$.1f%%"), float(curval) * 0.1f);
+ *str = string_format(_(u8"%1$5.1f\u00a0dB"), float(curval) * 0.1f);
else
- *str = string_format(_("%1$3d%%"), curval / 10);
+ *str = string_format(_(u8"%1$3d\u00a0dB"), curval / 10);
}
-
return curval;
}
//-------------------------------------------------
-// slider_panning - speaker panning slider
-// callback
+// slider_devvol_chan - device channel volume
+// slider callback
//-------------------------------------------------
-int32_t mame_ui_manager::slider_panning(speaker_device &speaker, std::string *str, int32_t newval)
+int32_t mame_ui_manager::slider_devvol_chan(device_sound_interface *snd, int channel, std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- speaker.set_pan(float(newval) * 0.001f);
+ snd->set_user_output_gain(channel, newval == -960 ? 0 : osd::db_to_linear(newval * 0.1f));
+
+ int curval = snd->user_output_gain(channel) == 0 ? -960 : floorf(osd::linear_to_db(snd->user_output_gain(channel)) * 10.0f + 0.5f);
- int32_t curval = floorf(speaker.pan() * 1000.0f + 0.5f);
if (str)
{
- switch (curval)
- {
- // preset strings for exact center/left/right
- case 0:
- *str = _("Center");
- break;
-
- case -1000:
- *str = _("Left");
- break;
-
- case 1000:
- *str = _("Right");
- break;
-
- // otherwise show as floating point
- default:
- *str = string_format(_("%1$.3f"), float(curval) * 0.001f);
- break;
- }
+ if (curval == -960)
+ *str = _("Mute");
+ else if (curval % 10)
+ *str = string_format(_(u8"%1$5.1f\u00a0dB"), float(curval) * 0.1f);
+ else
+ *str = string_format(_(u8"%1$3d\u00a0dB"), curval / 10);
}
-
return curval;
}
+
//-------------------------------------------------
// slider_adjuster - analog adjuster slider
// callback
@@ -2252,6 +2629,29 @@ 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;
+
+ for (auto *target = machine().render().first_target(); target; target = target->next())
+ {
+ if (!target->hidden())
+ target->forget_pointers();
+ }
+
+ 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..ed5fa97156d 100644
--- a/src/frontend/mame/ui/ui.h
+++ b/src/frontend/mame/ui/ui.h
@@ -13,18 +13,24 @@
#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 <chrono>
#include <ctime>
+#include <functional>
#include <set>
+#include <string>
#include <string_view>
#include <typeindex>
#include <typeinfo>
@@ -63,14 +69,6 @@ class laserdisc_device;
class mame_ui_manager;
-enum class ui_callback_type
-{
- GENERAL,
- MODAL,
- MENU,
- VIEWER
-};
-
// ======================> ui_colors
class ui_colors
@@ -132,6 +130,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();
@@ -155,13 +165,21 @@ public:
void display_startup_screens(bool first_time);
virtual void set_startup_text(const char *text, bool force) override;
bool update_and_render(render_container &container);
+
+ // getting display font and metrics
render_font *get_font();
float get_line_height(float scale = 1.0F);
+ float target_font_height() const { return m_target_font_height; }
float get_char_width(char32_t ch);
float get_string_width(std::string_view s);
float get_string_width(std::string_view s, float text_size);
+ float box_lr_border() const { return target_font_height() * 0.25f; }
+ float box_tb_border() const { return target_font_height() * 0.25f; }
+
+ // drawing boxes and text
void draw_outlined_box(render_container &container, float x0, float y0, float x1, float y1, rgb_t backcolor);
void draw_outlined_box(render_container &container, float x0, float y0, float x1, float y1, rgb_t fgcolor, rgb_t bgcolor);
+ void draw_textured_box(render_container &container, float x0, float y0, float x1, float y1, rgb_t backcolor, rgb_t linecolor, render_texture *texture = nullptr, uint32_t flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
void draw_text(render_container &container, std::string_view buf, float x, float y);
void draw_text_full(render_container &container, std::string_view origs, float x, float y, float origwrapwidth, ui::text_layout::text_justify justify, ui::text_layout::word_wrapping wrap, draw_mode draw, rgb_t fgcolor, rgb_t bgcolor, float *totalwidth = nullptr, float *totalheight = nullptr);
void draw_text_full(render_container &container, std::string_view origs, float x, float y, float origwrapwidth, ui::text_layout::text_justify justify, ui::text_layout::word_wrapping wrap, draw_mode draw, rgb_t fgcolor, rgb_t bgcolor, float *totalwidth, float *totalheight, float text_size);
@@ -184,37 +202,66 @@ 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; }
void image_handler_ingame();
- void increase_frameskip();
- void decrease_frameskip();
void request_quit();
+ void set_pointer_activity_timeout(int target, std::chrono::steady_clock::duration timeout) noexcept;
+ void set_hide_inactive_pointers(int target, bool hide) noexcept;
+ void restore_initial_pointer_options(int target) noexcept;
+ std::chrono::steady_clock::duration pointer_activity_timeout(int target) const noexcept;
+ bool hide_inactive_pointers(int target) const noexcept;
+
+ // drawing informational overlays
void draw_fps_counter(render_container &container);
void draw_profiler(render_container &container);
+ // pointer display for UI handlers
+ 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; }
- float box_lr_border() const { return target_font_height() * 0.25f; }
- float box_tb_border() const { return target_font_height() * 0.25f; }
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; }
-
- // draw an outlined box with given line color and filled with a texture
- void draw_textured_box(render_container &container, float x0, float y0, float x1, float y1, rgb_t backcolor, rgb_t linecolor, render_texture *texture = nullptr, uint32_t flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
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 +279,17 @@ 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;
+ class pointer_options;
+
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 pointer_options_vector = std::vector<pointer_options>;
+ using display_pointer_vector = std::vector<display_pointer>;
// instance variables
std::unique_ptr<render_font> m_font;
@@ -248,16 +303,16 @@ private:
osd_ticks_t m_popup_text_end;
std::unique_ptr<uint8_t []> m_non_char_keys_down;
+ pointer_options_vector m_pointer_options;
+ 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_pointers_changed;
ui_options m_ui_options;
ui_colors m_ui_colors;
float m_target_font_height;
- bool m_has_warnings;
bool m_unthrottle_mute;
bool m_image_display_enabled;
@@ -280,15 +335,20 @@ 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);
+ void increase_frameskip();
+ void decrease_frameskip();
+ void config_load_warnings(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
+ void config_save_warnings(config_type cfg_type, util::xml::data_node *parentnode);
+ void config_load_pointers(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
+ void config_save_pointers(config_type cfg_type, util::xml::data_node *parentnode);
template <typename... Params> void slider_alloc(Params &&...args) { m_sliders.push_back(std::make_unique<slider_state>(std::forward<Params>(args)...)); }
// slider controls
int32_t slider_volume(std::string *str, int32_t newval);
- int32_t slider_mixervol(int item, std::string *str, int32_t newval);
- int32_t slider_panning(speaker_device &speaker, std::string *str, int32_t newval);
+ int32_t slider_devvol(device_sound_interface *snd, std::string *str, int32_t newval);
+ int32_t slider_devvol_chan(device_sound_interface *snd, int channel, std::string *str, int32_t newval);
int32_t slider_adjuster(ioport_field &field, std::string *str, int32_t newval);
int32_t slider_overclock(device_t &device, std::string *str, int32_t newval);
int32_t slider_refresh(screen_device &screen, std::string *str, int32_t newval);
diff --git a/src/frontend/mame/ui/utils.cpp b/src/frontend/mame/ui/utils.cpp
index b83abc5c277..8acd4a70b5e 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(); }
@@ -687,7 +680,7 @@ class working_machine_filter_impl : public simple_filter_impl_base<machine_filte
public:
working_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
- virtual bool apply(ui_system_info const &system) const override { return !(system.driver->flags & machine_flags::NOT_WORKING); }
+ virtual bool apply(ui_system_info const &system) const override { return !(system.driver->type.emulation_flags() & device_t::flags::NOT_WORKING); }
};
@@ -750,7 +743,7 @@ class save_machine_filter_impl : public simple_filter_impl_base<machine_filter,
public:
save_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
- virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::SUPPORTS_SAVE; }
+ virtual bool apply(ui_system_info const &system) const override { return !(system.driver->type.emulation_flags() & device_t::flags::SAVE_UNSUPPORTED); }
};
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/videoopt.cpp b/src/frontend/mame/ui/videoopt.cpp
index ae5098b9064..5b8afb77fbf 100644
--- a/src/frontend/mame/ui/videoopt.cpp
+++ b/src/frontend/mame/ui/videoopt.cpp
@@ -11,9 +11,12 @@
#include "emu.h"
#include "ui/videoopt.h"
+#include "rendfont.h"
#include "rendlay.h"
#include "rendutil.h"
+#include <chrono>
+
namespace ui {
@@ -23,6 +26,7 @@ constexpr uintptr_t ITEM_ROTATE = 0x00000100;
constexpr uintptr_t ITEM_ZOOM = 0x00000101;
constexpr uintptr_t ITEM_UNEVENSTRETCH = 0x00000102;
constexpr uintptr_t ITEM_KEEPASPECT = 0x00000103;
+constexpr uintptr_t ITEM_POINTERTIMEOUT = 0x00000104;
constexpr uintptr_t ITEM_TOGGLE_FIRST = 0x00000200;
constexpr uintptr_t ITEM_VIEW_FIRST = 0x00000300;
@@ -121,7 +125,7 @@ void menu_video_options::populate()
if (!m_snapshot || !machine().video().snap_native())
{
for (char const *name = m_target.view_name(ref = 0); name; name = m_target.view_name(++ref))
- item_append(name, 0, reinterpret_cast<void *>(ITEM_VIEW_FIRST + ref));
+ item_append(name, convert_command_glyph(ref == m_target.view() ? "_>" : "_<"), 0, reinterpret_cast<void *>(ITEM_VIEW_FIRST + ref));
item_append(menu_item_type::SEPARATOR);
}
@@ -191,6 +195,32 @@ void menu_video_options::populate()
item_append_on_off(_("Maintain Aspect Ratio"), m_target.keepaspect(), 0, reinterpret_cast<void *>(ITEM_KEEPASPECT));
}
+ // add pointer display options
+ if (!m_target.hidden())
+ {
+ item_append(menu_item_type::SEPARATOR);
+
+ // use millisecond precision for timeout display
+ auto const timeout = std::chrono::duration_cast<std::chrono::milliseconds>(ui().pointer_activity_timeout(m_target.index()));
+ bool const hide = ui().hide_inactive_pointers(m_target.index());
+ if (hide)
+ {
+ if (timeout.count())
+ {
+ int const precision = (timeout.count() % 10) ? 3 : (timeout.count() % 100) ? 2 : 1;
+ item_append(
+ _("Hide Inactive Pointers After Delay"),
+ util::string_format(_("%1$.*2$f s"), timeout.count() * 1e-3, precision),
+ ((timeout >= std::chrono::milliseconds(100)) ? FLAG_LEFT_ARROW : 0) | FLAG_RIGHT_ARROW,
+ reinterpret_cast<void *>(ITEM_POINTERTIMEOUT));
+ }
+ else
+ item_append(_("Hide Inactive Pointers After Delay"), _("Always"), FLAG_RIGHT_ARROW, reinterpret_cast<void *>(ITEM_POINTERTIMEOUT));
+ }
+ else
+ item_append(_("Hide Inactive Pointers After Delay"), _("Never"), FLAG_LEFT_ARROW, reinterpret_cast<void *>(ITEM_POINTERTIMEOUT));
+ }
+
item_append(menu_item_type::SEPARATOR);
}
@@ -210,6 +240,7 @@ bool menu_video_options::handle(event const *ev)
});
bool const snap_lockout(m_snapshot && machine().video().is_recording());
bool changed(false);
+ set_process_flags((reinterpret_cast<uintptr_t>(get_selection_ref()) == ITEM_POINTERTIMEOUT) ? PROCESS_LR_REPEAT : 0);
// process the menu
if (ev && uintptr_t(ev->itemref))
@@ -305,7 +336,7 @@ bool menu_video_options::handle(event const *ev)
}
break;
- // keep aspect handles left/right keys the same (toggle)
+ // keep aspect handles left/right keys identically (toggle)
case ITEM_KEEPASPECT:
if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT))
{
@@ -316,6 +347,71 @@ bool menu_video_options::handle(event const *ev)
}
break;
+ // pointer inactivity timeout
+ case ITEM_POINTERTIMEOUT:
+ switch (ev->iptkey)
+ {
+ // decrease value
+ case IPT_UI_LEFT:
+ if (!ui().hide_inactive_pointers(m_target.index()))
+ {
+ ui().set_hide_inactive_pointers(m_target.index(), true);
+ ui().set_pointer_activity_timeout(m_target.index(), std::chrono::milliseconds(10'000));
+ changed = true;
+ }
+ else
+ {
+ auto timeout = ui().pointer_activity_timeout(m_target.index());
+ if (timeout >= std::chrono::milliseconds(100))
+ {
+ bool const shift_pressed = machine().input().code_pressed(KEYCODE_LSHIFT) || machine().input().code_pressed(KEYCODE_RSHIFT);
+ std::chrono::milliseconds const increment(shift_pressed ? 100 : 1'000);
+ auto const remainder = timeout % increment;
+ timeout -= remainder.count() ? remainder : increment;
+ ui().set_pointer_activity_timeout(m_target.index(), timeout);
+ changed = true;
+
+ if (!timeout.count())
+ machine().popmessage(_("Clickable artwork is still active when pointer is hidden."));
+ }
+ }
+ break;
+
+ // increase value
+ case IPT_UI_RIGHT:
+ if (ui().hide_inactive_pointers(m_target.index()))
+ {
+ auto const timeout = ui().pointer_activity_timeout(m_target.index());
+ if (std::chrono::milliseconds(10'000) <= timeout)
+ {
+ ui().set_hide_inactive_pointers(m_target.index(), false);
+ }
+ else
+ {
+ bool const shift_pressed = machine().input().code_pressed(KEYCODE_LSHIFT) || machine().input().code_pressed(KEYCODE_RSHIFT);
+ int const increment(shift_pressed ? 100 : 1'000);
+ ui().set_pointer_activity_timeout(
+ m_target.index(),
+ std::chrono::milliseconds((1 + (timeout / std::chrono::milliseconds(increment))) * increment));
+ }
+ changed = true;
+ }
+ break;
+
+ // toggle hide after delay
+ case IPT_UI_SELECT:
+ ui().set_hide_inactive_pointers(m_target.index(), !ui().hide_inactive_pointers(m_target.index()));
+ changed = true;
+ break;
+
+ // restore initial setting
+ case IPT_UI_CLEAR:
+ ui().restore_initial_pointer_options(m_target.index());
+ changed = true;
+ break;
+ }
+ break;
+
// anything else is a view item
default:
if (reinterpret_cast<uintptr_t>(ev->itemref) >= ITEM_VIEW_FIRST)
diff --git a/src/frontend/mame/ui/viewgfx.cpp b/src/frontend/mame/ui/viewgfx.cpp
index c4a6ecdbf68..1d772da645d 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;
@@ -940,7 +1032,7 @@ uint32_t gfx_viewer::handle_palette(mame_ui_manager &mui, render_container &cont
if (index < total)
{
rgb_t const col = indirect ? palette.indirect_color(index) : raw_color[index];
- if (palette.indirect_entries() && indirect)
+ if (palette.indirect_entries() && !indirect)
{
util::stream_format(title_buf,
_("gfxview", u8" #%1$X \u2192 %2$X (A:%3$02X R:%4$02X G:%5$02X B:%6$02X)"),
@@ -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/frontend/mame/ui/widgets.cpp b/src/frontend/mame/ui/widgets.cpp
index 59fda0f203c..372a4df7331 100644
--- a/src/frontend/mame/ui/widgets.cpp
+++ b/src/frontend/mame/ui/widgets.cpp
@@ -24,7 +24,7 @@ namespace ui {
//-------------------------------------------------
widgets_manager::widgets_manager(running_machine &machine)
- : m_hilight_bitmap(std::make_unique<bitmap_argb32>(256, 1))
+ : m_hilight_bitmap(std::make_unique<bitmap_argb32>(512, 1))
, m_hilight_texture(nullptr, machine.render())
, m_hilight_main_bitmap(std::make_unique<bitmap_argb32>(1, 128))
, m_hilight_main_texture(nullptr, machine.render())
@@ -33,9 +33,9 @@ widgets_manager::widgets_manager(running_machine &machine)
render_manager &render(machine.render());
// create a texture for hilighting items
- for (unsigned x = 0; x < 256; ++x)
+ for (unsigned x = 0; x < 512; ++x)
{
- unsigned const alpha((x < 25) ? (0xff * x / 25) : (x >(256 - 25)) ? (0xff * (255 - x) / 25) : 0xff);
+ unsigned const alpha((x < 50) ? ((x + 1) * 5) : (x > (511 - 50)) ? ((512 - x) * 5) : 0xff);
m_hilight_bitmap->pix(0, x) = rgb_t(alpha, 0xff, 0xff, 0xff);
}
m_hilight_texture.reset(render.texture_alloc());