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.cpp36
-rw-r--r--src/frontend/mame/ui/about.h6
-rw-r--r--src/frontend/mame/ui/analogipt.cpp653
-rw-r--r--src/frontend/mame/ui/analogipt.h37
-rw-r--r--src/frontend/mame/ui/auditmenu.cpp37
-rw-r--r--src/frontend/mame/ui/auditmenu.h9
-rw-r--r--src/frontend/mame/ui/barcode.cpp109
-rw-r--r--src/frontend/mame/ui/barcode.h10
-rw-r--r--src/frontend/mame/ui/cheatopt.cpp178
-rw-r--r--src/frontend/mame/ui/cheatopt.h5
-rw-r--r--src/frontend/mame/ui/confswitch.cpp238
-rw-r--r--src/frontend/mame/ui/confswitch.h11
-rw-r--r--src/frontend/mame/ui/custui.cpp693
-rw-r--r--src/frontend/mame/ui/custui.h36
-rw-r--r--src/frontend/mame/ui/datmenu.cpp321
-rw-r--r--src/frontend/mame/ui/datmenu.h23
-rw-r--r--src/frontend/mame/ui/devctrl.h10
-rw-r--r--src/frontend/mame/ui/devopt.cpp39
-rw-r--r--src/frontend/mame/ui/devopt.h3
-rw-r--r--src/frontend/mame/ui/dirmenu.cpp458
-rw-r--r--src/frontend/mame/ui/dirmenu.h4
-rw-r--r--src/frontend/mame/ui/filecreate.cpp141
-rw-r--r--src/frontend/mame/ui/filecreate.h24
-rw-r--r--src/frontend/mame/ui/filemngr.cpp145
-rw-r--r--src/frontend/mame/ui/filemngr.h20
-rw-r--r--src/frontend/mame/ui/filesel.cpp358
-rw-r--r--src/frontend/mame/ui/filesel.h71
-rw-r--r--src/frontend/mame/ui/floppycntrl.cpp106
-rw-r--r--src/frontend/mame/ui/floppycntrl.h3
-rw-r--r--src/frontend/mame/ui/icorender.cpp122
-rw-r--r--src/frontend/mame/ui/icorender.h8
-rw-r--r--src/frontend/mame/ui/imgcntrl.cpp133
-rw-r--r--src/frontend/mame/ui/imgcntrl.h11
-rw-r--r--src/frontend/mame/ui/info.cpp126
-rw-r--r--src/frontend/mame/ui/info.h18
-rw-r--r--src/frontend/mame/ui/info_pty.cpp5
-rw-r--r--src/frontend/mame/ui/info_pty.h4
-rw-r--r--src/frontend/mame/ui/inifile.cpp66
-rw-r--r--src/frontend/mame/ui/inputdevices.cpp180
-rw-r--r--src/frontend/mame/ui/inputdevices.h4
-rw-r--r--src/frontend/mame/ui/inputmap.cpp77
-rw-r--r--src/frontend/mame/ui/inputmap.h16
-rw-r--r--src/frontend/mame/ui/inputopts.cpp6
-rw-r--r--src/frontend/mame/ui/inputopts.h4
-rw-r--r--src/frontend/mame/ui/inputtoggle.cpp173
-rw-r--r--src/frontend/mame/ui/inputtoggle.h4
-rw-r--r--src/frontend/mame/ui/keyboard.cpp74
-rw-r--r--src/frontend/mame/ui/keyboard.h4
-rw-r--r--src/frontend/mame/ui/mainmenu.cpp41
-rw-r--r--src/frontend/mame/ui/mainmenu.h4
-rw-r--r--src/frontend/mame/ui/menu.cpp1622
-rw-r--r--src/frontend/mame/ui/menu.h304
-rw-r--r--src/frontend/mame/ui/miscmenu.cpp243
-rw-r--r--src/frontend/mame/ui/miscmenu.h28
-rw-r--r--src/frontend/mame/ui/moptions.cpp120
-rw-r--r--src/frontend/mame/ui/moptions.h56
-rw-r--r--src/frontend/mame/ui/optsmenu.cpp31
-rw-r--r--src/frontend/mame/ui/optsmenu.h12
-rw-r--r--src/frontend/mame/ui/pluginopt.cpp50
-rw-r--r--src/frontend/mame/ui/pluginopt.h17
-rw-r--r--src/frontend/mame/ui/prscntrl.cpp69
-rw-r--r--src/frontend/mame/ui/prscntrl.h45
-rw-r--r--src/frontend/mame/ui/quitmenu.cpp38
-rw-r--r--src/frontend/mame/ui/quitmenu.h7
-rw-r--r--src/frontend/mame/ui/selector.cpp80
-rw-r--r--src/frontend/mame/ui/selector.h9
-rw-r--r--src/frontend/mame/ui/selgame.cpp309
-rw-r--r--src/frontend/mame/ui/selgame.h19
-rw-r--r--src/frontend/mame/ui/selmenu.cpp3399
-rw-r--r--src/frontend/mame/ui/selmenu.h238
-rw-r--r--src/frontend/mame/ui/selsoft.cpp186
-rw-r--r--src/frontend/mame/ui/selsoft.h16
-rw-r--r--src/frontend/mame/ui/simpleselgame.cpp295
-rw-r--r--src/frontend/mame/ui/simpleselgame.h14
-rw-r--r--src/frontend/mame/ui/sliders.cpp251
-rw-r--r--src/frontend/mame/ui/sliders.h7
-rw-r--r--src/frontend/mame/ui/slotopt.cpp61
-rw-r--r--src/frontend/mame/ui/slotopt.h9
-rw-r--r--src/frontend/mame/ui/sndmenu.cpp8
-rw-r--r--src/frontend/mame/ui/sndmenu.h4
-rw-r--r--src/frontend/mame/ui/state.cpp143
-rw-r--r--src/frontend/mame/ui/state.h16
-rw-r--r--src/frontend/mame/ui/submenu.cpp36
-rw-r--r--src/frontend/mame/ui/submenu.h7
-rw-r--r--src/frontend/mame/ui/swlist.cpp236
-rw-r--r--src/frontend/mame/ui/swlist.h15
-rw-r--r--src/frontend/mame/ui/systemlist.cpp4
-rw-r--r--src/frontend/mame/ui/tapectrl.cpp5
-rw-r--r--src/frontend/mame/ui/tapectrl.h4
-rw-r--r--src/frontend/mame/ui/text.cpp13
-rw-r--r--src/frontend/mame/ui/text.h2
-rw-r--r--src/frontend/mame/ui/textbox.cpp515
-rw-r--r--src/frontend/mame/ui/textbox.h53
-rw-r--r--src/frontend/mame/ui/ui.cpp677
-rw-r--r--src/frontend/mame/ui/ui.h105
-rw-r--r--src/frontend/mame/ui/utils.cpp447
-rw-r--r--src/frontend/mame/ui/utils.h126
-rw-r--r--src/frontend/mame/ui/videoopt.cpp29
-rw-r--r--src/frontend/mame/ui/videoopt.h8
-rw-r--r--src/frontend/mame/ui/viewgfx.cpp139
100 files changed, 9844 insertions, 5147 deletions
diff --git a/src/frontend/mame/ui/about.cpp b/src/frontend/mame/ui/about.cpp
index 05ba893ae00..7b737273c3a 100644
--- a/src/frontend/mame/ui/about.cpp
+++ b/src/frontend/mame/ui/about.cpp
@@ -64,17 +64,30 @@ menu_about::~menu_about()
//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_about::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu_textbox::recompute_metrics(width, height, aspect);
+
+ // make space for the title and revision
+ set_custom_space((line_height() * m_header.size()) + (tb_border() * 3.0F), 0.0F);
+}
+
+
+//-------------------------------------------------
// 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(
std::begin(m_header), std::end(m_header),
- origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(),
+ origx1, origx2, origy1 - top, origy1 - tb_border(),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
+ ui().colors().text_color(), UI_GREEN_COLOR);
}
@@ -87,7 +100,7 @@ void menu_about::populate_text(std::optional<text_layout> &layout, float &width,
if (!layout || (layout->width() != width))
{
rgb_t const color = ui().colors().text_color();
- layout.emplace(ui().create_layout(container(), width));
+ layout.emplace(create_layout(width));
for (char const *const *line = copying_text; *line; ++line)
{
layout->add_text(*line, color);
@@ -103,21 +116,8 @@ void menu_about::populate_text(std::optional<text_layout> &layout, float &width,
// populate - populates the about modal
//-------------------------------------------------
-void menu_about::populate(float &customtop, float &custombottom)
-{
- // make space for the title and revision
- customtop = (ui().get_line_height() * m_header.size()) + (ui().box_tb_border() * 3.0f);
-}
-
-
-//-------------------------------------------------
-// handle - manages inputs in the about modal
-//-------------------------------------------------
-
-void menu_about::handle(event const *ev)
+void menu_about::populate()
{
- if (ev)
- handle_key(ev->iptkey);
}
} // namespace ui
diff --git a/src/frontend/mame/ui/about.h b/src/frontend/mame/ui/about.h
index 13fc8e3e84f..90e9d738fa3 100644
--- a/src/frontend/mame/ui/about.h
+++ b/src/frontend/mame/ui/about.h
@@ -29,13 +29,13 @@ public:
virtual ~menu_about() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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 populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() 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 d8c22431ddd..aa6ffa5a07f 100644
--- a/src/frontend/mame/ui/analogipt.cpp
+++ b/src/frontend/mame/ui/analogipt.cpp
@@ -11,6 +11,10 @@
#include "emu.h"
#include "ui/analogipt.h"
+#include "ui/textbox.h"
+
+#include "uiinput.h"
+
#include <algorithm>
#include <iterator>
#include <string>
@@ -19,6 +23,20 @@
namespace ui {
+namespace {
+
+char const HELP_TEXT[] = N_p("menu-analoginput",
+ "Show/hide settings \t\t%1$s\n"
+ "Decrease value \t\t%2$s\n"
+ "Increase value \t\t%3$s\n"
+ "Restore default value \t\t%4$s\n"
+ "Previous device \t\t%5$s\n"
+ "Next device \t\t%6$s\n"
+ "Return to previous menu \t\t%7$s");
+
+} // anonymous namespace
+
+
inline menu_analog::item_data::item_data(ioport_field &f, int t) noexcept
: field(f)
, type(t)
@@ -37,13 +55,19 @@ inline menu_analog::item_data::item_data(ioport_field &f, int t) noexcept
inline menu_analog::field_data::field_data(ioport_field &f) noexcept
: field(f)
- , range(f.maxval() - f.minval())
- , neutral(float(f.analog_reverse() ? (f.maxval() - f.defvalue()) : (f.defvalue() - f.minval())) / range)
- , origin((f.analog_wraps() && (f.defvalue() != f.minval()) && (f.defvalue() != f.maxval())) ? 0.0f : neutral)
+ , range(0.0F)
+ , neutral(0.0F)
+ , origin(0.0F)
, shift(0U)
, show_neutral((f.defvalue() != f.minval()) && (f.defvalue() != f.maxval()))
{
for (ioport_value m = f.mask(); m && !BIT(m, 0); m >>= 1, ++shift) { }
+ ioport_value const m(f.mask() >> shift);
+ range = (f.maxval() - f.minval()) & m;
+ ioport_value const n((f.analog_reverse() ? (f.maxval() - f.defvalue()) : (f.defvalue() - f.minval())) & m);
+ neutral = float(n) / range;
+ if (!f.analog_wraps() || (f.defvalue() == f.minval()) || (f.defvalue() == f.maxval()))
+ origin = neutral;
}
@@ -52,12 +76,23 @@ 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(_("Analog Input Adjustments"));
+ set_heading(_("menu-analoginput", "Analog Input Adjustments"));
}
@@ -66,48 +101,54 @@ menu_analog::~menu_analog()
}
-void menu_analog::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu_analog::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ 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_bottom_fields) + (tb_border() * 3.0F));
+}
+
+
+void menu_analog::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
// work out how much space to use for field names
- float const aspect(machine().render().ui_aspect(&container()));
- float const extrawidth(0.4f + (((ui().box_lr_border() * 2.0f) + ui().get_line_height()) * aspect));
- float const nameavail(1.0f - (ui().box_lr_border() * 2.0f * aspect) - 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)(ui().get_string_width(data.field.get().name()), namewidth), nameavail);
+ 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 lineheight(ui().get_line_height());
- float const border(ui().box_tb_border());
- 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(_("Press %s to show menu"), ui().get_general_input_setting(IPT_UI_ON_SCREEN_DISPLAY));
+ 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 + lineheight + (border * 2.0f),
+ m_box_left, m_box_right, y - top, y - top + line_height() + (tb_border() * 2.0F),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- fgcolor, ui().colors().background_color(), 1.0f);
- boxtop = y - top + lineheight + (border * 3.0f);
- firstliney = y - top + lineheight + (border * 4.0f);
- visible_fields = std::min<int>(m_field_data.size(), int((y2 + bottom - border - firstliney) / lineheight));
- boxbottom = firstliney + (lineheight * visible_fields) + border;
+ fgcolor, ui().colors().background_color());
+ m_box_top = y - top + line_height() + (tb_border() * 3.0F);
+ firstliney = y - top + line_height() + (tb_border() * 4.0F);
+ m_visible_fields = std::min<int>(m_field_data.size(), int((y2 + bottom - tb_border() - firstliney) / line_height()));
+ m_box_bottom = firstliney + (line_height() * m_visible_fields) + tb_border();
}
else
{
- boxtop = y2 + border;
- boxbottom = y2 + bottom;
- firstliney = y2 + (border * 2.0f);
- visible_fields = m_visible_fields;
+ m_box_top = y2 + tb_border();
+ m_box_bottom = y2 + bottom;
+ firstliney = y2 + (tb_border() * 2.0F);
+ m_visible_fields = m_bottom_fields;
}
- ui().draw_outlined_box(container(), boxleft, boxtop, boxright, boxbottom, ui().colors().background_color());
+ ui().draw_outlined_box(container(), m_box_left, m_box_top, m_box_right, m_box_bottom, ui().colors().background_color());
// force the field being configured to be visible
ioport_field *const selfield(selectedref ? &reinterpret_cast<item_data *>(selectedref)->field.get() : nullptr);
@@ -123,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 += lineheight * aspect;
- float const nameleft(boxleft + (ui().box_lr_border() * aspect));
+ 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 + (lineheight * 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 = lineheight * 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 * lineheight),
- 0.5f * (nameleft + indright + arrowwidth), liney + (0.75f * lineheight),
- 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;
}
}
@@ -160,181 +210,394 @@ void menu_analog::custom_render(void *selectedref, float top, float bottom, floa
field_data &data(m_field_data[line + m_top_field]);
bool const selected(&data.field.get() == selfield);
rgb_t const fieldcolor(selected ? ui().colors().selected_color() : fgcolor);
- ui().draw_text_full(
- container(),
+ draw_text_normal(
data.field.get().name(),
nameleft, liney, namewidth,
text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, fieldcolor, ui().colors().text_bg_color());
+ fieldcolor);
ioport_value cur(0U);
data.field.get().live().analog->read(cur);
- cur = (cur >> data.shift) - data.field.get().minval();
+ 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 + (lineheight * 0.2f));
- float const indbottom(liney + (lineheight * 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;
}
-void menu_analog::handle(event const *ev)
+bool menu_analog::handle(event const *ev)
{
- // handle events
- if (ev)
+ // deal with repeating scroll arrows
+ bool scrolled(false);
+ if ((pointer_action::SCROLL_UP == m_pointer_action) || (pointer_action::SCROLL_DOWN == m_pointer_action))
{
- if (IPT_UI_ON_SCREEN_DISPLAY == ev->iptkey)
+ 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))
{
- m_hide_menu = !m_hide_menu;
- set_process_flags(PROCESS_LR_REPEAT | (m_hide_menu ? (PROCESS_CUSTOM_NAV | PROCESS_CUSTOM_ONLY) : 0));
+ while (scroll_if_expired(std::chrono::steady_clock::now()))
+ {
+ scrolled = true;
+ m_scroll_repeat += std::chrono::milliseconds(100);
+ }
}
- else if (m_hide_menu)
+ }
+
+ if (!ev)
+ {
+ 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;
+ }
+ else if (IPT_UI_HELP == ev->iptkey)
+ {
+ stack_push<menu_fixed_textbox>(
+ ui(),
+ container(),
+ _("menu-analoginput", "Analog Input Adjustments Help"),
+ util::string_format(
+ _("menu-analoginput", HELP_TEXT),
+ ui().get_general_input_setting(IPT_UI_ON_SCREEN_DISPLAY),
+ ui().get_general_input_setting(IPT_UI_LEFT),
+ ui().get_general_input_setting(IPT_UI_RIGHT),
+ ui().get_general_input_setting(IPT_UI_CLEAR),
+ 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)));
+ }
+ else if (m_hide_menu)
+ {
+ switch (ev->iptkey)
{
- switch (ev->iptkey)
+ case IPT_UI_UP:
+ if (m_top_field)
{
- case IPT_UI_UP:
--m_top_field;
- break;
- case IPT_UI_DOWN:
+ return true;
+ }
+ break;
+ case IPT_UI_DOWN:
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ {
++m_top_field;
- break;
- case IPT_UI_HOME:
+ 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:
+ if (m_top_field)
+ {
m_top_field = 0;
- break;
- case IPT_UI_END:
- m_top_field = m_field_data.size();
- break;
+ return true;
}
+ break;
+ case IPT_UI_END:
+ 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)
+ }
+ else if (ev->itemref)
+ {
+ item_data &data(*reinterpret_cast<item_data *>(ev->itemref));
+ int newval(data.cur);
+
+ switch (ev->iptkey)
{
- item_data &data(*reinterpret_cast<item_data *>(ev->itemref));
- int newval(data.cur);
+ // flip toggles when selected
+ case IPT_UI_SELECT:
+ if (ANALOG_ITEM_REVERSE == data.type)
+ newval = newval ? 0 : 1;
+ break;
+
+ // if cleared, reset to default value
+ case IPT_UI_CLEAR:
+ newval = data.defvalue;
+ break;
+
+ // left decrements
+ case IPT_UI_LEFT:
+ newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ break;
+
+ // right increments
+ case IPT_UI_RIGHT:
+ newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ break;
- switch (ev->iptkey)
+ // move to first item for previous device
+ case IPT_UI_PREV_GROUP:
{
- // if selected, reset to default value
- case IPT_UI_SELECT:
- case IPT_UI_CLEAR:
- newval = data.defvalue;
- break;
-
- // left decrements
- case IPT_UI_LEFT:
- newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
- break;
-
- // right increments
- case IPT_UI_RIGHT:
- newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
- break;
-
- // move to first item for previous device
- case IPT_UI_PREV_GROUP:
+ auto current = std::distance(m_item_data.data(), &data);
+ device_t const *dev(&data.field.get().device());
+ bool found_break = false;
+ while (0 < current)
{
- auto current = std::distance(m_item_data.data(), &data);
- device_t const *dev(&data.field.get().device());
- bool found_break = false;
- while (0 < current)
+ if (!found_break)
{
- if (!found_break)
- {
- device_t const *prev(&m_item_data[--current].field.get().device());
- if (prev != dev)
- {
- dev = prev;
- found_break = true;
- }
- }
- else if (&m_item_data[current - 1].field.get().device() != dev)
+ device_t const *prev(&m_item_data[--current].field.get().device());
+ if (prev != dev)
{
- set_selection(&m_item_data[current]);
- set_top_line(selected_index() - 1);
- break;
- }
- else
- {
- --current;
- }
- if (found_break && !current)
- {
- set_selection(&m_item_data[current]);
- set_top_line(selected_index() - 1);
- break;
+ dev = prev;
+ found_break = true;
}
}
+ else if (&m_item_data[current - 1].field.get().device() != dev)
+ {
+ set_selection(&m_item_data[current]);
+ set_top_line(selected_index() - 1);
+ return true;
+ }
+ else
+ {
+ --current;
+ }
+ if (found_break && !current)
+ {
+ set_selection(&m_item_data[current]);
+ set_top_line(selected_index() - 1);
+ return true;
+ }
}
- break;
+ }
+ break;
- // move to first item for next device
- case IPT_UI_NEXT_GROUP:
+ // move to first item for next device
+ case IPT_UI_NEXT_GROUP:
+ {
+ auto current = std::distance(m_item_data.data(), &data);
+ device_t const *const dev(&data.field.get().device());
+ while (m_item_data.size() > ++current)
{
- auto current = std::distance(m_item_data.data(), &data);
- device_t const *const dev(&data.field.get().device());
- while (m_item_data.size() > ++current)
+ if (&m_item_data[current].field.get().device() != dev)
{
- if (&m_item_data[current].field.get().device() != dev)
- {
- set_selection(&m_item_data[current]);
- set_top_line(selected_index() - 1);
- break;
- }
+ set_selection(&m_item_data[current]);
+ set_top_line(selected_index() - 1);
+ return true;
}
}
- break;
}
+ break;
+ }
- // clamp to range
- newval = std::clamp(newval, data.min, data.max);
-
- // if things changed, update
- if (newval != data.cur)
- {
- ioport_field::user_settings settings;
+ // clamp to range
+ newval = std::clamp(newval, data.min, data.max);
- // get the settings and set the new value
- data.field.get().get_user_settings(settings);
- switch (data.type)
- {
- case ANALOG_ITEM_KEYSPEED: settings.delta = newval; break;
- case ANALOG_ITEM_CENTERSPEED: settings.centerdelta = newval; break;
- case ANALOG_ITEM_REVERSE: settings.reverse = newval; break;
- case ANALOG_ITEM_SENSITIVITY: settings.sensitivity = newval; break;
- }
- data.field.get().set_user_settings(settings);
- data.cur = newval;
+ // if things changed, update
+ if (newval != data.cur)
+ {
+ ioport_field::user_settings settings;
- // update the menu item
- ev->item->set_subtext(item_text(data.type, newval));
- ev->item->set_flags((data.cur <= data.min) ? FLAG_RIGHT_ARROW : (data.cur >= data.max) ? FLAG_LEFT_ARROW : FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW);
+ // get the settings and set the new value
+ data.field.get().get_user_settings(settings);
+ switch (data.type)
+ {
+ case ANALOG_ITEM_KEYSPEED: settings.delta = newval; break;
+ case ANALOG_ITEM_CENTERSPEED: settings.centerdelta = newval; break;
+ case ANALOG_ITEM_REVERSE: settings.reverse = newval; break;
+ case ANALOG_ITEM_SENSITIVITY: settings.sensitivity = newval; break;
}
+ data.field.get().set_user_settings(settings);
+ data.cur = newval;
+
+ // update the menu item
+ ev->item->set_subtext(item_text(data.type, newval));
+ 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;
}
}
+
+ return scrolled;
}
-void menu_analog::populate(float &customtop, float &custombottom)
+void menu_analog::populate()
{
// loop over input ports
if (m_item_data.empty())
@@ -369,22 +632,22 @@ void menu_analog::populate(float &customtop, float &custombottom)
{
default:
case ANALOG_ITEM_KEYSPEED:
- text = string_format(_("%1$s Increment/Decrement Speed"), field->name());
+ text = string_format(_("menu-analoginput", "%1$s Increment/Decrement Speed"), field->name());
data.cur = settings.delta;
break;
case ANALOG_ITEM_CENTERSPEED:
- text = string_format(_("%1$s Auto-centering Speed"), field->name());
+ text = string_format(_("menu-analoginput", "%1$s Auto-centering Speed"), field->name());
data.cur = settings.centerdelta;
break;
case ANALOG_ITEM_REVERSE:
- text = string_format(_("%1$s Reverse"), field->name());
+ text = string_format(_("menu-analoginput", "%1$s Reverse"), field->name());
data.cur = settings.reverse;
break;
case ANALOG_ITEM_SENSITIVITY:
- text = string_format(_("%1$s Sensitivity"), field->name());
+ text = string_format(_("menu-analoginput", "%1$s Sensitivity"), field->name());
data.cur = settings.sensitivity;
break;
}
@@ -399,12 +662,12 @@ void menu_analog::populate(float &customtop, float &custombottom)
// display a message if there are toggle inputs enabled
if (!prev_owner)
- item_append(_("[no analog inputs are enabled]"), FLAG_DISABLE, nullptr);
+ item_append(_("menu-analoginput", "[no analog inputs are enabled]"), FLAG_DISABLE, nullptr);
item_append(menu_item_type::SEPARATOR);
// space for live display
- custombottom = (ui().get_line_height() * m_visible_fields) + (ui().box_tb_border() * 3.0f);
+ set_custom_space(0.0F, (line_height() * m_bottom_fields) + (tb_border() * 3.0F));
}
@@ -456,10 +719,72 @@ void menu_analog::find_fields()
}
// restrict live display to 40% height plus borders
- if ((ui().get_line_height() * m_field_data.size()) > 0.4f)
- m_visible_fields = unsigned(0.4f / ui().get_line_height());
+ if ((line_height() * m_field_data.size()) > 0.4F)
+ m_bottom_fields = unsigned(0.4F / line_height());
else
- m_visible_fields = m_field_data.size();
+ 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
+ {
+ 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 f6807947ce6..ee59954911c 100644
--- a/src/frontend/mame/ui/analogipt.h
+++ b/src/frontend/mame/ui/analogipt.h
@@ -14,7 +14,10 @@
#include "ui/menu.h"
+#include <chrono>
#include <functional>
+#include <tuple>
+#include <utility>
#include <vector>
@@ -27,10 +30,21 @@ public:
virtual ~menu_analog() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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 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,
@@ -66,19 +80,34 @@ private:
using item_data_vector = std::vector<item_data>;
using field_data_vector = std::vector<field_data>;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
void find_fields();
+ bool scroll_if_expired(std::chrono::steady_clock::time_point now);
+ bool update_scroll_drag(ui_event const &uievt);
static std::string item_text(int type, int value);
item_data_vector m_item_data;
field_data_vector m_field_data;
std::string m_prompt;
- unsigned m_visible_fields;
+ unsigned m_bottom_fields;
+ int m_visible_fields;
int m_top_field;
bool m_hide_menu;
+
+ float m_box_left;
+ float m_box_top;
+ float m_box_right;
+ float m_box_bottom;
+
+ pointer_action m_pointer_action;
+ std::chrono::steady_clock::time_point m_scroll_repeat;
+ std::pair<float, float> m_base_pointer;
+ std::pair<float, float> m_last_pointer;
+ int m_scroll_base;
+ bool m_arrow_clicked_first;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/auditmenu.cpp b/src/frontend/mame/ui/auditmenu.cpp
index ae585d6e2ed..180c3005b73 100644
--- a/src/frontend/mame/ui/auditmenu.cpp
+++ b/src/frontend/mame/ui/auditmenu.cpp
@@ -18,6 +18,7 @@
#include "drivenum.h"
#include "fileio.h"
+#include "main.h"
#include "uiinput.h"
#include "util/corestr.h"
@@ -74,7 +75,15 @@ menu_audit::~menu_audit()
}
-void menu_audit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu_audit::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(0.0F, (line_height() * 1.0F) + (tb_border() * 3.0F));
+}
+
+
+void menu_audit::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
switch (m_phase)
{
@@ -83,9 +92,9 @@ void menu_audit::custom_render(void *selectedref, float top, float bottom, float
{
draw_text_box(
&m_prompt, &m_prompt + 1,
- x, x2, y2 + ui().box_tb_border(), y2 + bottom,
+ x, x2, y2 + tb_border(), y2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
+ ui().colors().text_color(), UI_GREEN_COLOR);
}
break;
@@ -107,7 +116,7 @@ void menu_audit::custom_render(void *selectedref, float top, float bottom, float
container(),
std::move(text).str(),
text_layout::text_justify::CENTER,
- 0.5f, 0.5f,
+ 0.5F, 0.5F,
ui().colors().background_color());
}
break;
@@ -118,31 +127,30 @@ void menu_audit::custom_render(void *selectedref, float top, float bottom, float
util::string_format(
_("Cancel audit?\n\nPress %1$s to cancel\nPress %2$s to continue"),
ui().get_general_input_setting(IPT_UI_SELECT),
- ui().get_general_input_setting(IPT_UI_CANCEL)),
+ ui().get_general_input_setting(IPT_UI_BACK)),
text_layout::text_justify::CENTER,
- 0.5f, 0.5f,
+ 0.5F, 0.5F,
UI_RED_COLOR);
break;
}
}
-bool menu_audit::custom_ui_cancel()
+bool menu_audit::custom_ui_back()
{
return m_phase != phase::CONFIRMATION;
}
-void menu_audit::populate(float &customtop, float &custombottom)
+void menu_audit::populate()
{
if (m_unavailable && (m_availablesorted.size() != m_unavailable))
item_append(util::string_format(_("Audit media for %1$u systems marked unavailable"), m_unavailable), 0, ITEMREF_START_FAST);
item_append(util::string_format(_("Audit media for all %1$u systems"), m_availablesorted.size()), 0, ITEMREF_START_FULL);
item_append(menu_item_type::SEPARATOR, 0);
- custombottom = (ui().get_line_height() * 1.0f) + (ui().box_tb_border() * 3.0f);
}
-void menu_audit::handle(event const *ev)
+bool menu_audit::handle(event const *ev)
{
switch (m_phase)
{
@@ -154,10 +162,11 @@ void menu_audit::handle(event const *ev)
set_process_flags(PROCESS_CUSTOM_ONLY | PROCESS_NOINPUT);
m_phase = phase::AUDIT;
m_fast = ITEMREF_START_FAST == ev->itemref;
- m_prompt = util::string_format(_("Press %1$s to cancel\n"), ui().get_general_input_setting(IPT_UI_CANCEL));
+ m_prompt = util::string_format(_("Press %1$s to cancel\n"), ui().get_general_input_setting(IPT_UI_BACK));
m_future.resize(std::thread::hardware_concurrency());
for (auto &future : m_future)
future = std::async(std::launch::async, [this] () { return do_audit(); });
+ return true;
}
}
break;
@@ -177,19 +186,23 @@ void menu_audit::handle(event const *ev)
}
stack_pop();
}
- else if (machine().ui_input().pressed(IPT_UI_CANCEL))
+ else if (machine().ui_input().pressed(IPT_UI_BACK))
{
if (phase::AUDIT == m_phase)
m_phase = phase::CANCELLATION;
else
m_phase = phase::AUDIT;
+ return true;
}
else if ((phase::CANCELLATION == m_phase) && machine().ui_input().pressed(IPT_UI_SELECT))
{
m_cancel.store(true);
+ return true;
}
break;
}
+
+ return false;
}
bool menu_audit::do_audit()
diff --git a/src/frontend/mame/ui/auditmenu.h b/src/frontend/mame/ui/auditmenu.h
index de42c33b6d1..3a1c236549d 100644
--- a/src/frontend/mame/ui/auditmenu.h
+++ b/src/frontend/mame/ui/auditmenu.h
@@ -29,14 +29,15 @@ public:
virtual ~menu_audit() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool custom_ui_cancel() override;
+ 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 bool custom_ui_back() override;
private:
enum class phase { CONFIRMATION, AUDIT, CANCELLATION };
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
bool do_audit();
void save_available_machines();
diff --git a/src/frontend/mame/ui/barcode.cpp b/src/frontend/mame/ui/barcode.cpp
index a06e4b99673..979203f0133 100644
--- a/src/frontend/mame/ui/barcode.cpp
+++ b/src/frontend/mame/ui/barcode.cpp
@@ -54,28 +54,15 @@ menu_barcode_reader::~menu_barcode_reader()
// populate - populates the barcode input menu
//-------------------------------------------------
-void menu_barcode_reader::populate(float &customtop, float &custombottom)
+void menu_barcode_reader::populate()
{
if (current_device())
{
- std::string buffer;
- const char *new_barcode;
-
// selected device
item_append(std::string(current_display_name()), std::string(current_device()->tag() + 1), current_display_flags(), ITEMREF_SELECT_READER);
// append the "New Barcode" item
- if (get_selection_ref() == ITEMREF_NEW_BARCODE)
- {
- buffer.append(m_barcode_buffer);
- new_barcode = buffer.c_str();
- }
- else
- {
- new_barcode = m_barcode_buffer.c_str();
- }
-
- item_append(_("New Barcode:"), new_barcode, 0, ITEMREF_NEW_BARCODE);
+ item_append(_("New Barcode:"), m_barcode_buffer, 0, ITEMREF_NEW_BARCODE);
// finish up the menu
item_append(_("Enter Code"), 0, ITEMREF_ENTER_BARCODE);
@@ -88,58 +75,74 @@ void menu_barcode_reader::populate(float &customtop, float &custombottom)
// handle - manages inputs in the barcode input menu
//-------------------------------------------------
-void menu_barcode_reader::handle(event const *ev)
+bool menu_barcode_reader::handle(event const *ev)
{
- // process the event
- if (ev)
+ if (!ev)
+ return false;
+
+ switch (ev->iptkey)
{
- // handle selections
- switch (ev->iptkey)
+ case IPT_UI_LEFT:
+ if (ev->itemref == ITEMREF_SELECT_READER)
+ return previous();
+ break;
+
+ case IPT_UI_RIGHT:
+ if (ev->itemref == ITEMREF_SELECT_READER)
+ return next();
+ break;
+
+ case IPT_UI_SELECT:
+ if (ev->itemref == ITEMREF_ENTER_BARCODE)
{
- case IPT_UI_LEFT:
- if (ev->itemref == ITEMREF_SELECT_READER)
- previous();
- break;
-
- case IPT_UI_RIGHT:
- if (ev->itemref == ITEMREF_SELECT_READER)
- next();
- break;
-
- case IPT_UI_SELECT:
- if (ev->itemref == ITEMREF_ENTER_BARCODE)
+ //osd_printf_verbose("code %s\n", m_barcode_buffer);
+ if (!current_device()->is_valid(m_barcode_buffer.length()))
{
- std::string tmp_file(m_barcode_buffer);
- //printf("code %s\n", m_barcode_buffer);
- if (!current_device()->is_valid(tmp_file.length()))
- ui().popup_time(5, "%s", _("Barcode length invalid!"));
- else
- {
- current_device()->write_code(tmp_file.c_str(), tmp_file.length());
- // if sending was successful, reset char buffer
- m_barcode_buffer.clear();
- reset(reset_options::REMEMBER_POSITION);
- }
+ ui().popup_time(5, "%s", _("Barcode length invalid!"));
}
- break;
-
- case IPT_UI_CLEAR:
- if (get_selection_ref() == ITEMREF_NEW_BARCODE)
+ else
{
+ current_device()->write_code(m_barcode_buffer.c_str(), m_barcode_buffer.length());
+ // if sending was successful, reset char buffer
m_barcode_buffer.clear();
reset(reset_options::REMEMBER_POSITION);
}
- break;
+ }
+ break;
+
+ case IPT_UI_CLEAR:
+ if (ev->itemref == ITEMREF_NEW_BARCODE)
+ {
+ m_barcode_buffer.clear();
+ ev->item->set_subtext(m_barcode_buffer);
+ return true;
+ }
+ break;
+
+ case IPT_UI_PASTE:
+ if (get_selection_ref() == ITEMREF_NEW_BARCODE)
+ {
+ if (paste_text(m_barcode_buffer, uchar_is_digit))
+ {
+ ev->item->set_subtext(m_barcode_buffer);
+ return true;
+ }
+ }
+ break;
- case IPT_SPECIAL:
- if (get_selection_ref() == ITEMREF_NEW_BARCODE)
+ case IPT_SPECIAL:
+ if (get_selection_ref() == ITEMREF_NEW_BARCODE)
+ {
+ if (input_character(m_barcode_buffer, ev->unichar, uchar_is_digit))
{
- if (input_character(m_barcode_buffer, ev->unichar, uchar_is_digit))
- reset(reset_options::REMEMBER_POSITION);
+ ev->item->set_subtext(m_barcode_buffer);
+ return true;
}
- break;
}
+ break;
}
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/barcode.h b/src/frontend/mame/ui/barcode.h
index e171485cf79..7fa9881c3dd 100644
--- a/src/frontend/mame/ui/barcode.h
+++ b/src/frontend/mame/ui/barcode.h
@@ -13,9 +13,13 @@
#pragma once
-#include "machine/bcreader.h"
#include "ui/devctrl.h"
+#include "machine/bcreader.h"
+
+#include <string>
+
+
namespace ui {
class menu_barcode_reader : public menu_device_control<barcode_reader_device> {
@@ -24,8 +28,8 @@ public:
virtual ~menu_barcode_reader() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
std::string m_barcode_buffer;
};
diff --git a/src/frontend/mame/ui/cheatopt.cpp b/src/frontend/mame/ui/cheatopt.cpp
index 9aae81134ef..c8457a1403b 100644
--- a/src/frontend/mame/ui/cheatopt.cpp
+++ b/src/frontend/mame/ui/cheatopt.cpp
@@ -20,85 +20,96 @@
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)
/*-------------------------------------------------
menu_cheat - handle the cheat menu
-------------------------------------------------*/
-void menu_cheat::handle(event const *ev)
+bool menu_cheat::handle(event const *ev)
{
- // handle events
- if (ev && ev->itemref)
- {
- bool changed = false;
+ if (!ev || !ev->itemref)
+ return false;
+
+ bool changed = false;
- // clear cheat comment on any movement or keypress
- machine().popmessage();
+ // clear cheat comment on any movement or keypress
+ machine().popmessage();
- if ((ev->itemref == ITEMREF_CHEATS_RESET_ALL || ev->itemref == ITEMREF_CHEATS_RELOAD_ALL) && ev->iptkey == IPT_UI_SELECT)
+ if (ev->itemref == ITEMREF_CHEATS_ENABLE)
+ {
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
{
- // handle reset all + reset all cheats for reload all option
- for (auto &curcheat : mame_machine_manager::instance()->cheat().entries())
- if (curcheat->select_default_state())
- changed = true;
+ // handle global enable toggle
+ mame_machine_manager::instance()->cheat().set_enable(ev->iptkey == IPT_UI_RIGHT || (ev->iptkey == IPT_UI_CLEAR), false);
+ changed = true;
}
- else if (ev->itemref >= ITEMREF_CHEATS_FIRST_ITEM)
+ }
+ 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
+ for (auto &curcheat : mame_machine_manager::instance()->cheat().entries())
+ if (curcheat->select_default_state())
+ changed = true;
+ }
+ else if (ev->itemref >= ITEMREF_CHEATS_FIRST_ITEM)
+ {
+ // handle individual cheats
+ cheat_entry *curcheat = reinterpret_cast<cheat_entry *>(ev->itemref);
+ const char *string;
+ switch (ev->iptkey)
{
- // handle individual cheats
- cheat_entry *curcheat = reinterpret_cast<cheat_entry *>(ev->itemref);
- const char *string;
- switch (ev->iptkey)
- {
- // if selected, activate a oneshot
- case IPT_UI_SELECT:
- changed = curcheat->activate();
- break;
-
- // if cleared, reset to default value
- case IPT_UI_CLEAR:
- changed = curcheat->select_default_state();
- break;
-
- // left decrements
- case IPT_UI_LEFT:
- changed = curcheat->select_previous_state();
- break;
-
- // right increments
- case IPT_UI_RIGHT:
- changed = curcheat->select_next_state();
- break;
-
- // bring up display comment if one exists
- case IPT_UI_DISPLAY_COMMENT:
- case IPT_UI_UP:
- case IPT_UI_DOWN:
- string = curcheat->comment();
- if (string && *string)
- machine().popmessage(_("Cheat Comment:\n%s"), string);
- break;
- }
+ // if selected, activate a oneshot
+ case IPT_UI_SELECT:
+ changed = curcheat->activate();
+ break;
+
+ // if cleared, reset to default value
+ case IPT_UI_CLEAR:
+ changed = curcheat->select_default_state();
+ break;
+
+ // left decrements
+ case IPT_UI_LEFT:
+ changed = curcheat->select_previous_state();
+ break;
+
+ // right increments
+ case IPT_UI_RIGHT:
+ changed = curcheat->select_next_state();
+ break;
+
+ // bring up display comment if one exists
+ case IPT_UI_DISPLAY_COMMENT:
+ case IPT_UI_UP:
+ case IPT_UI_DOWN:
+ string = curcheat->comment();
+ if (string && *string)
+ machine().popmessage(_("Cheat Comment:\n%s"), string);
+ break;
}
+ }
- // handle reload all
- if (ev->itemref == ITEMREF_CHEATS_RELOAD_ALL && ev->iptkey == IPT_UI_SELECT)
- {
- // re-init cheat engine and thus reload cheats/cheats have already been turned off by here
- mame_machine_manager::instance()->cheat().reload();
-
- // display the reloaded cheats
- reset(reset_options::REMEMBER_REF);
- machine().popmessage(_("All cheats reloaded"));
- }
+ // handle reload all
+ if (ev->itemref == ITEMREF_CHEATS_RELOAD_ALL && ev->iptkey == IPT_UI_SELECT)
+ {
+ // re-init cheat engine and thus reload cheats/cheats have already been turned off by here
+ mame_machine_manager::instance()->cheat().reload();
- // if things changed, update
- if (changed)
- reset(reset_options::REMEMBER_REF);
+ // display the reloaded cheats
+ machine().popmessage(_("All cheats reloaded"));
+ changed = true;
}
+
+ // if things changed, update
+ if (changed)
+ reset(reset_options::REMEMBER_REF);
+
+ return false; // always triggers an item reset if the menu needs to be redrawn
}
@@ -108,17 +119,25 @@ void 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::populate(float &customtop, float &custombottom)
+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;
@@ -128,16 +147,27 @@ void menu_cheat::populate(float &customtop, float &custombottom)
else
item_append(text, subtext, flags, curcheat.get());
}
+ }
+ else
+ {
+ // indicate that none were found
+ item_append(_("[no cheats found]"), FLAG_DISABLE, nullptr);
+ }
+
+ item_append(menu_item_type::SEPARATOR);
- /* add a 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 c01ec151189..77847f80950 100644
--- a/src/frontend/mame/ui/cheatopt.h
+++ b/src/frontend/mame/ui/cheatopt.h
@@ -24,8 +24,9 @@ public:
virtual ~menu_cheat() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual void menu_activated() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/confswitch.cpp b/src/frontend/mame/ui/confswitch.cpp
index 5440b5545ec..f2f8a241d98 100644
--- a/src/frontend/mame/ui/confswitch.cpp
+++ b/src/frontend/mame/ui/confswitch.cpp
@@ -11,6 +11,8 @@
#include "emu.h"
#include "ui/confswitch.h"
+#include "uiinput.h"
+
#include <algorithm>
#include <cstring>
@@ -89,7 +91,7 @@ void menu_confswitch::menu_activated()
}
-void menu_confswitch::populate(float &customtop, float &custombottom)
+void menu_confswitch::populate()
{
// locate relevant fields if necessary
if (m_fields.empty())
@@ -171,99 +173,113 @@ void menu_confswitch::populate(float &customtop, float &custombottom)
}
-void menu_confswitch::handle(event const *ev)
+bool menu_confswitch::handle(event const *ev)
{
- // handle events
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ if (IPT_CUSTOM == ev->iptkey)
{
- if (uintptr_t(ev->itemref) == 1U)
- {
- // reset
- if (ev->iptkey == IPT_UI_SELECT)
- machine().schedule_hard_reset();
- }
- else
- {
- // actual settings
- ioport_field &field(*reinterpret_cast<ioport_field *>(ev->itemref));
- bool changed(false);
+ // clicked a switch
+ reset(reset_options::REMEMBER_REF);
+ return true;
+ }
- switch (ev->iptkey)
+ if (uintptr_t(ev->itemref) == 1U)
+ {
+ // reset
+ if (ev->iptkey == IPT_UI_SELECT)
+ machine().schedule_hard_reset();
+ }
+ else
+ {
+ // 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;
+ break;
+
+ // right goes to next setting
+ case IPT_UI_SELECT:
+ case IPT_UI_RIGHT:
+ field.select_next_setting();
+ changed = true;
+ break;
+
+ // if cleared, reset to default value
+ case IPT_UI_CLEAR:
{
- // if selected, reset to default value
- case IPT_UI_SELECT:
+ ioport_field::user_settings settings;
+ field.get_user_settings(settings);
+ if (field.defvalue() != settings.value)
{
- ioport_field::user_settings settings;
- field.get_user_settings(settings);
settings.value = field.defvalue();
field.set_user_settings(settings);
+ changed = true;
}
- changed = true;
- break;
-
- // left goes to previous setting
- case IPT_UI_LEFT:
- field.select_previous_setting();
- changed = true;
- break;
-
- // right goes to next setting
- case IPT_UI_RIGHT:
- field.select_next_setting();
- changed = true;
- break;
+ }
+ break;
- // trick to get previous group - depend on headings having null reference
- case IPT_UI_PREV_GROUP:
+ // trick to get previous group - depend on headings having null reference
+ case IPT_UI_PREV_GROUP:
+ {
+ auto current = selected_index();
+ bool found_break = false;
+ while (0 < current)
{
- auto current = selected_index();
- bool found_break = false;
- while (0 < current)
+ if (!found_break)
{
- if (!found_break)
- {
- if (!item(--current).ref())
- found_break = true;
- }
- else if (!item(current - 1).ref())
- {
- set_selected_index(current);
- set_top_line(current - 1);
- break;
- }
- else
- {
- --current;
- }
+ if (!item(--current).ref())
+ found_break = true;
+ }
+ else if (!item(current - 1).ref())
+ {
+ set_selected_index(current);
+ set_top_line(current - 1);
+ return true;
+ }
+ else
+ {
+ --current;
}
}
- break;
+ }
+ break;
- // trick to get next group - depend on special item references
- case IPT_UI_NEXT_GROUP:
+ // trick to get next group - depend on special item references
+ case IPT_UI_NEXT_GROUP:
+ {
+ auto current = selected_index();
+ while (item_count() > ++current)
{
- auto current = selected_index();
- while (item_count() > ++current)
+ if (!item(current).ref())
{
- if (!item(current).ref())
+ if ((item_count() > (current + 1)) && (uintptr_t(item(current + 1).ref()) != 1))
{
- if ((item_count() > (current + 1)) && (uintptr_t(item(current + 1).ref()) != 1))
- {
- set_selected_index(current + 1);
- set_top_line(current);
- }
- break;
+ set_selected_index(current + 1);
+ set_top_line(current);
+ return true;
}
+ break;
}
}
- break;
}
-
- // if anything changed, rebuild the menu, trying to stay on the same field
- if (changed)
- reset(reset_options::REMEMBER_REF);
+ 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;
}
@@ -320,17 +336,27 @@ menu_settings_dip_switches::~menu_settings_dip_switches()
}
-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::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu_confswitch::recompute_metrics(width, height, aspect);
+
+ set_custom_space(
+ 0.0f,
+ m_visible_switch_groups
+ ? ((m_visible_switch_groups * (DIP_SWITCH_HEIGHT * line_height())) + ((m_visible_switch_groups - 1) * (DIP_SWITCH_SPACING * line_height())) + (tb_border() * 3.0f))
+ : 0.0f);
+}
+
+
+void menu_settings_dip_switches::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
// catch if no DIP locations have to be drawn
if (!m_visible_switch_groups)
return;
// calculate optimal width
- float const aspect(machine().render().ui_aspect(&container()));
- float const lineheight(ui().get_line_height());
- float const maxwidth(1.0f - (ui().box_lr_border() * 2.0f * aspect));
- m_single_width = (lineheight * SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * aspect);
+ float const maxwidth(1.0f - (lr_border() * 2.0f));
+ 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())
@@ -338,30 +364,30 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
if (group.mask)
{
maxswitches = (std::max)(group.switch_count(), maxswitches);
- float const namewidth(ui().get_string_width(group.name));
+ float const namewidth(get_string_width(group.name));
float const switchwidth(m_single_width * maxswitches);
- width = (std::min)((std::max)(namewidth + switchwidth + (lineheight * 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 + (ui().box_lr_border() * 2.0f * aspect), x2 - x1));
+ float const boxwidth((std::max)(width + (lr_border() * 2.0f), x2 - x1));
float const boxleft((1.0f - boxwidth) * 0.5f);
- ui().draw_outlined_box(container(), boxleft, y2 + ui().box_tb_border(), boxleft + boxwidth, y2 + bottom, ui().colors().background_color());
+ ui().draw_outlined_box(container(), boxleft, y2 + tb_border(), boxleft + boxwidth, y2 + 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) - (lineheight * 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(lineheight * SINGLE_TOGGLE_SWITCH_HEIGHT);
- m_nub_width = lineheight * SINGLE_TOGGLE_SWITCH_WIDTH * aspect;
- float const ygap(lineheight * ((DIP_SWITCH_HEIGHT * 0.5f) - SINGLE_TOGGLE_SWITCH_HEIGHT) * 0.5f);
+ float const nubheight(line_height() * SINGLE_TOGGLE_SWITCH_HEIGHT);
+ 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;
- m_clickable_height = (lineheight * DIP_SWITCH_HEIGHT) - (ygap * 2.0f);
+ m_clickable_height = (line_height() * DIP_SWITCH_HEIGHT) - (ygap * 2.0f);
unsigned line(0U);
for (unsigned n = 0; switch_groups().size() > n; ++n)
{
@@ -378,16 +404,15 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
}
// draw the name
- float const liney(y2 + (ui().box_tb_border() * 2.0f) + (lineheight * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) * line));
- ui().draw_text_full(
- container(),
+ float const liney(y2 + (tb_border() * 2.0f) + (line_height() * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) * line));
+ draw_text_normal(
group.name,
- nameleft, liney + (lineheight * (DIP_SWITCH_HEIGHT - 1.0f) / 2.0f), namewidth,
+ nameleft, liney + (line_height() * (DIP_SWITCH_HEIGHT - 1.0f) / 2.0f), namewidth,
text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ ui().colors().text_color());
// draw the group outline
- float const switchbottom(liney + (DIP_SWITCH_HEIGHT * lineheight));
+ float const switchbottom(liney + (DIP_SWITCH_HEIGHT * line_height()));
unsigned const cnt(group.switch_count());
ui().draw_outlined_box(
container(),
@@ -432,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);
- float const x(get_mouse_x() - m_first_nub);
- float const y(get_mouse_y());
+ auto const [cx, y] = pointer_location();
+ if (cx < m_first_nub)
+ return std::make_tuple(IPT_INVALID, false, false);
+
+ 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]);
@@ -459,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);
}
}
}
@@ -468,32 +495,31 @@ bool menu_settings_dip_switches::custom_mouse_down()
}
}
- return false;
+ return std::make_tuple(IPT_INVALID, false, false);
}
-void menu_settings_dip_switches::populate(float &customtop, float &custombottom)
+void menu_settings_dip_switches::populate()
{
// let the base class add items
- menu_confswitch::populate(customtop, custombottom);
+ menu_confswitch::populate();
// use up to about 70% of height for DIP switch display
if (active_switch_groups())
{
m_switch_group_y.resize(switch_groups().size());
- float const lineheight(ui().get_line_height());
- float const groupheight(DIP_SWITCH_HEIGHT * lineheight);
- float const groupspacing(DIP_SWITCH_SPACING * lineheight);
+ float const groupheight(DIP_SWITCH_HEIGHT * line_height());
+ float const groupspacing(DIP_SWITCH_SPACING * line_height());
if ((active_switch_groups() * (groupheight + groupspacing)) > 0.7f)
m_visible_switch_groups = unsigned(0.7f / (groupheight + groupspacing));
else
m_visible_switch_groups = active_switch_groups();
- custombottom = (m_visible_switch_groups * groupheight) + ((m_visible_switch_groups - 1) * groupspacing) + (ui().box_tb_border() * 3.0f);
+ set_custom_space(0.0f, (m_visible_switch_groups * groupheight) + ((m_visible_switch_groups - 1) * groupspacing) + (tb_border() * 3.0f));
}
else
{
m_visible_switch_groups = 0U;
- custombottom = 0.0f;
+ set_custom_space(0.0f, 0.0f);
}
}
diff --git a/src/frontend/mame/ui/confswitch.h b/src/frontend/mame/ui/confswitch.h
index 598b0208085..7f00783b0f1 100644
--- a/src/frontend/mame/ui/confswitch.h
+++ b/src/frontend/mame/ui/confswitch.h
@@ -57,14 +57,14 @@ protected:
menu_confswitch(mame_ui_manager &mui, render_container &container, uint32_t type);
virtual void menu_activated() override;
- virtual void populate(float &customtop, float &custombottom) override;
+ virtual void populate() override;
field_vector const &fields() { return m_fields; }
switch_group_vector const &switch_groups() { return m_switch_groups; }
unsigned active_switch_groups() const { return m_active_switch_groups; }
private:
- virtual void handle(event const *ev) override;
+ virtual bool handle(event const *ev) override;
void find_fields();
@@ -82,11 +82,12 @@ public:
virtual ~menu_settings_dip_switches() override;
protected:
- 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 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 std::tuple<int, bool, bool> custom_pointer_updated(bool changed, ui_event const &uievt) override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
+ virtual void populate() override;
std::vector<float> m_switch_group_y;
unsigned m_visible_switch_groups;
diff --git a/src/frontend/mame/ui/custui.cpp b/src/frontend/mame/ui/custui.cpp
index 08cad10ccdd..9cfd7284d9b 100644
--- a/src/frontend/mame/ui/custui.cpp
+++ b/src/frontend/mame/ui/custui.cpp
@@ -22,6 +22,7 @@
#include "corestr.h"
#include "osdepend.h"
+#include "path.h"
#include <algorithm>
#include <iterator>
@@ -77,6 +78,7 @@ menu_custom_ui::menu_custom_ui(mame_ui_manager &mui, render_container &container
, m_handler(std::move(handler))
, m_currlang(0)
, m_currsysnames(0)
+ , m_currpanels(ui().options().hide_panels())
{
set_process_flags(PROCESS_LR_REPEAT);
set_heading(_("Customize UI"));
@@ -91,7 +93,7 @@ menu_custom_ui::menu_custom_ui(mame_ui_manager &mui, render_container &container
void menu_custom_ui::menu_dismissed()
{
- ui().options().set_value(OPTION_HIDE_PANELS, ui_globals::panels_status, OPTION_PRIORITY_CMDLINE);
+ ui().options().set_value(OPTION_HIDE_PANELS, m_currpanels, OPTION_PRIORITY_CMDLINE);
machine().options().set_value(OPTION_LANGUAGE, m_currlang ? m_languages[m_currlang] : "", OPTION_PRIORITY_CMDLINE);
load_translation(machine().options());
@@ -108,106 +110,110 @@ void menu_custom_ui::menu_dismissed()
// handle
//-------------------------------------------------
-void menu_custom_ui::handle(event const *ev)
+bool menu_custom_ui::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ switch ((uintptr_t)ev->itemref)
{
- switch ((uintptr_t)ev->itemref)
+ case FONT_MENU:
+ if (ev->iptkey == IPT_UI_SELECT)
+ menu::stack_push<menu_font_ui>(ui(), container(), nullptr);
+ break;
+ case COLORS_MENU:
+ if (ev->iptkey == IPT_UI_SELECT)
+ menu::stack_push<menu_colors_ui>(ui(), container());
+ break;
+ case LANGUAGE_MENU:
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
{
- case FONT_MENU:
- if (ev->iptkey == IPT_UI_SELECT)
- menu::stack_push<menu_font_ui>(ui(), container(), nullptr);
- break;
- case COLORS_MENU:
- if (ev->iptkey == IPT_UI_SELECT)
- menu::stack_push<menu_colors_ui>(ui(), container());
- break;
- case LANGUAGE_MENU:
- if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
- {
- if (ev->iptkey == IPT_UI_LEFT)
- --m_currlang;
- else if (ev->iptkey == IPT_UI_RIGHT)
- ++m_currlang;
- else
- m_currlang = 0;
- ev->item->set_subtext(m_languages[m_currlang]);
- ev->item->set_flags(get_arrow_flags<std::size_t>(0, m_languages.size() - 1, m_currlang));
- }
- else if (ev->iptkey == IPT_UI_SELECT)
- {
- // copying list of language names - expensive
- menu::stack_push<menu_selector>(
- ui(), container(), _("UI Language"), std::vector<std::string>(m_languages), m_currlang,
- [this, item = ev->item] (int selection)
- {
- m_currlang = selection;
- item->set_subtext(m_languages[selection]);
- item->set_flags(get_arrow_flags<std::size_t>(0, m_languages.size() - 1, selection));
- });
- }
- break;
- case SYSNAMES_MENU:
- if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
- {
- if (ev->iptkey == IPT_UI_LEFT)
- --m_currsysnames;
- else if (ev->iptkey == IPT_UI_RIGHT)
- ++m_currsysnames;
- else
- m_currsysnames = 0;
- ev->item->set_subtext(m_sysnames[m_currsysnames]);
- ev->item->set_flags(get_arrow_flags<std::size_t>(0, m_sysnames.size() - 1, m_currsysnames));
- }
- else if (ev->iptkey == IPT_UI_SELECT)
- {
- // copying list of file names - expensive
- menu::stack_push<menu_selector>(
- ui(), container(), _("System Names"), std::vector<std::string>(m_sysnames), m_currsysnames,
- [this, item = ev->item] (int selection)
- {
- m_currsysnames = selection;
- item->set_subtext(m_sysnames[selection]);
- item->set_flags(get_arrow_flags<std::size_t>(0, m_sysnames.size() - 1, selection));
- });
- }
- break;
- case HIDE_MENU:
- if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
- {
- if (ev->iptkey == IPT_UI_LEFT)
- --ui_globals::panels_status;
- else if (ev->iptkey == IPT_UI_RIGHT)
- ++ui_globals::panels_status;
- else
- ui_globals::panels_status = 0;
- ev->item->set_subtext(_(HIDE_STATUS[ui_globals::panels_status]));
- ev->item->set_flags(get_arrow_flags<uint16_t>(0, HIDE_BOTH, ui_globals::panels_status));
- }
- else if (ev->iptkey == IPT_UI_SELECT)
- {
- std::vector<std::string> s_sel(std::size(HIDE_STATUS));
- std::transform(std::begin(HIDE_STATUS), std::end(HIDE_STATUS), s_sel.begin(), [](auto &s) { return _(s); });
- menu::stack_push<menu_selector>(
- ui(), container(), _("Show Side Panels"), std::move(s_sel), ui_globals::panels_status,
- [item = ev->item] (int selection)
- {
- ui_globals::panels_status = selection;
- item->set_subtext(_(HIDE_STATUS[selection]));
- item->set_flags(get_arrow_flags<uint16_t>(0, HIDE_BOTH, selection));
- });
- }
- break;
+ if (ev->iptkey == IPT_UI_LEFT)
+ --m_currlang;
+ else if (ev->iptkey == IPT_UI_RIGHT)
+ ++m_currlang;
+ else
+ m_currlang = 0;
+ ev->item->set_subtext(m_languages[m_currlang]);
+ ev->item->set_flags(get_arrow_flags<std::size_t>(0, m_languages.size() - 1, m_currlang));
+ return true;
+ }
+ else if (ev->iptkey == IPT_UI_SELECT)
+ {
+ // copying list of language names - expensive
+ menu::stack_push<menu_selector>(
+ ui(), container(), _("UI Language"), std::vector<std::string>(m_languages), m_currlang,
+ [this, item = ev->item] (int selection)
+ {
+ m_currlang = selection;
+ item->set_subtext(m_languages[selection]);
+ item->set_flags(get_arrow_flags<std::size_t>(0, m_languages.size() - 1, selection));
+ });
+ }
+ break;
+ case SYSNAMES_MENU:
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
+ {
+ if (ev->iptkey == IPT_UI_LEFT)
+ --m_currsysnames;
+ else if (ev->iptkey == IPT_UI_RIGHT)
+ ++m_currsysnames;
+ else
+ m_currsysnames = 0;
+ ev->item->set_subtext(m_sysnames[m_currsysnames]);
+ ev->item->set_flags(get_arrow_flags<std::size_t>(0, m_sysnames.size() - 1, m_currsysnames));
+ return true;
+ }
+ else if (ev->iptkey == IPT_UI_SELECT)
+ {
+ // copying list of file names - expensive
+ menu::stack_push<menu_selector>(
+ ui(), container(), _("System Names"), std::vector<std::string>(m_sysnames), m_currsysnames,
+ [this, item = ev->item] (int selection)
+ {
+ m_currsysnames = selection;
+ item->set_subtext(m_sysnames[selection]);
+ item->set_flags(get_arrow_flags<std::size_t>(0, m_sysnames.size() - 1, selection));
+ });
+ }
+ break;
+ case HIDE_MENU:
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
+ {
+ if (ev->iptkey == IPT_UI_LEFT)
+ --m_currpanels;
+ else if (ev->iptkey == IPT_UI_RIGHT)
+ ++m_currpanels;
+ else
+ m_currpanels = 0;
+ ev->item->set_subtext(_(HIDE_STATUS[m_currpanels]));
+ ev->item->set_flags(get_arrow_flags<uint16_t>(0, HIDE_BOTH, m_currpanels));
+ return true;
+ }
+ else if (ev->iptkey == IPT_UI_SELECT)
+ {
+ std::vector<std::string> s_sel(std::size(HIDE_STATUS));
+ std::transform(std::begin(HIDE_STATUS), std::end(HIDE_STATUS), s_sel.begin(), [](auto &s) { return _(s); });
+ menu::stack_push<menu_selector>(
+ ui(), container(), _("Show Side Panels"), std::move(s_sel), m_currpanels,
+ [this, item = ev->item] (int selection)
+ {
+ m_currpanels = selection;
+ item->set_subtext(_(HIDE_STATUS[selection]));
+ item->set_flags(get_arrow_flags<uint16_t>(0, HIDE_BOTH, selection));
+ });
}
+ break;
}
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_custom_ui::populate(float &customtop, float &custombottom)
+void menu_custom_ui::populate()
{
uint32_t arrow_flags;
item_append(_("Fonts"), 0, (void *)(uintptr_t)FONT_MENU);
@@ -219,8 +225,8 @@ void menu_custom_ui::populate(float &customtop, float &custombottom)
arrow_flags = get_arrow_flags<std::size_t>(0, m_sysnames.size() - 1, m_currsysnames);
item_append(_("System Names"), m_sysnames[m_currsysnames], arrow_flags, (void *)(uintptr_t)SYSNAMES_MENU);
- arrow_flags = get_arrow_flags<uint16_t>(0, HIDE_BOTH, ui_globals::panels_status);
- item_append(_("Show Side Panels"), _(HIDE_STATUS[ui_globals::panels_status]), arrow_flags, (void *)(uintptr_t)HIDE_MENU);
+ arrow_flags = get_arrow_flags<uint16_t>(0, HIDE_BOTH, m_currpanels);
+ item_append(_("Show Side Panels"), _(HIDE_STATUS[m_currpanels]), arrow_flags, (void *)(uintptr_t)HIDE_MENU);
item_append(menu_item_type::SEPARATOR);
}
@@ -250,7 +256,7 @@ void menu_custom_ui::find_languages()
std::sort(
std::next(m_languages.begin()),
m_languages.end(),
- [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); });
+ [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x, y); });
char const *const lang = machine().options().language();
if (*lang)
@@ -259,8 +265,8 @@ void menu_custom_ui::find_languages()
std::next(m_languages.begin()),
m_languages.end(),
lang,
- [] (std::string const &x, char const *y) { return 0 > core_stricmp(x.c_str(), y); });
- if ((m_languages.end() != found) && !core_stricmp(found->c_str(), lang))
+ [] (std::string const &x, char const *y) { return 0 > core_stricmp(x, y); });
+ if ((m_languages.end() != found) && !core_stricmp(*found, lang))
m_currlang = std::distance(m_languages.begin(), found);
}
else
@@ -292,7 +298,7 @@ void menu_custom_ui::find_sysnames()
std::sort(
m_sysnames.begin(),
m_sysnames.end(),
- [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); });
+ [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x, y); });
char const *const names = ui().options().system_names();
if (*names)
@@ -301,9 +307,9 @@ void menu_custom_ui::find_sysnames()
std::next(m_sysnames.begin()),
m_sysnames.end(),
names,
- [] (std::string const &x, char const *y) { return 0 > core_stricmp(x.c_str(), y); });
+ [] (std::string const &x, char const *y) { return 0 > core_stricmp(x, y); });
m_currsysnames = std::distance(m_sysnames.begin(), found);
- if ((m_sysnames.end() == found) || core_stricmp(found->c_str(), names))
+ if ((m_sysnames.end() == found) || core_stricmp(*found, names))
m_sysnames.emplace(found, names);
}
else
@@ -327,6 +333,7 @@ menu_font_ui::menu_font_ui(mame_ui_manager &mui, render_container &container, st
, m_info_min(parse_number<float>(mui.options().get_entry(OPTION_INFOS_SIZE)->minimum()))
, m_info_max(parse_number<float>(mui.options().get_entry(OPTION_INFOS_SIZE)->maximum()))
, m_info_size(mui.options().infos_size())
+ , m_face_changed(false)
, m_changed(false)
, m_actual(0U)
{
@@ -373,17 +380,20 @@ void menu_font_ui::menu_dismissed()
{
ui_options &moptions = ui().options();
- std::string name(m_fonts[m_actual].first);
-#ifdef UI_WINDOWS
- if (name != "default")
+ if (m_face_changed)
{
- if (m_italic)
- name.insert(0, "[I]");
- if (m_bold)
- name.insert(0, "[B]");
- }
+ std::string name(m_fonts[m_actual].first);
+#ifdef UI_WINDOWS
+ if (name != "default")
+ {
+ if (m_italic)
+ name.insert(0, "[I]");
+ if (m_bold)
+ name.insert(0, "[B]");
+ }
#endif
- machine().options().set_value(OPTION_UI_FONT, name, OPTION_PRIORITY_CMDLINE);
+ machine().options().set_value(OPTION_UI_FONT, name, OPTION_PRIORITY_CMDLINE);
+ }
moptions.set_value(OPTION_INFOS_SIZE, m_info_size, OPTION_PRIORITY_CMDLINE);
moptions.set_value(OPTION_FONT_ROWS, m_font_size, OPTION_PRIORITY_CMDLINE);
@@ -399,97 +409,104 @@ void menu_font_ui::menu_dismissed()
// handle
//-------------------------------------------------
-void menu_font_ui::handle(event const *ev)
+bool menu_font_ui::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ switch ((uintptr_t)ev->itemref)
{
- switch ((uintptr_t)ev->itemref)
+ case FONT_SIZE:
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
{
- case FONT_SIZE:
- if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
- {
- m_changed = true;
- if (ev->iptkey == IPT_UI_LEFT)
- --m_font_size;
- else if (ev->iptkey == IPT_UI_RIGHT)
- ++m_font_size;
- else
- m_font_size = parse_number<int>(ui().options().get_entry(OPTION_FONT_ROWS)->default_value().c_str());
- ev->item->set_subtext(string_format("%d", m_font_size));
- ev->item->set_flags(get_arrow_flags(m_font_min, m_font_max, m_font_size));
- }
- break;
+ m_changed = true;
+ if (ev->iptkey == IPT_UI_LEFT)
+ --m_font_size;
+ else if (ev->iptkey == IPT_UI_RIGHT)
+ ++m_font_size;
+ else
+ m_font_size = parse_number<int>(ui().options().get_entry(OPTION_FONT_ROWS)->default_value().c_str());
+ ev->item->set_subtext(string_format("%d", m_font_size));
+ ev->item->set_flags(get_arrow_flags(m_font_min, m_font_max, m_font_size));
+ return true;
+ }
+ break;
- case INFOS_SIZE:
- if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
- {
- m_changed = true;
- if (ev->iptkey == IPT_UI_LEFT)
- m_info_size -= 0.05f;
- else if (ev->iptkey == IPT_UI_RIGHT)
- m_info_size += 0.05f;
- else
- m_info_size = parse_number<float>(ui().options().get_entry(OPTION_INFOS_SIZE)->default_value().c_str());
- ev->item->set_subtext(string_format("%.2f", m_info_size));
- ev->item->set_flags(get_arrow_flags(m_info_min, m_info_max, m_info_size));
- }
- break;
+ case INFOS_SIZE:
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
+ {
+ m_changed = true;
+ if (ev->iptkey == IPT_UI_LEFT)
+ m_info_size -= 0.05f;
+ else if (ev->iptkey == IPT_UI_RIGHT)
+ m_info_size += 0.05f;
+ else
+ m_info_size = parse_number<float>(ui().options().get_entry(OPTION_INFOS_SIZE)->default_value().c_str());
+ ev->item->set_subtext(string_format("%.2f", m_info_size));
+ ev->item->set_flags(get_arrow_flags(m_info_min, m_info_max, m_info_size));
+ return true;
+ }
+ break;
- case MUI_FNT:
- if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
- {
- m_changed = true;
- if (ev->iptkey == IPT_UI_LEFT)
- --m_actual;
- else if (ev->iptkey == IPT_UI_RIGHT)
- ++m_actual;
- else
- m_actual = 0;
- reset(reset_options::REMEMBER_REF);
- }
- else if (ev->iptkey == IPT_UI_SELECT)
- {
- std::vector<std::string> display_names;
- display_names.reserve(m_fonts.size());
- for (auto const &font : m_fonts)
- display_names.emplace_back(font.second);
- menu::stack_push<menu_selector>(
- ui(), container(), _("UI Font"), std::move(display_names), m_actual,
- [this] (int selection)
- {
- m_changed = true;
- m_actual = selection;
- reset(reset_options::REMEMBER_REF);
- });
- }
- break;
+ case MUI_FNT:
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_CLEAR))
+ {
+ m_face_changed = true;
+ m_changed = true;
+ if (ev->iptkey == IPT_UI_LEFT)
+ --m_actual;
+ else if (ev->iptkey == IPT_UI_RIGHT)
+ ++m_actual;
+ else
+ m_actual = 0;
+ reset(reset_options::REMEMBER_REF);
+ }
+ else if (ev->iptkey == IPT_UI_SELECT)
+ {
+ std::vector<std::string> display_names;
+ display_names.reserve(m_fonts.size());
+ for (auto const &font : m_fonts)
+ display_names.emplace_back(font.second);
+ menu::stack_push<menu_selector>(
+ ui(), container(), _("UI Font"), std::move(display_names), m_actual,
+ [this] (int selection)
+ {
+ m_face_changed = true;
+ m_changed = true;
+ m_actual = selection;
+ reset(reset_options::REMEMBER_REF);
+ });
+ }
+ break;
#ifdef UI_WINDOWS
- case MUI_BOLD:
- case MUI_ITALIC:
- if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_SELECT) || (ev->iptkey == IPT_UI_CLEAR))
- {
- m_changed = true;
- bool &val = ((uintptr_t)ev->itemref == MUI_BOLD) ? m_bold : m_italic;
- if (ev->iptkey == IPT_UI_CLEAR)
- val = false;
- else
- val = !val;
- ev->item->set_subtext(val ? _("On") : _("Off"));
- ev->item->set_flags(val ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW);
- }
- break;
-#endif
+ case MUI_BOLD:
+ case MUI_ITALIC:
+ if ((ev->iptkey == IPT_UI_LEFT) || (ev->iptkey == IPT_UI_RIGHT) || (ev->iptkey == IPT_UI_SELECT) || (ev->iptkey == IPT_UI_CLEAR))
+ {
+ m_face_changed = true;
+ m_changed = true;
+ bool &val = ((uintptr_t)ev->itemref == MUI_BOLD) ? m_bold : m_italic;
+ if (ev->iptkey == IPT_UI_CLEAR)
+ val = false;
+ else
+ val = !val;
+ ev->item->set_subtext(val ? _("On") : _("Off"));
+ ev->item->set_flags(val ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW);
+ return true;
}
+ break;
+#endif
}
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_font_ui::populate(float &customtop, float &custombottom)
+void menu_font_ui::populate()
{
// set filter arrow
uint32_t arrow_flags;
@@ -514,24 +531,33 @@ void menu_font_ui::populate(float &customtop, float &custombottom)
item_append(_("Infos text size"), string_format("%.2f", m_info_size), arrow_flags, (void *)(uintptr_t)INFOS_SIZE);
item_append(menu_item_type::SEPARATOR);
+}
+
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
+void menu_font_ui::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(0.0f, line_height() + 3.0f * tb_border());
}
//-------------------------------------------------
// 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)
{
char const *const bottomtext[] = { _("Sample text - Lorem ipsum dolor sit amet, consectetur adipiscing elit.") };
draw_text_box(
std::begin(bottomtext), std::end(bottomtext),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), UI_GREEN_COLOR, m_info_size);
+ ui().colors().text_color(), UI_GREEN_COLOR, ui().get_line_height(m_info_size));
}
}
@@ -583,9 +609,8 @@ void menu_colors_ui::menu_dismissed()
// handle
//-------------------------------------------------
-void menu_colors_ui::handle(event const *ev)
+bool menu_colors_ui::handle(event const *ev)
{
- // process the menu
if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT)
{
if ((uintptr_t)ev->itemref != MUI_RESTORE)
@@ -595,15 +620,18 @@ void menu_colors_ui::handle(event const *ev)
else
{
restore_colors();
+ return true;
}
}
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_colors_ui::populate(float &customtop, float &custombottom)
+void menu_colors_ui::populate()
{
item_append(_("color-option", "Normal text"), 0, (void *)(uintptr_t)MUI_TEXT_COLOR);
item_append(_("color-option", "Selected color"), 0, (void *)(uintptr_t)MUI_SELECTED_COLOR);
@@ -625,36 +653,38 @@ void menu_colors_ui::populate(float &customtop, float &custombottom)
item_append(menu_item_type::SEPARATOR);
item_append(_("Restore default colors"), 0, (void *)(uintptr_t)MUI_RESTORE);
+}
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_colors_ui::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(0.0f, line_height() + 3.0f * tb_border());
}
//-------------------------------------------------
// 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)) };
draw_text_box(
std::begin(bottomtext), std::end(bottomtext),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
// compute maxwidth
char const *const topbuf = _("Menu Preview");
- const float lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container());
- float width;
- ui().draw_text_full(
- container(),
- topbuf,
- 0.0f, 0.0f, 1.0f,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &width, nullptr);
- float maxwidth = width + 2.0f * lr_border;
+ float width = get_string_width(topbuf);
+ float maxwidth = width + 2.0f * lr_border();
std::string sampletxt[5];
@@ -666,53 +696,45 @@ void menu_colors_ui::custom_render(void *selectedref, float top, float bottom, f
for (auto & elem: sampletxt)
{
- ui().draw_text_full(
- container(),
- elem,
- 0.0f, 0.0f, 1.0f,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &width, nullptr);
- width += 2 * lr_border;
- maxwidth = std::max(maxwidth, width);
+ width = get_string_width(elem);
+ maxwidth = std::max(maxwidth, width + 2.0f * lr_border());
}
// compute our bounds for header
- float x1 = origx2 + 2.0f * lr_border;
+ float x1 = origx2 + 2.0f * lr_border();
float x2 = x1 + maxwidth;
float y1 = origy1;
- float y2 = y1 + bottom - ui().box_tb_border();
+ float y2 = y1 + bottom - tb_border();
// 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 += ui().box_tb_border();
- y2 -= ui().box_tb_border();
+ x1 += lr_border();
+ x2 -= lr_border();
+ y1 += tb_border();
+ y2 -= tb_border();
// draw the text within it
- ui().draw_text_full(
- container(),
+ draw_text_normal(
topbuf,
x1, y1, x2 - x1,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(), nullptr, nullptr);
+ ui().colors().text_color());
// compute our bounds for menu preview
- float line_height = ui().get_line_height();
- x1 -= lr_border;
- x2 += lr_border;
- y1 = y2 + 2.0f * ui().box_tb_border();
- y2 = y1 + 5.0f * line_height + 2.0f * ui().box_tb_border();
+ x1 -= lr_border();
+ x2 += lr_border();
+ y1 = y2 + 2.0f * tb_border();
+ y2 = y1 + 5.0f * line_height() + 2.0f * tb_border();
// draw a box
ui().draw_outlined_box(container(), x1, y1, x2, y2, m_color_table[MUI_BACKGROUND_COLOR].color);
// take off the borders
- x1 += lr_border;
- x2 -= lr_border;
- y1 += ui().box_tb_border();
+ x1 += lr_border();
+ x2 -= lr_border();
+ y1 += tb_border();
// draw normal text
ui().draw_text_full(
@@ -720,8 +742,10 @@ void menu_colors_ui::custom_render(void *selectedref, float top, float bottom, f
sampletxt[0],
x1, y1, x2 - x1,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, m_color_table[MUI_TEXT_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color, nullptr, nullptr);
- y1 += line_height;
+ mame_ui_manager::NORMAL, m_color_table[MUI_TEXT_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color,
+ nullptr, nullptr,
+ line_height());
+ y1 += line_height();
// draw subitem text
ui().draw_text_full(
@@ -729,28 +753,34 @@ void menu_colors_ui::custom_render(void *selectedref, float top, float bottom, f
sampletxt[1],
x1, y1, x2 - x1,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, m_color_table[MUI_SUBITEM_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color, nullptr, nullptr);
- y1 += line_height;
+ mame_ui_manager::NORMAL, m_color_table[MUI_SUBITEM_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color,
+ nullptr, nullptr,
+ line_height());
+ y1 += line_height();
// draw selected text
- highlight(x1, y1, x2, y1 + line_height, m_color_table[MUI_SELECTED_BG_COLOR].color);
+ highlight(x1, y1, x2, y1 + line_height(), m_color_table[MUI_SELECTED_BG_COLOR].color);
ui().draw_text_full(
container(),
sampletxt[2],
x1, y1, x2 - x1,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, m_color_table[MUI_SELECTED_COLOR].color, m_color_table[MUI_SELECTED_BG_COLOR].color, nullptr, nullptr);
- y1 += line_height;
+ mame_ui_manager::NORMAL, m_color_table[MUI_SELECTED_COLOR].color, m_color_table[MUI_SELECTED_BG_COLOR].color,
+ nullptr, nullptr,
+ line_height());
+ y1 += line_height();
// draw mouse over text
- highlight(x1, y1, x2, y1 + line_height, m_color_table[MUI_MOUSEOVER_BG_COLOR].color);
+ highlight(x1, y1, x2, y1 + line_height(), m_color_table[MUI_MOUSEOVER_BG_COLOR].color);
ui().draw_text_full(
container(),
sampletxt[3],
x1, y1, x2 - x1,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, m_color_table[MUI_MOUSEOVER_COLOR].color, m_color_table[MUI_MOUSEOVER_BG_COLOR].color, nullptr, nullptr);
- y1 += line_height;
+ mame_ui_manager::NORMAL, m_color_table[MUI_MOUSEOVER_COLOR].color, m_color_table[MUI_MOUSEOVER_BG_COLOR].color,
+ nullptr, nullptr,
+ line_height());
+ y1 += line_height();
// draw clone text
ui().draw_text_full(
@@ -758,8 +788,9 @@ void menu_colors_ui::custom_render(void *selectedref, float top, float bottom, f
sampletxt[4],
x1, y1, x2 - x1,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, m_color_table[MUI_CLONE_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color, nullptr, nullptr);
-
+ mame_ui_manager::NORMAL, m_color_table[MUI_CLONE_COLOR].color, m_color_table[MUI_TEXT_BG_COLOR].color,
+ nullptr, nullptr,
+ line_height());
}
//-------------------------------------------------
@@ -792,88 +823,89 @@ menu_rgb_ui::menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_
// handle
//-------------------------------------------------
-void menu_rgb_ui::handle(event const *ev)
+bool menu_rgb_ui::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ switch (ev->iptkey)
{
- switch (ev->iptkey)
+ case IPT_UI_LEFT:
+ case IPT_UI_RIGHT:
{
- case IPT_UI_LEFT:
- case IPT_UI_RIGHT:
+ bool changed = false;
+ int updated = (IPT_UI_LEFT == ev->iptkey) ? -1 : 1;
+ switch (uintptr_t(ev->itemref))
{
- bool changed = false;
- int updated = (IPT_UI_LEFT == ev->iptkey) ? -1 : 1;
- switch (uintptr_t(ev->itemref))
+ case RGB_ALPHA:
+ updated += m_color->a();
+ if ((0 <= updated) && (255 >= updated))
{
- case RGB_ALPHA:
- updated += m_color->a();
- if ((0 <= updated) && (255 >= updated))
- {
- m_color->set_a(updated);
- changed = true;
- }
- break;
- case RGB_RED:
- updated += m_color->r();
- if ((0 <= updated) && (255 >= updated))
- {
- m_color->set_r(updated);
- changed = true;
- }
- break;
- case RGB_GREEN:
- updated += m_color->g();
- if ((0 <= updated) && (255 >= updated))
- {
- m_color->set_g(updated);
- changed = true;
- }
- break;
- case RGB_BLUE:
- updated += m_color->b();
- if ((0 <= updated) && (255 >= updated))
- {
- m_color->set_b(updated);
- changed = true;
- }
- break;
+ m_color->set_a(updated);
+ changed = true;
}
- if (changed)
+ break;
+ case RGB_RED:
+ updated += m_color->r();
+ if ((0 <= updated) && (255 >= updated))
{
- ev->item->set_subtext(string_format("%3u", updated));
- ev->item->set_flags(get_arrow_flags<uint8_t>(0, 255, updated));
+ m_color->set_r(updated);
+ changed = true;
}
- }
- break;
-
- case IPT_UI_SELECT:
- if (uintptr_t(ev->itemref) == PALETTE_CHOOSE)
- {
- menu::stack_push<menu_palette_sel>(ui(), container(), *m_color);
break;
- }
- [[fallthrough]];
- case IPT_SPECIAL:
- switch (uintptr_t(ev->itemref))
- {
- case RGB_ALPHA:
- case RGB_RED:
case RGB_GREEN:
+ updated += m_color->g();
+ if ((0 <= updated) && (255 >= updated))
+ {
+ m_color->set_g(updated);
+ changed = true;
+ }
+ break;
case RGB_BLUE:
- inkey_special(ev);
+ updated += m_color->b();
+ if ((0 <= updated) && (255 >= updated))
+ {
+ m_color->set_b(updated);
+ changed = true;
+ }
break;
}
+ if (changed)
+ {
+ ev->item->set_subtext(string_format("%3u", updated));
+ ev->item->set_flags(get_arrow_flags<uint8_t>(0, 255, updated));
+ return true;
+ }
+ }
+ break;
+
+ case IPT_UI_SELECT:
+ if (uintptr_t(ev->itemref) == PALETTE_CHOOSE)
+ {
+ menu::stack_push<menu_palette_sel>(ui(), container(), *m_color);
break;
}
+ [[fallthrough]];
+ case IPT_SPECIAL:
+ switch (uintptr_t(ev->itemref))
+ {
+ case RGB_ALPHA:
+ case RGB_RED:
+ case RGB_GREEN:
+ case RGB_BLUE:
+ return inkey_special(ev);
+ }
+ break;
}
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_rgb_ui::populate(float &customtop, float &custombottom)
+void menu_rgb_ui::populate()
{
// set filter arrow
std::string s_text = std::string(m_search).append("_");
@@ -914,55 +946,58 @@ void menu_rgb_ui::populate(float &customtop, float &custombottom)
item_append(menu_item_type::SEPARATOR);
item_append(_("Choose from palette"), 0, (void *)(uintptr_t)PALETTE_CHOOSE);
item_append(menu_item_type::SEPARATOR);
+}
+
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_rgb_ui::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
+ set_custom_space(0.0f, line_height() + 3.0f * tb_border());
}
//-------------------------------------------------
// 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)
{
- const float lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container());
float maxwidth = origx2 - origx1;
std::string sampletxt(_("Color preview:"));
- float width;
- ui().draw_text_full(
- container(),
- sampletxt,
- 0.0f, 0.0f, 1.0f,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
- &width);
- width += 2 * lr_border;
+ float width = get_string_width(sampletxt);
+ width += 2 * lr_border();
maxwidth = std::max(origx2 - origx1, width);
// compute our bounds
float x1 = 0.5f - 0.5f * maxwidth;
float x2 = x1 + maxwidth;
- float y1 = origy2 + ui().box_tb_border();
+ float y1 = origy2 + tb_border();
float y2 = origy2 + bottom;
// draw a box - force black to ensure the text is legible
ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t::black());
// take off the borders
- x1 += lr_border;
- y1 += ui().box_tb_border();
+ x1 += lr_border();
+ y1 += tb_border();
// draw the text label - force white to ensure it's legible
ui().draw_text_full(
container(),
sampletxt,
- x1, y1, width - lr_border,
+ x1, y1, width - lr_border(),
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, rgb_t::white(), rgb_t::black());
+ mame_ui_manager::NORMAL, rgb_t::white(), rgb_t::black(),
+ nullptr, nullptr,
+ line_height());
- x1 += width + (lr_border * 2.0f);
- x2 -= lr_border;
- y2 -= ui().box_tb_border();
+ x1 += width + (lr_border() * 2.0f);
+ x2 -= lr_border();
+ y2 -= tb_border();
// add white under half the sample swatch to make alpha effects visible
container().add_rect((x1 + x2) * 0.5f, y1, x2, y2, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
@@ -973,7 +1008,7 @@ void menu_rgb_ui::custom_render(void *selectedref, float top, float bottom, floa
// handle special key event
//-------------------------------------------------
-void menu_rgb_ui::inkey_special(const event *menu_event)
+bool menu_rgb_ui::inkey_special(const event *menu_event)
{
if (menu_event->iptkey == IPT_UI_SELECT)
{
@@ -1016,11 +1051,16 @@ void menu_rgb_ui::inkey_special(const event *menu_event)
menu_event->item->set_subtext("_");
menu_event->item->set_flags(0);
}
+ return true;
}
- else if (m_key_active)
+ else if (m_key_active && input_character(m_search, 3, menu_event->unichar, uchar_is_digit))
{
- input_character(m_search, 3, menu_event->unichar, uchar_is_digit);
menu_event->item->set_subtext(m_search + "_");
+ return true;
+ }
+ else
+ {
+ return false;
}
}
@@ -1050,9 +1090,8 @@ menu_palette_sel::menu_palette_sel(mame_ui_manager &mui, render_container &conta
// handle
//-------------------------------------------------
-void menu_palette_sel::handle(event const *ev)
+bool menu_palette_sel::handle(event const *ev)
{
- // process the menu
if (ev && ev->itemref)
{
if (ev->iptkey == IPT_UI_SELECT)
@@ -1062,13 +1101,15 @@ void menu_palette_sel::handle(event const *ev)
stack_pop();
}
}
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_palette_sel::populate(float &customtop, float &custombottom)
+void menu_palette_sel::populate()
{
for (unsigned x = 0; x < std::size(s_palette); ++x)
item_append(_("color-preset", s_palette[x].first), s_palette[x].second, FLAG_COLOR_BOX, (void *)(uintptr_t)(x + 1));
diff --git a/src/frontend/mame/ui/custui.h b/src/frontend/mame/ui/custui.h
index 6a5e09ae954..be9df136f7e 100644
--- a/src/frontend/mame/ui/custui.h
+++ b/src/frontend/mame/ui/custui.h
@@ -16,6 +16,8 @@
#include "ui/menu.h"
#include <functional>
+#include <string>
+#include <vector>
namespace ui {
@@ -33,8 +35,8 @@ protected:
virtual void menu_dismissed() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
void find_languages();
void find_sysnames();
@@ -44,6 +46,7 @@ private:
std::vector<std::string> m_sysnames;
std::size_t m_currlang;
std::size_t m_currsysnames;
+ u8 m_currpanels;
};
//-------------------------------------------------
@@ -56,12 +59,13 @@ public:
menu_font_ui(mame_ui_manager &mui, render_container &container, std::function<void (bool)> &&handler);
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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_dismissed() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
void list();
@@ -71,6 +75,7 @@ private:
int m_font_size;
float const m_info_min, m_info_max;
float m_info_size;
+ bool m_face_changed;
bool m_changed;
std::uint16_t m_actual;
@@ -90,7 +95,8 @@ public:
menu_colors_ui(mame_ui_manager &mui, render_container &container);
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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_dismissed() override;
private:
@@ -121,8 +127,8 @@ private:
const char *option;
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
s_color_table m_color_table[MUI_RESTORE];
void restore_colors();
@@ -138,7 +144,8 @@ public:
menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_t *color, std::string &&title);
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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;
private:
enum
@@ -150,16 +157,15 @@ private:
PALETTE_CHOOSE
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
- void inkey_special(const event *menu_event);
+ bool inkey_special(const event *menu_event);
rgb_t *m_color;
std::string m_search;
bool m_key_active;
int m_lock_ref;
- std::string m_title;
};
//-------------------------------------------------
@@ -172,8 +178,8 @@ public:
menu_palette_sel(mame_ui_manager &mui, render_container &container, rgb_t &_color);
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
static std::pair<const char *, const char *> const s_palette[];
rgb_t &m_original;
diff --git a/src/frontend/mame/ui/datmenu.cpp b/src/frontend/mame/ui/datmenu.cpp
index c99a09c367f..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,29 +39,33 @@ 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()))
{
- if (image.filename())
+ if (image.loaded_through_softlist())
{
- m_list = strensure(image.software_list_name());
+ m_list = image.software_list_name();
m_short = image.software_entry()->shortname();
m_long = image.software_entry()->longname();
m_parent = image.software_entry()->parentname();
+ 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++;
}
}
@@ -76,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++;
}
}
@@ -166,207 +178,238 @@ void menu_dats_view::add_info_text(text_layout &layout, std::string_view text, r
// handle
//-------------------------------------------------
-void menu_dats_view::handle(event const *ev)
+bool menu_dats_view::handle(event const *ev)
{
if (ev)
{
+ // don't bother with parent event handling if we need to redraw anyway
switch (ev->iptkey)
{
case IPT_UI_LEFT:
- if (m_actual > 0)
+ if (m_current_tab > 0)
{
- m_actual--;
+ 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())
+ if ((m_current_tab + 1) < m_items_list.size())
{
- m_actual++;
+ m_current_tab++;
+ m_tab_line = std::make_pair(1.0F, 0.0F);
+ m_clicked_tab = -1;
reset_layout();
+ return true;
}
break;
-
- default:
- handle_key(ev->iptkey);
}
}
+
+ return menu_textbox::handle(ev);
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_dats_view::populate(float &customtop, float &custombottom)
+void menu_dats_view::populate()
+{
+}
+
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_dats_view::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
- customtop = 2.0f * ui().get_line_height() + 4.0f * ui().box_tb_border();
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
+ 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());
}
//-------------------------------------------------
// 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;
- float const lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container());
- ui().draw_text_full(
- container(),
- driver,
- 0.0f, 0.0f, 1.0f,
- ui::text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &width, nullptr);
- 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 * ui().box_tb_border() - ui().get_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 += ui().box_tb_border();
-
- ui().draw_text_full(
- container(),
+ 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,
- mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color());
-
- maxwidth = 0;
- for (auto const &elem : m_items_list)
- {
- ui().draw_text_full(
- container(),
- elem.label,
- 0.0f, 0.0f, 1.0f,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
- &width, nullptr);
- maxwidth += width;
- }
+ ui().colors().text_color());
- 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 + ui().box_tb_border();
- y2 += ui().get_line_height() + 2.0f * ui().box_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 += ui().box_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);
-
- 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();
- ui().draw_text_full(
- container(),
- elem.label,
- x1, y1, 1.0f,
- text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NONE, fcolor, bcolor,
- &width, nullptr);
+ auto &elem(m_items_list[i]);
- 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);
- x1 += width + space;
- ++x;
+ 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());
}
// bottom
if (!m_items_list.empty())
{
- std::string const revision(util::string_format(_("Revision: %1$s"), m_items_list[m_actual].revision));
- ui().draw_text_full(
- container(),
+ 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,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
- &width, nullptr);
- width += 2 * lr_border;
- maxwidth = std::max(origx2 - origx1, width);
+ 0.0F, 0.0F, 1.0F,
+ 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 + ui().box_tb_border();
+ y1 = origy2 + tb_border();
y2 = origy2 + bottom;
// 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 += ui().box_tb_border();
-
// draw the text within it
- ui().draw_text_full(
- container(),
+ 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,
- mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color());
+ 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);
}
//-------------------------------------------------
@@ -377,35 +420,21 @@ 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(ui().create_layout(container(), width));
+ layout.emplace(create_layout(width));
add_info_text(*layout, buffer, ui().colors().text_color());
lines = std::numeric_limits<int>::max();
}
}
-//-------------------------------------------------
-// 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 79b9bd8c1f6..e8e7cea2887 100644
--- a/src/frontend/mame/ui/datmenu.h
+++ b/src/frontend/mame/ui/datmenu.h
@@ -43,33 +43,36 @@ public:
static void add_info_text(text_layout &layout, std::string_view text, rgb_t color, float size = 1.0f);
protected:
- 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 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 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;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ std::pair<float, float> bounds;
+ };
- void get_data(std::string &buffer);
- void get_data_sw(std::string &buffer);
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
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/devctrl.h b/src/frontend/mame/ui/devctrl.h
index ec9dc0d2928..ad89f726172 100644
--- a/src/frontend/mame/ui/devctrl.h
+++ b/src/frontend/mame/ui/devctrl.h
@@ -37,8 +37,8 @@ protected:
int count() { return m_count; }
int current_index();
- void previous();
- void next();
+ bool previous();
+ bool next();
std::string current_display_name();
uint32_t current_display_flags();
@@ -82,7 +82,7 @@ int menu_device_control<DeviceType>::current_index()
//-------------------------------------------------
template<class DeviceType>
-void menu_device_control<DeviceType>::previous()
+bool menu_device_control<DeviceType>::previous()
{
// left arrow - rotate left through devices
if (m_device && (1 < m_count))
@@ -96,6 +96,7 @@ void menu_device_control<DeviceType>::previous()
m_device = iter.byindex(index);
reset(reset_options::REMEMBER_POSITION);
}
+ return false; // triggers an item reset on changes anyway
}
@@ -104,7 +105,7 @@ void menu_device_control<DeviceType>::previous()
//-------------------------------------------------
template<class DeviceType>
-void menu_device_control<DeviceType>::next()
+bool menu_device_control<DeviceType>::next()
{
// right arrow - rotate right through cassette devices
if (m_device && (1 < m_count))
@@ -118,6 +119,7 @@ void menu_device_control<DeviceType>::next()
m_device = iter.byindex(index);
reset(reset_options::REMEMBER_POSITION);
}
+ return false; // triggers an item reset on changes anyway
}
diff --git a/src/frontend/mame/ui/devopt.cpp b/src/frontend/mame/ui/devopt.cpp
index 0f20c35486f..8d841c7d131 100644
--- a/src/frontend/mame/ui/devopt.cpp
+++ b/src/frontend/mame/ui/devopt.cpp
@@ -15,6 +15,10 @@
#include "romload.h"
#include "screen.h"
+#include "util/unicode.h"
+
+#include <locale>
+
namespace ui {
@@ -44,7 +48,7 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
if (!layout || (layout->width() != width))
{
rgb_t const color = ui().colors().text_color();
- layout.emplace(ui().create_layout(container(), width));
+ layout.emplace(create_layout(width));
machine_config &mconfig(const_cast<machine_config &>(machine().config()));
machine_config::token const tok(mconfig.begin_configuration(mconfig.root_device()));
@@ -53,6 +57,13 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
if (!d.configured())
d.config_complete();
+ // get decimal separator
+ std::string point;
+ {
+ wchar_t const s(std::use_facet<std::numpunct<wchar_t> >(std::locale()).decimal_point());
+ point = utf8_from_wstring(std::wstring_view(&s, 1));
+ }
+
layout->add_text(
util::string_format(
m_mounted
@@ -91,7 +102,7 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
if (d > 0)
{
size_t dpos = hz.length() - d;
- hz.insert(dpos, ".");
+ hz.insert(dpos, point);
size_t last = hz.find_last_not_of('0');
hz = hz.substr(0, last + (last != dpos ? 1 : 0));
}
@@ -100,8 +111,8 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
layout->add_text(
util::string_format(
(count > 1)
- ? ((clock != 0) ? " %1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : " %1$d" UTF8_MULTIPLY "%2$s\n")
- : ((clock != 0) ? " %2$s %3$s" UTF8_NBSP "%4$s\n" : " %2$s\n"),
+ ? ((clock != 0) ? u8" %1$d×%2$s %3$s\u00a0%4$s\n" : u8" %1$d×%2$s\n")
+ : ((clock != 0) ? u8" %2$s %3$s\u00a0%4$s\n" : " %2$s\n"),
count, name, hz,
(d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz")),
color);
@@ -127,7 +138,7 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
if (valid)
{
size_t dpos = hz.length() - 6;
- hz.insert(dpos, ".");
+ hz.insert(dpos, point);
size_t last = hz.find_last_not_of('0');
hz = hz.substr(0, last + (last != dpos ? 1 : 0));
}
@@ -136,8 +147,8 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
layout->add_text(
util::string_format(
(screen.orientation() & ORIENTATION_SWAP_XY)
- ? _(" Screen '%1$s': %2$d \xC3\x97 %3$d (V) %4$s\xC2\xA0Hz\n")
- : _(" Screen '%1$s': %2$d \xC3\x97 %3$d (H) %4$s\xC2\xA0Hz\n"),
+ ? _(u8" Screen '%1$s': %2$d × %3$d (V) %4$s\u00a0Hz\n")
+ : _(u8" Screen '%1$s': %2$d × %3$d (H) %4$s\u00a0Hz\n"),
screen.tag(),
visarea.width(),
visarea.height(),
@@ -173,7 +184,7 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
if (d > 0)
{
size_t dpos = hz.length() - d;
- hz.insert(dpos, ".");
+ hz.insert(dpos, point);
size_t last = hz.find_last_not_of('0');
hz = hz.substr(0, last + (last != dpos ? 1 : 0));
}
@@ -182,8 +193,8 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
layout->add_text(
util::string_format(
(count > 1)
- ? ((clock != 0) ? " %1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : " %1$d" UTF8_MULTIPLY "%2$s\n")
- : ((clock != 0) ? " %2$s %3$s" UTF8_NBSP "%4$s\n" : " %2$s\n"),
+ ? ((clock != 0) ? u8" %1$d×%2$s %3$s\u00a0%4$s\n" : u8" %1$d×%2$s\n")
+ : ((clock != 0) ? u8" %2$s %3$s\u00a0%4$s\n" : " %2$s\n"),
count, sound.device().name(), hz,
(d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz")),
color);
@@ -353,14 +364,8 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
width = layout->actual_width();
}
-void menu_device_config::populate(float &customtop, float &custombottom)
-{
-}
-
-void menu_device_config::handle(event const *ev)
+void menu_device_config::populate()
{
- if (ev)
- handle_key(ev->iptkey);
}
} // namespace ui
diff --git a/src/frontend/mame/ui/devopt.h b/src/frontend/mame/ui/devopt.h
index 74c81a75809..2e67cd4524f 100644
--- a/src/frontend/mame/ui/devopt.h
+++ b/src/frontend/mame/ui/devopt.h
@@ -28,8 +28,7 @@ protected:
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() 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 1aa4cb6a8fe..083dfbeba79 100644
--- a/src/frontend/mame/ui/dirmenu.cpp
+++ b/src/frontend/mame/ui/dirmenu.cpp
@@ -9,11 +9,10 @@
*********************************************************************/
#include "emu.h"
+#include "ui/dirmenu.h"
#include "ui/ui.h"
-#include "ui/dirmenu.h"
#include "ui/utils.h"
-#include "ui/optsmenu.h"
#include "emuopts.h"
#include "fileio.h"
@@ -29,49 +28,46 @@ namespace ui {
namespace {
-constexpr int ADDING = 1;
-constexpr int CHANGE = 2;
-
struct folders_entry
{
const char *name;
const char *option;
- const int action;
};
const folders_entry f_folders[] =
{
- { N_p("path-option", "ROMs"), OPTION_MEDIAPATH, ADDING },
- { N_p("path-option", "Software Media"), OPTION_SWPATH, CHANGE },
- { N_p("path-option", "Sound Samples"), OPTION_SAMPLEPATH, ADDING },
- { N_p("path-option", "Artwork"), OPTION_ARTPATH, ADDING },
- { N_p("path-option", "Crosshairs"), OPTION_CROSSHAIRPATH, ADDING },
- { N_p("path-option", "Cheat Files"), OPTION_CHEATPATH, ADDING },
- { N_p("path-option", "Plugins"), OPTION_PLUGINSPATH, ADDING },
- { N_p("path-option", "UI Translations"), OPTION_LANGUAGEPATH, CHANGE },
- { N_p("path-option", "INIs"), OPTION_INIPATH, ADDING },
- { N_p("path-option", "UI Settings"), OPTION_UI_PATH, CHANGE },
- { N_p("path-option", "Plugin Data"), OPTION_PLUGINDATAPATH, CHANGE },
- { N_p("path-option", "DATs"), OPTION_HISTORY_PATH, ADDING },
- { N_p("path-option", "Category INIs"), OPTION_CATEGORYINI_PATH, CHANGE },
- { N_p("path-option", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY, ADDING },
- { N_p("path-option", "Icons"), OPTION_ICONS_PATH, ADDING },
- { N_p("path-option", "Control Panels"), OPTION_CPANELS_PATH, ADDING },
- { N_p("path-option", "Cabinets"), OPTION_CABINETS_PATH, ADDING },
- { N_p("path-option", "Marquees"), OPTION_MARQUEES_PATH, ADDING },
- { N_p("path-option", "PCBs"), OPTION_PCBS_PATH, ADDING },
- { N_p("path-option", "Flyers"), OPTION_FLYERS_PATH, ADDING },
- { N_p("path-option", "Title Screens"), OPTION_TITLES_PATH, ADDING },
- { N_p("path-option", "Game Endings"), OPTION_ENDS_PATH, ADDING },
- { N_p("path-option", "Bosses"), OPTION_BOSSES_PATH, ADDING },
- { N_p("path-option", "Artwork Previews"), OPTION_ARTPREV_PATH, ADDING },
- { N_p("path-option", "Select"), OPTION_SELECT_PATH, ADDING },
- { N_p("path-option", "Game Over Screens"), OPTION_GAMEOVER_PATH, ADDING },
- { N_p("path-option", "HowTo"), OPTION_HOWTO_PATH, ADDING },
- { N_p("path-option", "Logos"), OPTION_LOGOS_PATH, ADDING },
- { N_p("path-option", "Scores"), OPTION_SCORES_PATH, ADDING },
- { N_p("path-option", "Versus"), OPTION_VERSUS_PATH, ADDING },
- { N_p("path-option", "Covers"), OPTION_COVER_PATH, ADDING }
+ { N_p("path-option", "ROMs"), OPTION_MEDIAPATH },
+ { N_p("path-option", "Software Media"), OPTION_SWPATH },
+ { N_p("path-option", "Sound Samples"), OPTION_SAMPLEPATH },
+ { N_p("path-option", "Artwork"), OPTION_ARTPATH },
+ { N_p("path-option", "Crosshairs"), OPTION_CROSSHAIRPATH },
+ { N_p("path-option", "Cheat Files"), OPTION_CHEATPATH },
+ { N_p("path-option", "Plugins"), OPTION_PLUGINSPATH },
+ { N_p("path-option", "UI Translations"), OPTION_LANGUAGEPATH },
+ { N_p("path-option", "Software Lists"), OPTION_HASHPATH },
+ { N_p("path-option", "INIs"), OPTION_INIPATH },
+ { N_p("path-option", "UI Settings"), OPTION_UI_PATH },
+ { N_p("path-option", "Plugin Data"), OPTION_PLUGINDATAPATH },
+ { N_p("path-option", "DATs"), OPTION_HISTORY_PATH },
+ { N_p("path-option", "Category INIs"), OPTION_CATEGORYINI_PATH },
+ { N_p("path-option", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY },
+ { N_p("path-option", "Icons"), OPTION_ICONS_PATH },
+ { N_p("path-option", "Control Panels"), OPTION_CPANELS_PATH },
+ { N_p("path-option", "Cabinets"), OPTION_CABINETS_PATH },
+ { N_p("path-option", "Marquees"), OPTION_MARQUEES_PATH },
+ { N_p("path-option", "PCBs"), OPTION_PCBS_PATH },
+ { N_p("path-option", "Flyers"), OPTION_FLYERS_PATH },
+ { N_p("path-option", "Title Screens"), OPTION_TITLES_PATH },
+ { N_p("path-option", "Game Endings"), OPTION_ENDS_PATH },
+ { N_p("path-option", "Bosses"), OPTION_BOSSES_PATH },
+ { N_p("path-option", "Artwork Previews"), OPTION_ARTPREV_PATH },
+ { N_p("path-option", "Select"), OPTION_SELECT_PATH },
+ { N_p("path-option", "Game Over Screens"), OPTION_GAMEOVER_PATH },
+ { N_p("path-option", "HowTo"), OPTION_HOWTO_PATH },
+ { N_p("path-option", "Logos"), OPTION_LOGOS_PATH },
+ { N_p("path-option", "Scores"), OPTION_SCORES_PATH },
+ { N_p("path-option", "Versus"), OPTION_VERSUS_PATH },
+ { N_p("path-option", "Covers"), OPTION_COVER_PATH }
};
@@ -85,8 +81,8 @@ public:
menu_remove_folder(mame_ui_manager &mui, render_container &container, int ref);
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
std::string m_searchpath;
int const m_ref;
@@ -118,7 +114,7 @@ menu_remove_folder::menu_remove_folder(mame_ui_manager &mui, render_container &c
// handle
//-------------------------------------------------
-void menu_remove_folder::handle(event const *ev)
+bool menu_remove_folder::handle(event const *ev)
{
// process the menu
if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT)
@@ -135,20 +131,20 @@ void menu_remove_folder::handle(event const *ev)
if (ui().options().exists(f_folders[m_ref].option))
ui().options().set_value(f_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
else if (machine().options().value(f_folders[m_ref].option) != tmppath)
- {
machine().options().set_value(f_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
- }
reset_parent(reset_options::REMEMBER_REF);
stack_pop();
}
+
+ return false;
}
//-------------------------------------------------
// populate menu
//-------------------------------------------------
-void menu_remove_folder::populate(float &customtop, float &custombottom)
+void menu_remove_folder::populate()
{
int folders_count = 0;
for (auto & elem : m_folders)
@@ -165,21 +161,24 @@ void menu_remove_folder::populate(float &customtop, float &custombottom)
class menu_add_change_folder : public menu
{
public:
- menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref);
+ menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref, bool multipath);
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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 bool custom_ui_cancel() override { return !m_search.empty(); }
+ virtual bool custom_ui_back() override { return !m_search.empty(); }
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+
+ void update_search();
int const m_ref;
+ bool const m_multipath;
std::string m_current_path;
std::string m_search;
- bool const m_change;
std::vector<std::string> m_folders;
};
@@ -187,10 +186,10 @@ private:
// ctor
//-------------------------------------------------
-menu_add_change_folder::menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref)
+menu_add_change_folder::menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref, bool multipath)
: menu(mui, container)
, m_ref(ref)
- , m_change(f_folders[ref].action == CHANGE)
+ , m_multipath(multipath)
{
// configure the starting path
osd_get_full_path(m_current_path, ".");
@@ -211,136 +210,99 @@ menu_add_change_folder::menu_add_change_folder(mame_ui_manager &mui, render_cont
// handle
//-------------------------------------------------
-void menu_add_change_folder::handle(event const *ev)
+bool menu_add_change_folder::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ if (ev->iptkey == IPT_UI_SELECT)
{
- if (ev->iptkey == IPT_UI_SELECT)
- {
- assert(ev->item);
- menu_item const &pitem = *ev->item;
+ assert(ev->item);
+ menu_item const &pitem = *ev->item;
- // go up to the parent path
- if (pitem.text() == "..")
- {
- size_t const first_sep = m_current_path.find_first_of(PATH_SEPARATOR[0]);
- size_t const last_sep = m_current_path.find_last_of(PATH_SEPARATOR[0]);
- m_current_path.erase(last_sep + ((first_sep == last_sep) ? 1 : 0));
- }
+ // go up to the parent path
+ if (pitem.text() == "..")
+ {
+ size_t const first_sep = m_current_path.find_first_of(PATH_SEPARATOR[0]);
+ size_t const last_sep = m_current_path.find_last_of(PATH_SEPARATOR[0]);
+ m_current_path.erase(last_sep + ((first_sep == last_sep) ? 1 : 0));
+ }
+ else
+ {
+ // if isn't a drive, appends the directory
+ if (pitem.subtext() != "[DRIVE]")
+ util::path_append(m_current_path, pitem.text());
else
- {
- // if isn't a drive, appends the directory
- if (pitem.subtext() != "[DRIVE]")
- util::path_append(m_current_path, pitem.text());
- else
- m_current_path = pitem.text();
- }
+ m_current_path = pitem.text();
+ }
- // reset the char buffer also in this case
- m_search.clear();
- reset(reset_options::SELECT_FIRST);
+ // reset the char buffer also in this case
+ m_search.clear();
+ reset(reset_options::SELECT_FIRST);
+ }
+ else if (ev->iptkey == IPT_UI_PASTE)
+ {
+ if (paste_text(m_search, uchar_is_printable))
+ {
+ update_search();
+ return true;
}
- else if (ev->iptkey == IPT_SPECIAL)
+ }
+ else if (ev->iptkey == IPT_SPECIAL)
+ {
+ if (ev->unichar == 0x09)
{
- bool update_selected = false;
-
- if (ev->unichar == 0x09)
+ // Tab key, save current path
+ std::string error_string;
+ if (!m_multipath)
{
- // Tab key, save current path
- std::string error_string;
- if (m_change)
- {
- if (ui().options().exists(f_folders[m_ref].option))
- ui().options().set_value(f_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE);
- else if (machine().options().value(f_folders[m_ref].option) != m_current_path)
- machine().options().set_value(f_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE);
- }
- else
- {
- m_folders.push_back(m_current_path);
- std::string tmppath;
- for (int x = 0; x < m_folders.size(); ++x)
- {
- tmppath.append(m_folders[x]);
- if (x != m_folders.size() - 1)
- tmppath.append(";");
- }
-
- if (ui().options().exists(f_folders[m_ref].option))
- ui().options().set_value(f_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
- else if (machine().options().value(f_folders[m_ref].option) != tmppath)
- machine().options().set_value(f_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
- }
-
- reset_parent(reset_options::SELECT_FIRST);
- stack_pop();
+ if (ui().options().exists(f_folders[m_ref].option))
+ ui().options().set_value(f_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE);
+ else if (machine().options().value(f_folders[m_ref].option) != m_current_path)
+ machine().options().set_value(f_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE);
}
else
{
- // if it's any other key and we're not maxed out, update
- update_selected = input_character(m_search, ev->unichar, uchar_is_printable);
- }
-
- // check for entries which matches our search buffer
- if (update_selected)
- {
- const int cur_selected = selected_index();
- int entry, bestmatch = 0;
-
- // from current item to the end
- for (entry = cur_selected; entry < item_count(); entry++)
- if (item(entry).ref() && !m_search.empty())
- {
- int match = 0;
- for (int i = 0; i < m_search.size() + 1; i++)
- {
- if (core_strnicmp(item(entry).text().c_str(), m_search.data(), i) == 0)
- match = i;
- }
-
- if (match > bestmatch)
- {
- bestmatch = match;
- set_selected_index(entry);
- }
- }
-
- // and from the first item to current one
- for (entry = 0; entry < cur_selected; entry++)
+ m_folders.push_back(m_current_path);
+ std::string tmppath;
+ for (int x = 0; x < m_folders.size(); ++x)
{
- if (item(entry).ref() && !m_search.empty())
- {
- int match = 0;
- for (int i = 0; i < m_search.size() + 1; i++)
- {
- if (core_strnicmp(item(entry).text().c_str(), m_search.data(), i) == 0)
- match = i;
- }
-
- if (match > bestmatch)
- {
- bestmatch = match;
- set_selected_index(entry);
- }
- }
+ tmppath.append(m_folders[x]);
+ if (x != m_folders.size() - 1)
+ tmppath.append(";");
}
- centre_selection();
+
+ if (ui().options().exists(f_folders[m_ref].option))
+ ui().options().set_value(f_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
+ else if (machine().options().value(f_folders[m_ref].option) != tmppath)
+ machine().options().set_value(f_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
}
+
+ reset_parent(reset_options::SELECT_FIRST);
+ stack_pop();
}
- else if (ev->iptkey == IPT_UI_CANCEL)
+ else if (input_character(m_search, ev->unichar, uchar_is_printable))
{
- // reset the char buffer also in this case
- m_search.clear();
+ // if it's any other key and we're not maxed out, update
+ update_search();
+ return true;
}
}
+ else if (ev->iptkey == IPT_UI_CANCEL)
+ {
+ // reset the char buffer also in this case
+ m_search.clear();
+ return true;
+ }
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_add_change_folder::populate(float &customtop, float &custombottom)
+void menu_add_change_folder::populate()
{
int folders_count = 0;
@@ -359,7 +321,8 @@ void menu_add_change_folder::populate(float &customtop, float &custombottom)
}
// sort
- std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(std::locale());
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
std::sort(
dirnames.begin(),
dirnames.end(),
@@ -375,37 +338,95 @@ void menu_add_change_folder::populate(float &customtop, float &custombottom)
item_append(name, "[DIR]", 0, (void *)(uintptr_t)++folders_count);
item_append(menu_item_type::SEPARATOR);
+}
+
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_add_change_folder::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
// configure the custom rendering
- customtop = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
- custombottom = 1.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
+ set_custom_space(2.0f * line_height() + 3.0f * tb_border(), 1.0f * line_height() + 3.0f * tb_border());
}
//-------------------------------------------------
// 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(
- m_change ? _("Change %1$s Folder - Search: %2$s_") : _("Add %1$s Folder - Search: %2$s_"),
+ m_multipath ? _("Add %1$s Folder - Search: %2$s_") : _("Change %1$s Folder - Search: %2$s_"),
_("path-option", f_folders[m_ref].name),
m_search),
m_current_path };
draw_text_box(
std::begin(toptext), std::end(toptext),
- origx1, origx2, origy1 - top, origy1 - ui().box_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, 1.0f);
+ ui().colors().text_color(), UI_GREEN_COLOR);
// bottom text
char const *const bottomtext[] = { _("Press TAB to set") };
draw_text_box(
std::begin(bottomtext), std::end(bottomtext),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
+}
+
+//-------------------------------------------------
+// update search
+//-------------------------------------------------
+
+void menu_add_change_folder::update_search()
+{
+ // check for entries which matches our search buffer
+ const int cur_selected = selected_index();
+ int entry, bestmatch = 0;
+
+ // from current item to the end
+ for (entry = cur_selected; entry < item_count(); entry++)
+ if (item(entry).ref() && !m_search.empty())
+ {
+ int match = 0;
+ for (int i = 0; i < m_search.size() + 1; i++)
+ {
+ if (core_strnicmp(item(entry).text().c_str(), m_search.data(), i) == 0)
+ match = i;
+ }
+
+ if (match > bestmatch)
+ {
+ bestmatch = match;
+ set_selected_index(entry);
+ }
+ }
+
+ // and from the first item to current one
+ for (entry = 0; entry < cur_selected; entry++)
+ {
+ if (item(entry).ref() && !m_search.empty())
+ {
+ int match = 0;
+ for (int i = 0; i < m_search.size() + 1; i++)
+ {
+ if (core_strnicmp(item(entry).text().c_str(), m_search.data(), i) == 0)
+ match = i;
+ }
+
+ if (match > bestmatch)
+ {
+ bestmatch = match;
+ set_selected_index(entry);
+ }
+ }
+ }
+ centre_selection();
}
@@ -418,13 +439,15 @@ class menu_display_actual : public menu
public:
menu_display_actual(mame_ui_manager &mui, render_container &container, int selectedref)
: menu(mui, container)
- , m_heading{ util::string_format((f_folders[selectedref].action == ADDING) ? _("%1$s Folders") : _("%1$s Folder"), _("path-option", f_folders[selectedref].name)) }
, m_ref(selectedref)
+ , m_multipath(is_multipath(f_folders[selectedref].option))
+ , m_heading{ util::string_format(m_multipath ? _("%1$s Folders") : _("%1$s Folder"), _("path-option", f_folders[selectedref].name)) }
{
}
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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;
private:
enum
@@ -433,22 +456,38 @@ private:
REMOVE,
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+
+ bool is_multipath(std::string_view folder) const;
+ int const m_ref;
+ bool const m_multipath;
std::string const m_heading[1];
std::string m_searchpath;
std::vector<std::string> m_folders;
- int const m_ref;
};
//-------------------------------------------------
+// is_multipath
+//-------------------------------------------------
+
+bool menu_display_actual::is_multipath(std::string_view folder) const
+{
+ auto option = ui().options().get_entry(folder);
+ if (!option)
+ option = machine().options().get_entry(folder);
+ assert(option);
+
+ return option->type() == core_options::option_type::MULTIPATH;
+}
+
+//-------------------------------------------------
// handle
//-------------------------------------------------
-void menu_display_actual::handle(event const *ev)
+bool menu_display_actual::handle(event const *ev)
{
- // process the menu
if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT)
{
switch ((uintptr_t)ev->itemref)
@@ -458,56 +497,78 @@ void menu_display_actual::handle(event const *ev)
break;
case ADD_CHANGE:
- menu::stack_push<menu_add_change_folder>(ui(), container(), m_ref);
+ menu::stack_push<menu_add_change_folder>(ui(), container(), m_ref, m_multipath);
break;
}
}
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_display_actual::populate(float &customtop, float &custombottom)
+void menu_display_actual::populate()
{
- if (ui().options().exists(f_folders[m_ref].option))
- m_searchpath.assign(ui().options().value(f_folders[m_ref].option));
- else
- m_searchpath.assign(machine().options().value(f_folders[m_ref].option));
+ auto const &folder = f_folders[m_ref];
+ auto option = ui().options().get_entry(folder.option);
+ if (!option)
+ option = machine().options().get_entry(folder.option);
+ assert(option);
+
+ m_searchpath.assign(option->value());
- path_iterator path(m_searchpath);
- std::string curpath;
m_folders.clear();
- while (path.next(curpath))
- m_folders.push_back(curpath);
+ if (m_multipath)
+ {
+ path_iterator path(m_searchpath);
+ std::string curpath;
+ while (path.next(curpath))
+ m_folders.push_back(curpath);
+
+ item_append(_("Add Folder"), 0, (void *)ADD_CHANGE);
+ if (m_folders.size() > 1)
+ item_append(_("Remove Folder"), 0, (void *)REMOVE);
+ }
+ else
+ {
+ m_folders.push_back(m_searchpath);
+ item_append(_("Change Folder"), 0, (void *)ADD_CHANGE);
+ }
- item_append((f_folders[m_ref].action == CHANGE) ? _("Change Folder") : _("Add Folder"), 0, (void *)ADD_CHANGE);
+ item_append(menu_item_type::SEPARATOR);
- if (m_folders.size() > 1)
- item_append(_("Remove Folder"), 0, (void *)REMOVE);
+ set_custom_space((m_folders.size() + 1) * line_height() + 6.0f * tb_border(), 0.0f);
+}
- item_append(menu_item_type::SEPARATOR);
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_display_actual::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
- customtop = (m_folders.size() + 1) * ui().get_line_height() + 6.0f * ui().box_tb_border();
+ set_custom_space((m_folders.size() + 1) * line_height() + 6.0f * tb_border(), 0.0f);
}
//-------------------------------------------------
// 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 lineheight(ui().get_line_height());
float const maxwidth(draw_text_box(
std::begin(m_folders), std::end(m_folders),
- origx1, origx2, origy1 - (3.0f * ui().box_tb_border()) - (m_folders.size() * lineheight), origy1 - ui().box_tb_border(),
+ origx1, origx2, origy1 - (3.0f * tb_border()) - (m_folders.size() * line_height()), origy1 - tb_border(),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f));
+ ui().colors().text_color(), ui().colors().background_color()));
draw_text_box(
std::begin(m_heading), std::end(m_heading),
- 0.5f * (1.0f - maxwidth), 0.5f * (1.0f + maxwidth), origy1 - top, origy1 - top + lineheight + (2.0f * ui().box_tb_border()),
+ 0.5f * (1.0f - maxwidth), 0.5f * (1.0f + maxwidth), origy1 - top, origy1 - top + line_height() + (2.0f * tb_border()),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
+ ui().colors().text_color(), UI_GREEN_COLOR);
}
} // anonymous namespace
@@ -535,21 +596,22 @@ menu_directory::~menu_directory()
// handle
//-------------------------------------------------
-void menu_directory::handle(event const *ev)
+bool menu_directory::handle(event const *ev)
{
- // process the menu
if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT)
menu::stack_push<menu_display_actual>(ui(), container(), selected_index());
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_directory::populate(float &customtop, float &custombottom)
+void menu_directory::populate()
{
for (auto & elem : f_folders)
- item_append(_("path-option", elem.name), 0, (void *)(uintptr_t)elem.action);
+ item_append(_("path-option", elem.name), 0, this); // need a non-null reference pointer - value is immaterial
item_append(menu_item_type::SEPARATOR);
}
diff --git a/src/frontend/mame/ui/dirmenu.h b/src/frontend/mame/ui/dirmenu.h
index 644a71ba564..54eb155267e 100644
--- a/src/frontend/mame/ui/dirmenu.h
+++ b/src/frontend/mame/ui/dirmenu.h
@@ -31,8 +31,8 @@ public:
virtual ~menu_directory() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/filecreate.cpp b/src/frontend/mame/ui/filecreate.cpp
index 3ecb44f9b62..828e59cbb74 100644
--- a/src/frontend/mame/ui/filecreate.cpp
+++ b/src/frontend/mame/ui/filecreate.cpp
@@ -17,6 +17,9 @@
#include "ui/ui.h"
#include "ui/utils.h"
+#include "formats/flopimg.h"
+
+#include "path.h"
#include "zippath.h"
#include <cstring>
@@ -52,11 +55,11 @@ CONFIRM SAVE AS MENU
// ctor
//-------------------------------------------------
-menu_confirm_save_as::menu_confirm_save_as(mame_ui_manager &mui, render_container &container, bool *yes)
+menu_confirm_save_as::menu_confirm_save_as(mame_ui_manager &mui, render_container &container, bool &yes)
: menu(mui, container)
+ , m_yes(yes)
{
- m_yes = yes;
- *m_yes = false;
+ m_yes = false;
}
@@ -73,7 +76,7 @@ menu_confirm_save_as::~menu_confirm_save_as()
// populate
//-------------------------------------------------
-void menu_confirm_save_as::populate(float &customtop, float &custombottom)
+void menu_confirm_save_as::populate()
{
item_append(_("File Already Exists - Override?"), FLAG_DISABLE, nullptr);
item_append(menu_item_type::SEPARATOR);
@@ -85,17 +88,19 @@ void menu_confirm_save_as::populate(float &customtop, float &custombottom)
// handle - confirm save as menu
//-------------------------------------------------
-void menu_confirm_save_as::handle(event const *ev)
+bool menu_confirm_save_as::handle(event const *ev)
{
// process the event
if (ev && (ev->iptkey == IPT_UI_SELECT))
{
if (ev->itemref == ITEMREF_YES)
- *m_yes = true;
+ m_yes = true;
// no matter what, pop out
stack_pop();
}
+
+ return false;
}
@@ -116,7 +121,7 @@ menu_file_create::menu_file_create(mame_ui_manager &mui, render_container &conta
, m_current_format(nullptr)
{
m_image = image;
- m_ok = true;
+ m_ok = false;
m_filename.reserve(1024);
m_filename = core_filename_extract_base(current_file);
@@ -133,14 +138,36 @@ menu_file_create::~menu_file_create()
//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void menu_file_create::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(line_height() + 3.0F * tb_border(), 0.0F);
+}
+
+
+//-------------------------------------------------
// 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,
- std::string_view());
+ m_current_directory,
+ std::string_view());
+}
+
+
+//-------------------------------------------------
+// custom_ui_back - override back handling
+//-------------------------------------------------
+
+bool menu_file_create::custom_ui_back()
+{
+ return (get_selection_ref() == ITEMREF_NEW_IMAGE_NAME) && !m_filename.empty();
}
@@ -148,10 +175,9 @@ void menu_file_create::custom_render(void *selectedref, float top, float bottom,
// populate - populates the file creator menu
//-------------------------------------------------
-void menu_file_create::populate(float &customtop, float &custombottom)
+void menu_file_create::populate()
{
std::string buffer;
- const image_device_format *format;
const std::string *new_image_name;
// append the "New Image Name" item
@@ -167,9 +193,10 @@ void menu_file_create::populate(float &customtop, float &custombottom)
item_append(_("New Image Name:"), *new_image_name, 0, ITEMREF_NEW_IMAGE_NAME);
// do we support multiple formats?
- if (ENABLE_FORMATS) format = m_image->formatlist().front().get();
- if (ENABLE_FORMATS && (format != nullptr))
+ image_device_format const *const format = ENABLE_FORMATS ? m_image->formatlist().front().get() : nullptr;
+ if (format)
{
+ // FIXME: is this in the right order? It reassigns m_current_format after reading it.
item_append(_("Image Format:"), m_current_format->description(), 0, ITEMREF_FORMAT);
m_current_format = format;
}
@@ -177,8 +204,6 @@ void menu_file_create::populate(float &customtop, float &custombottom)
// finish up the menu
item_append(menu_item_type::SEPARATOR);
item_append(_("Create"), 0, ITEMREF_CREATE);
-
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
@@ -186,40 +211,64 @@ void menu_file_create::populate(float &customtop, float &custombottom)
// handle - file creator menu
//-------------------------------------------------
-void menu_file_create::handle(event const *ev)
+bool menu_file_create::handle(event const *ev)
{
- // process the event
- if (ev)
+ if (!ev)
+ return false;
+
+ // handle selections
+ switch (ev->iptkey)
{
- // handle selections
- switch (ev->iptkey)
+ case IPT_UI_SELECT:
+ if ((ev->itemref == ITEMREF_CREATE) || (ev->itemref == ITEMREF_NEW_IMAGE_NAME))
{
- case IPT_UI_SELECT:
- if ((ev->itemref == ITEMREF_CREATE) || (ev->itemref == ITEMREF_NEW_IMAGE_NAME))
+ std::string tmp_file(m_filename);
+ if (tmp_file.find('.') != -1 && tmp_file.find('.') < tmp_file.length() - 1)
{
- std::string tmp_file(m_filename);
- if (tmp_file.find('.') != -1 && tmp_file.find('.') < tmp_file.length() - 1)
- {
- m_current_file = m_filename;
- stack_pop();
- }
- else
- ui().popup_time(1, "%s", _("Please enter a file extension too"));
+ m_current_file = m_filename;
+ m_ok = true;
+ stack_pop();
}
- break;
+ else
+ {
+ ui().popup_time(1, "%s", _("Please enter a file extension too"));
+ }
+ }
+ break;
- case IPT_SPECIAL:
- if (get_selection_ref() == ITEMREF_NEW_IMAGE_NAME)
+ case IPT_UI_PASTE:
+ if (ev->itemref == ITEMREF_NEW_IMAGE_NAME)
+ {
+ if (paste_text(m_filename, &osd_is_valid_filename_char))
{
- input_character(m_filename, ev->unichar, &osd_is_valid_filename_char);
- reset(reset_options::REMEMBER_POSITION);
+ ev->item->set_subtext(m_filename + "_");
+ return true;
}
- break;
- case IPT_UI_CANCEL:
- m_ok = false;
- break;
}
+ break;
+
+ case IPT_SPECIAL:
+ if (ev->itemref == ITEMREF_NEW_IMAGE_NAME)
+ {
+ if (input_character(m_filename, ev->unichar, &osd_is_valid_filename_char))
+ {
+ ev->item->set_subtext(m_filename + "_");
+ return true;
+ }
+ }
+ break;
+
+ case IPT_UI_CANCEL:
+ if ((ev->itemref == ITEMREF_NEW_IMAGE_NAME) && !m_filename.empty())
+ {
+ m_filename.clear();
+ ev->item->set_subtext("_");
+ return true;
+ }
+ break;
}
+
+ return false;
}
@@ -253,7 +302,7 @@ menu_select_format::~menu_select_format()
// populate
//-------------------------------------------------
-void menu_select_format::populate(float &customtop, float &custombottom)
+void menu_select_format::populate()
{
item_append(_("Select image format"), FLAG_DISABLE, nullptr);
for (unsigned int i = 0; i != m_formats.size(); i++)
@@ -271,7 +320,7 @@ void menu_select_format::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_select_format::handle(event const *ev)
+bool menu_select_format::handle(event const *ev)
{
// process the menu
if (ev && ev->iptkey == IPT_UI_SELECT)
@@ -279,6 +328,8 @@ void menu_select_format::handle(event const *ev)
*m_result = (floppy_image_format_t *)ev->itemref;
stack_pop();
}
+
+ return false;
}
@@ -312,7 +363,7 @@ menu_select_floppy_init::~menu_select_floppy_init()
// populate
//-------------------------------------------------
-void menu_select_floppy_init::populate(float &customtop, float &custombottom)
+void menu_select_floppy_init::populate()
{
item_append(_("Select initial contents"), FLAG_DISABLE, nullptr);
int id = 0;
@@ -325,7 +376,7 @@ void menu_select_floppy_init::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_select_floppy_init::handle(event const *ev)
+bool menu_select_floppy_init::handle(event const *ev)
{
// process the menu
if (ev && ev->iptkey == IPT_UI_SELECT)
@@ -333,6 +384,8 @@ void menu_select_floppy_init::handle(event const *ev)
*m_result = int(uintptr_t(ev->itemref));
stack_pop();
}
+
+ return false;
}
diff --git a/src/frontend/mame/ui/filecreate.h b/src/frontend/mame/ui/filecreate.h
index 9d47605f5a7..a5f1ab92857 100644
--- a/src/frontend/mame/ui/filecreate.h
+++ b/src/frontend/mame/ui/filecreate.h
@@ -27,14 +27,14 @@ namespace ui {
class menu_confirm_save_as : public menu
{
public:
- menu_confirm_save_as(mame_ui_manager &mui, render_container &container, bool *yes);
+ menu_confirm_save_as(mame_ui_manager &mui, render_container &container, bool &yes);
virtual ~menu_confirm_save_as() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
- bool *m_yes;
+ bool &m_yes;
};
@@ -47,11 +47,13 @@ public:
virtual ~menu_file_create() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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 bool custom_ui_back() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
bool & m_ok;
device_image_interface * m_image;
@@ -71,8 +73,8 @@ public:
virtual ~menu_select_format() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
// internal state
std::vector<const floppy_image_format_t *> m_formats;
@@ -90,8 +92,8 @@ public:
virtual ~menu_select_floppy_init() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
// internal state
std::vector<std::reference_wrapper<const floppy_image_device::fs_info>> m_fs;
diff --git a/src/frontend/mame/ui/filemngr.cpp b/src/frontend/mame/ui/filemngr.cpp
index ec346940586..f9a99474a15 100644
--- a/src/frontend/mame/ui/filemngr.cpp
+++ b/src/frontend/mame/ui/filemngr.cpp
@@ -17,11 +17,16 @@
#include "ui/filesel.h"
#include "ui/floppycntrl.h"
#include "ui/imgcntrl.h"
+#include "ui/prscntrl.h"
#include "ui/miscmenu.h"
#include "ui/ui.h"
#include "softlist.h"
+#include <string_view>
+#include <unordered_set>
+#include <utility>
+
namespace ui {
@@ -35,8 +40,8 @@ namespace ui {
menu_file_manager::menu_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings)
: menu(mui, container)
- , selected_device(nullptr)
, m_warnings(warnings ? warnings : "")
+ , m_selected_device(nullptr)
{
// The warning string is used when accessing from the force_file_manager call, i.e.
// when the file manager is loaded top front in the case of mandatory image devices
@@ -54,40 +59,52 @@ menu_file_manager::~menu_file_manager()
//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void menu_file_manager::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(0.0F, line_height() + 3.0F * tb_border());
+}
+
+
+//-------------------------------------------------
// 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 = selected_device && selected_device->exists() ? selected_device->filename() : std::string_view();
+ 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);
}
-void menu_file_manager::fill_image_line(device_image_interface *img, std::string &instance, std::string &filename)
+void menu_file_manager::fill_image_line(device_image_interface &img, std::string &instance, std::string &filename)
{
// get the image type/id
- instance = string_format("%s (%s)", img->instance_name(), img->brief_instance_name());
+ instance = string_format("%s (%s)", img.instance_name(), img.brief_instance_name());
// get the base name
- if (img->basename() != nullptr)
+ if (img.basename())
{
- filename.assign(img->basename());
+ filename.assign(img.basename());
// if the image has been loaded through softlist, also show the loaded part
- if (img->loaded_through_softlist())
+ if (img.loaded_through_softlist())
{
- const software_part *tmp = img->part_entry();
+ const software_part *tmp = img.part_entry();
if (!tmp->name().empty())
{
filename.append(" (");
filename.append(tmp->name());
// also check if this part has a specific part_id (e.g. "Map Disc", "Bonus Disc", etc.), and in case display it
- if (img->get_feature("part_id") != nullptr)
+ if (img.get_feature("part_id") != nullptr)
{
filename.append(": ");
- filename.append(img->get_feature("part_id"));
+ filename.append(img.get_feature("part_id"));
}
filename.append(")");
}
@@ -101,9 +118,9 @@ void menu_file_manager::fill_image_line(device_image_interface *img, std::string
// populate
//-------------------------------------------------
-void menu_file_manager::populate(float &customtop, float &custombottom)
+void menu_file_manager::populate()
{
- std::string tmp_inst, tmp_name;
+ m_notifiers.clear();
if (!m_warnings.empty())
item_append(m_warnings, FLAG_DISABLE, nullptr);
@@ -111,6 +128,7 @@ void menu_file_manager::populate(float &customtop, float &custombottom)
// cycle through all devices for this system
bool missing_mandatory = false;
std::unordered_set<std::string> devtags;
+ std::string tmp_inst, tmp_name;
for (device_t &dev : device_enumerator(machine().root_device()))
{
bool tag_appended = false;
@@ -124,27 +142,49 @@ void menu_file_manager::populate(float &customtop, float &custombottom)
// if so, cycle through all its image interfaces
for (device_image_interface &scan : subiter)
{
- if (!scan.user_loadable())
- continue;
-
- // if it is a child device, and not something further down the device tree, we want it in the menu!
- if (strcmp(scan.device().owner()->tag(), dev.tag()) == 0)
+ if (scan.has_preset_images_selection())
+ {
if (devtags.insert(scan.device().tag()).second)
{
- if (!scan.basename() && scan.must_be_loaded())
- missing_mandatory = true;
-
- // check whether we already had some devices with the same owner: if not, output the owner tag!
- if (!tag_appended)
+ item_append(string_format(_("[root%1$s]"), scan.device().owner()->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ int const index = item_append(scan.image_type_name(), scan.preset_images_list()[scan.current_preset_image_id()], 0, (void *)&scan);
+ m_notifiers.emplace_back(scan.add_media_change_notifier(
+ [this, index, &scan] (device_image_interface::media_change_event ev)
+ {
+ item(index).set_subtext(scan.preset_images_list()[scan.current_preset_image_id()]);
+ }));
+ }
+ }
+ else if (scan.user_loadable())
+ {
+ // if it is a child device, and not something further down the device tree, we want it in the menu!
+ if (!strcmp(scan.device().owner()->tag(), dev.tag()))
+ {
+ if (devtags.insert(scan.device().tag()).second)
{
- item_append(string_format(_("[root%1$s]"), dev.tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
- tag_appended = true;
+ if (!scan.basename() && scan.must_be_loaded())
+ missing_mandatory = true;
+
+ // check whether we already had some devices with the same owner: if not, output the owner tag!
+ if (!tag_appended)
+ {
+ item_append(string_format(_("[root%1$s]"), dev.tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ tag_appended = true;
+ }
+
+ // finally, append the image interface to the menu
+ fill_image_line(scan, tmp_inst, tmp_name);
+ int const index = item_append(std::move(tmp_inst), std::move(tmp_name), 0, (void *)&scan);
+ m_notifiers.emplace_back(scan.add_media_change_notifier(
+ [this, index, &scan] (device_image_interface::media_change_event ev)
+ {
+ std::string text, subtext;
+ fill_image_line(scan, text, subtext);
+ item(index).set_subtext(std::move(subtext));
+ }));
}
-
- // finally, append the image interface to the menu
- fill_image_line(&scan, tmp_inst, tmp_name);
- item_append(tmp_inst, tmp_name, 0, (void *)&scan);
}
+ }
}
}
}
@@ -152,8 +192,6 @@ void menu_file_manager::populate(float &customtop, float &custombottom)
if (m_warnings.empty() || !missing_mandatory)
item_append(m_warnings.empty() ? _("Reset System") : _("Start System"), 0, (void *)1);
-
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
@@ -161,31 +199,50 @@ void menu_file_manager::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_file_manager::handle(event const *ev)
+bool menu_file_manager::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref && (ev->iptkey == IPT_UI_SELECT))
+ bool result = false;
+
+ if (ev)
{
if ((uintptr_t)ev->itemref == 1)
{
- machine().schedule_hard_reset();
+ if (m_selected_device)
+ {
+ m_selected_device = nullptr;
+ result = true;
+ }
+
+ if (IPT_UI_SELECT == ev->iptkey)
+ machine().schedule_hard_reset();
}
else
{
- selected_device = (device_image_interface *) ev->itemref;
- if (selected_device)
+ if (ev->itemref != m_selected_device)
{
- floppy_image_device *floppy_device = dynamic_cast<floppy_image_device *>(selected_device);
- if (floppy_device)
- menu::stack_push<menu_control_floppy_image>(ui(), container(), *floppy_device);
- else
- menu::stack_push<menu_control_device_image>(ui(), container(), *selected_device);
+ m_selected_device = (device_image_interface *)ev->itemref;
+ result = true;
+ }
- // reset the existing menu
- reset(reset_options::REMEMBER_POSITION);
+ if (m_selected_device && (IPT_UI_SELECT == ev->iptkey))
+ {
+ if (m_selected_device->has_preset_images_selection())
+ {
+ menu::stack_push<menu_control_device_preset>(ui(), container(), *m_selected_device);
+ }
+ else
+ {
+ floppy_image_device *floppy_device = dynamic_cast<floppy_image_device *>(m_selected_device);
+ if (floppy_device)
+ menu::stack_push<menu_control_floppy_image>(ui(), container(), *floppy_device);
+ else
+ menu::stack_push<menu_control_device_image>(ui(), container(), *m_selected_device);
+ }
}
}
}
+
+ return result;
}
// force file manager menu
diff --git a/src/frontend/mame/ui/filemngr.h b/src/frontend/mame/ui/filemngr.h
index 379912c8dfb..a8c68ceb211 100644
--- a/src/frontend/mame/ui/filemngr.h
+++ b/src/frontend/mame/ui/filemngr.h
@@ -14,31 +14,35 @@
#include "ui/menu.h"
+#include "notifier.h"
+
+#include <string>
+#include <vector>
+
namespace ui {
class menu_file_manager : public menu
{
public:
- std::string current_directory;
- std::string current_file;
- device_image_interface *selected_device;
-
static void force_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings);
menu_file_manager(mame_ui_manager &mui, render_container &container, const char *warnings);
virtual ~menu_file_manager();
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
- void fill_image_line(device_image_interface *img, std::string &instance, std::string &filename);
+ void fill_image_line(device_image_interface &img, std::string &instance, std::string &filename);
std::string const m_warnings;
+ std::vector<util::notifier_subscription> m_notifiers;
+ device_image_interface *m_selected_device;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/filesel.cpp b/src/frontend/mame/ui/filesel.cpp
index 36f18005409..5f4dddc65fc 100644
--- a/src/frontend/mame/ui/filesel.cpp
+++ b/src/frontend/mame/ui/filesel.cpp
@@ -19,6 +19,8 @@
#include "imagedev/floppy.h"
+#include "uiinput.h"
+
#include "util/corestr.h"
#include "util/zippath.h"
@@ -48,15 +50,26 @@ namespace ui {
// ctor
//-------------------------------------------------
-menu_file_selector::menu_file_selector(mame_ui_manager &mui, render_container &container, device_image_interface *image, std::string &current_directory, std::string &current_file, bool has_empty, bool has_softlist, bool has_create, menu_file_selector::result &result)
+menu_file_selector::menu_file_selector(
+ mame_ui_manager &mui,
+ render_container &container,
+ device_image_interface *image,
+ std::string_view directory,
+ std::string_view file,
+ bool has_empty,
+ bool has_softlist,
+ bool has_create,
+ handler_function &&handler)
: menu(mui, container)
+ , m_handler(std::move(handler))
, m_image(image)
- , m_current_directory(current_directory)
- , m_current_file(current_file)
+ , m_current_directory(directory)
+ , m_current_file(file)
+ , m_result(result::INVALID)
, m_has_empty(has_empty)
, m_has_softlist(has_softlist)
, m_has_create(has_create)
- , m_result(result)
+ , m_clicked_directory(std::string::npos, std::string::npos)
{
(void)m_image;
set_process_flags(PROCESS_IGNOREPAUSE);
@@ -69,6 +82,23 @@ 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));
+}
+
+
+//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void menu_file_selector::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ 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);
}
@@ -76,63 +106,131 @@ menu_file_selector::~menu_file_selector()
// 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 = ui().create_layout(container());
- 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 += ui().box_lr_border() * machine().render().ui_aspect(&container());
- y1 += ui().box_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);
}
@@ -286,7 +384,9 @@ void menu_file_selector::select_item(const file_selector_entry &entry)
break;
}
}
- m_current_directory.assign(entry.fullpath);
+ m_current_directory = entry.fullpath;
+ m_path_layout.reset();
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
reset(reset_options::SELECT_FIRST);
break;
@@ -301,57 +401,82 @@ void menu_file_selector::select_item(const file_selector_entry &entry)
//-------------------------------------------------
-// type_search_char
+// update_search
//-------------------------------------------------
-void menu_file_selector::type_search_char(char32_t ch)
+void menu_file_selector::update_search()
{
- std::string const current(m_filename);
- if (input_character(m_filename, ch, uchar_is_printable))
- {
- ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
- file_selector_entry const *const cur_selected(reinterpret_cast<file_selector_entry const *>(get_selection_ref()));
+ file_selector_entry const *const cur_selected(reinterpret_cast<file_selector_entry const *>(get_selection_ref()));
- // if it's a perfect match for the current selection, don't move it
- if (!cur_selected || core_strnicmp(cur_selected->basename.c_str(), m_filename.c_str(), m_filename.size()))
+ // if it's a perfect match for the current selection, don't move it
+ if (!cur_selected || core_strnicmp(cur_selected->basename.c_str(), m_filename.c_str(), m_filename.size()))
+ {
+ std::string::size_type bestmatch(0);
+ file_selector_entry const *selected_entry(cur_selected);
+ for (auto &entry : m_entrylist)
{
- std::string::size_type bestmatch(0);
- file_selector_entry const *selected_entry(cur_selected);
- for (auto &entry : m_entrylist)
+ // TODO: more efficient "common prefix" code
+ std::string::size_type match(0);
+ for (std::string::size_type i = 1; m_filename.size() >= i; ++i)
{
- // TODO: more efficient "common prefix" code
- std::string::size_type match(0);
- for (std::string::size_type i = 1; m_filename.size() >= i; ++i)
- {
- if (!core_strnicmp(entry.basename.c_str(), m_filename.c_str(), i))
- match = i;
- else
- break;
- }
-
- if (match > bestmatch)
- {
- bestmatch = match;
- selected_entry = &entry;
- }
+ if (!core_strnicmp(entry.basename.c_str(), m_filename.c_str(), i))
+ match = i;
+ else
+ break;
}
- if (selected_entry && (selected_entry != cur_selected))
+ if (match > bestmatch)
{
- set_selection((void *)selected_entry);
- centre_selection();
+ bestmatch = match;
+ selected_entry = &entry;
}
}
+
+ if (selected_entry && (selected_entry != cur_selected))
+ {
+ set_selection((void *)selected_entry);
+ centre_selection();
+ }
}
}
//-------------------------------------------------
+// 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
//-------------------------------------------------
-void menu_file_selector::populate(float &customtop, float &custombottom)
+void menu_file_selector::populate()
{
const file_selector_entry *selected_entry = nullptr;
@@ -401,7 +526,7 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
selected_entry = entry;
// do we have to select this file?
- if (!core_stricmp(m_current_file.c_str(), dirent->name))
+ if (!core_stricmp(m_current_file, dirent->name))
selected_entry = entry;
}
}
@@ -411,7 +536,8 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
if (m_entrylist.size() > first)
{
// sort the menu entries
- std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(std::locale());
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
std::sort(
m_entrylist.begin() + first,
m_entrylist.end(),
@@ -430,9 +556,6 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
// set the selection (if we have one)
if (selected_entry)
set_selection((void *)selected_entry);
-
- // set up custom render proc
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
@@ -440,34 +563,61 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_file_selector::handle(event const *ev)
+bool menu_file_selector::handle(event const *ev)
{
- // process the menu
- if (ev)
+ if (!ev)
+ return false;
+
+ if (ev->iptkey == IPT_SPECIAL)
{
- if (ev->iptkey == IPT_SPECIAL)
+ // if it's any other key and we're not maxed out, update
+ if (input_character(m_filename, ev->unichar, uchar_is_printable))
{
- // if it's any other key and we're not maxed out, update
- type_search_char(ev->unichar);
+ update_search();
+ return true;
}
- else if (ev->iptkey == IPT_UI_CANCEL)
+ }
+ else if (ev->iptkey == IPT_UI_PASTE)
+ {
+ if (paste_text(m_filename, uchar_is_printable))
{
- // reset the char buffer also in this case
- if (!m_filename.empty())
- {
- m_filename.clear();
- ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
- }
+ update_search();
+ return true;
}
- else if (ev->itemref && (ev->iptkey == IPT_UI_SELECT))
+ }
+ else if (ev->iptkey == IPT_UI_CANCEL)
+ {
+ // reset the char buffer also in this case
+ if (!m_filename.empty())
{
- // handle selections
- select_item(*reinterpret_cast<file_selector_entry const *>(ev->itemref));
-
- // reset the char buffer when pressing IPT_UI_SELECT
m_filename.clear();
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
+ 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
+ select_item(*reinterpret_cast<file_selector_entry const *>(ev->itemref));
+
+ // reset the char buffer when pressing IPT_UI_SELECT
+ m_filename.clear();
+ return true;
+ }
+
+ return false;
}
@@ -480,11 +630,15 @@ void 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)
{
}
@@ -495,6 +649,8 @@ menu_select_rw::menu_select_rw(mame_ui_manager &mui, render_container &container
menu_select_rw::~menu_select_rw()
{
+ if (m_handler)
+ m_handler(m_result);
}
@@ -502,9 +658,10 @@ menu_select_rw::~menu_select_rw()
// populate
//-------------------------------------------------
-void menu_select_rw::populate(float &customtop, float &custombottom)
+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));
@@ -517,14 +674,15 @@ void menu_select_rw::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_select_rw::handle(event const *ev)
+bool menu_select_rw::handle(event const *ev)
{
- // process the menu
if (ev && ev->iptkey == IPT_UI_SELECT)
{
m_result = result_from_itemref(ev->itemref);
stack_pop();
}
+
+ return false;
}
diff --git a/src/frontend/mame/ui/filesel.h b/src/frontend/mame/ui/filesel.h
index 3b09244ca20..6ca9cafbe68 100644
--- a/src/frontend/mame/ui/filesel.h
+++ b/src/frontend/mame/ui/filesel.h
@@ -15,6 +15,15 @@
#include "ui/menu.h"
+#include <functional>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+
namespace ui {
// ======================> menu_file_selector
@@ -31,22 +40,26 @@ public:
FILE
};
+ using handler_function = std::function<void (result result, std::string &&directory, std::string &&file)>;
+
menu_file_selector(
mame_ui_manager &mui,
render_container &container,
device_image_interface *image,
- std::string &current_directory,
- std::string &current_file,
+ std::string_view directory,
+ std::string_view file,
bool has_empty,
bool has_softlist,
bool has_create,
- result &result);
+ handler_function &&handler);
virtual ~menu_file_selector() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool custom_ui_cancel() override { return !m_filename.empty(); }
- virtual bool custom_mouse_down() override;
+ 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 bool custom_ui_back() override { return !m_filename.empty(); }
+ 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
@@ -71,19 +84,22 @@ private:
};
// internal state
- device_image_interface *const m_image;
- std::string & m_current_directory;
- std::string & m_current_file;
- bool const m_has_empty;
- bool const m_has_softlist;
- bool const m_has_create;
- result & m_result;
+ handler_function const m_handler;
+ device_image_interface *const m_image;
+ std::string m_current_directory;
+ std::string m_current_file;
+ std::optional<text_layout> m_path_layout;
+ std::pair<float, float> m_path_position;
+ result m_result;
+ bool const m_has_empty;
+ bool const m_has_softlist;
+ bool const m_has_create;
std::vector<file_selector_entry> m_entrylist;
- std::string m_hover_directory;
- std::string m_filename;
+ std::string m_filename;
+ std::pair<size_t, size_t> m_clicked_directory;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
// methods
file_selector_entry &append_entry(file_selector_entry_type entry_type, const std::string &entry_basename, const std::string &entry_fullpath);
@@ -91,7 +107,8 @@ private:
file_selector_entry *append_dirent_entry(const osd::directory::entry *dirent);
void append_entry_menu_item(const file_selector_entry *entry);
void select_item(const file_selector_entry &entry);
- void type_search_char(char32_t ch);
+ void update_search();
+ std::pair<size_t, size_t> get_directory_range(float x, float y);
};
@@ -108,23 +125,27 @@ public:
WRITE_OTHER,
WRITE_DIFF
};
+
+ using handler_function = std::function<void (result result)>;
+
menu_select_rw(
mame_ui_manager &mui,
render_container &container,
bool can_in_place,
- result &result);
+ handler_function &&handler);
virtual ~menu_select_rw() override;
+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);
-private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
-
// 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 9b85992a923..3f6f9bdfb61 100644
--- a/src/frontend/mame/ui/floppycntrl.cpp
+++ b/src/frontend/mame/ui/floppycntrl.cpp
@@ -12,8 +12,13 @@
#include "ui/filecreate.h"
#include "ui/floppycntrl.h"
+#include "formats/flopimg.h"
+#include "formats/fsmgr.h"
+
#include "zippath.h"
+#include <tuple>
+
namespace ui {
@@ -38,26 +43,28 @@ menu_control_floppy_image::~menu_control_floppy_image()
void menu_control_floppy_image::do_load_create()
{
- if(input_filename.compare("")==0) {
- image_init_result err = fd.create(output_filename, nullptr, nullptr);
- if (err != image_init_result::PASS) {
- machine().popmessage("Error: %s", fd.error());
+ if(input_filename.empty()) {
+ auto [err, message] = fd.create(output_filename, nullptr, nullptr);
+ if (err) {
+ machine().popmessage(_("Error creating floppy image: %1$s"), !message.empty() ? message : err.message());
return;
}
if (create_fs) {
// HACK: ensure the floppy_image structure is created since device_image_interface may not otherwise do so during "init phase"
- err = fd.finish_load();
- if (err == image_init_result::PASS) {
+ err = fd.finish_load().first;
+ if (!err) {
fs::meta_data meta;
fd.init_fs(create_fs, meta);
}
}
} else {
- image_init_result err = fd.load(input_filename);
- if ((err == image_init_result::PASS) && (output_filename.compare("") != 0))
- err = fd.reopen_for_write(output_filename) ? image_init_result::FAIL : image_init_result::PASS;
- if (err != image_init_result::PASS) {
- machine().popmessage("Error: %s", fd.error());
+ auto [err, message] = fd.load(input_filename);
+ if (!err && !output_filename.empty()) {
+ message.clear();
+ err = fd.reopen_for_write(output_filename);
+ }
+ if (err) {
+ machine().popmessage(_("Error opening floppy image: %1$s"), !message.empty() ? message : err.message());
return;
}
}
@@ -67,18 +74,20 @@ void menu_control_floppy_image::do_load_create()
void menu_control_floppy_image::hook_load(const std::string &filename)
{
+ std::error_condition err;
input_filename = filename;
- input_format = static_cast<floppy_image_device &>(m_image).identify(filename);
+ std::tie(err, input_format) = static_cast<floppy_image_device &>(m_image).identify(filename);
if (!input_format)
{
- machine().popmessage("Error: %s\n", m_image.error());
+ machine().popmessage("Error: %s", err.message());
stack_pop();
}
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
@@ -88,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;
+ }
+ });
}
}
@@ -174,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 fd7e17cf7ce..8bcb02d15b5 100644
--- a/src/frontend/mame/ui/floppycntrl.h
+++ b/src/frontend/mame/ui/floppycntrl.h
@@ -13,7 +13,6 @@
#include "ui/imgcntrl.h"
#include "imagedev/floppy.h"
-#include "formats/flopimg.h"
#include <memory>
@@ -30,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 363e64bad8b..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,13 +27,12 @@
#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
-#define LOG_GENERAL (1U << 0)
-
-//#define VERBOSE (LOG_GENERAL | LOG_DIB)
+//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"
@@ -74,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)
@@ -120,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()));
@@ -156,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))
@@ -175,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;
@@ -183,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);
@@ -208,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;
@@ -216,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");
@@ -239,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");
@@ -263,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)
@@ -275,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 5708a4a832a..6dd909f711c 100644
--- a/src/frontend/mame/ui/imgcntrl.cpp
+++ b/src/frontend/mame/ui/imgcntrl.cpp
@@ -9,23 +9,24 @@
***************************************************************************/
#include "emu.h"
-
#include "ui/imgcntrl.h"
-#include "ui/ui.h"
-#include "ui/filesel.h"
#include "ui/filecreate.h"
+#include "ui/filesel.h"
#include "ui/swlist.h"
+#include "ui/ui.h"
#include "audit.h"
#include "drivenum.h"
#include "emuopts.h"
#include "image.h"
#include "softlist_dev.h"
-#include "zippath.h"
+
+#include "util/zippath.h"
namespace ui {
+
/***************************************************************************
IMPLEMENTATION
***************************************************************************/
@@ -39,17 +40,45 @@ menu_control_device_image::menu_control_device_image(mame_ui_manager &mui, rende
, m_image(image)
, m_create_ok(false)
, m_create_confirmed(false)
+ , m_swi(nullptr)
+ , m_swp(nullptr)
+ , m_sld(nullptr)
{
m_submenu_result.i = -1;
if (m_image.software_list_name())
m_sld = software_list_device::find_by_name(mui.machine().config(), m_image.software_list_name());
- else
- m_sld = nullptr;
m_swi = m_image.software_entry();
m_swp = m_image.part_entry();
- if (m_swi != nullptr)
+ // if there's no image mounted, check for a software item with compatible parts mounted elsewhere
+ if (!m_image.exists() && m_image.image_interface())
+ {
+ assert(!m_swi);
+
+ for (device_image_interface &other : image_interface_enumerator(mui.machine().root_device()))
+ {
+ if (other.loaded_through_softlist() && (!m_sld || (m_sld->list_name() == other.software_list_name())))
+ {
+ software_info const &swi = *other.software_entry();
+ for (software_part const &swp : swi.parts())
+ {
+ if (swp.interface() == m_image.image_interface())
+ {
+ if (!m_sld)
+ m_sld = software_list_device::find_by_name(mui.machine().config(), other.software_list_name());
+ m_swi = &swi;
+ break;
+ }
+ }
+ }
+
+ if (m_swi)
+ break;
+ }
+ }
+
+ if (m_swi)
{
m_state = START_OTHER_PART;
m_current_directory = m_image.working_directory();
@@ -157,7 +186,9 @@ void menu_control_device_image::load_software_part()
// if everything looks good, load software
if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE || summary == media_auditor::NONE_NEEDED)
{
- m_image.load_software(temp_name);
+ auto [err, msg] = m_image.load_software(temp_name);
+ if (err)
+ machine().popmessage(_("Error loading software item: %1$s"), !msg.empty() ? msg : err.message());
stack_pop();
}
else
@@ -175,7 +206,9 @@ void menu_control_device_image::load_software_part()
void menu_control_device_image::hook_load(const std::string &name)
{
- m_image.load(name);
+ auto [err, msg] = m_image.load(name);
+ if (err)
+ machine().popmessage(_("Error loading media image: %1$s"), !msg.empty() ? msg : err.message());
stack_pop();
}
@@ -184,7 +217,7 @@ void menu_control_device_image::hook_load(const std::string &name)
// populate
//-------------------------------------------------
-void menu_control_device_image::populate(float &customtop, float &custombottom)
+void menu_control_device_image::populate()
{
throw emu_fatalerror("menu_control_device_image::populate: Shouldn't get here!");
}
@@ -194,7 +227,7 @@ void menu_control_device_image::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_control_device_image::handle(event const *ev)
+bool menu_control_device_image::handle(event const *ev)
{
throw emu_fatalerror("menu_control_device_image::handle: Shouldn't get here!");
}
@@ -209,9 +242,43 @@ 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, m_current_directory, m_current_file, true, m_image.image_interface()!=nullptr, m_image.is_creatable(), m_submenu_result.filesel);
- m_state = SELECT_FILE;
+ menu::stack_push<menu_file_selector>(
+ ui(), container(),
+ &m_image,
+ m_current_directory,
+ m_current_file,
+ true,
+ m_image.image_interface() != nullptr,
+ m_image.is_creatable(),
+ [this] (menu_file_selector::result result, std::string &&directory, std::string &&file)
+ {
+ m_current_directory = std::move(directory);
+ m_current_file = std::move(file);
+ switch (result)
+ {
+ case menu_file_selector::result::EMPTY:
+ m_image.unload();
+ stack_pop();
+ break;
+
+ case menu_file_selector::result::FILE:
+ hook_load(m_current_file);
+ break;
+
+ case menu_file_selector::result::CREATE:
+ menu::stack_push<menu_file_create>(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok);
+ m_state = CHECK_CREATE;
+ break;
+
+ case menu_file_selector::result::SOFTLIST:
+ m_state = START_SOFTLIST;
+ break;
+
+ default: // return to system
+ stack_pop();
+ break;
+ }
+ });
break;
case START_SOFTLIST:
@@ -222,7 +289,7 @@ void menu_control_device_image::menu_activated()
case START_OTHER_PART:
m_submenu_result.swparts = menu_software_parts::result::INVALID;
- menu::stack_push<menu_software_parts>(ui(), container(), m_swi, m_swp->interface().c_str(), &m_swp, true, m_submenu_result.swparts);
+ menu::stack_push<menu_software_parts>(ui(), container(), m_swi, m_image.image_interface(), &m_swp, true, m_submenu_result.swparts);
m_state = SELECT_OTHER_PART;
break;
@@ -302,34 +369,6 @@ void menu_control_device_image::menu_activated()
}
break;
- case SELECT_FILE:
- switch (m_submenu_result.filesel)
- {
- case menu_file_selector::result::EMPTY:
- m_image.unload();
- stack_pop();
- break;
-
- case menu_file_selector::result::FILE:
- hook_load(m_current_file);
- break;
-
- case menu_file_selector::result::CREATE:
- menu::stack_push<menu_file_create>(ui(), container(), &m_image, m_current_directory, m_current_file, m_create_ok);
- m_state = CHECK_CREATE;
- break;
-
- case menu_file_selector::result::SOFTLIST:
- m_state = START_SOFTLIST;
- menu_activated();
- break;
-
- default: // return to system
- stack_pop();
- break;
- }
- break;
-
case CREATE_FILE:
{
bool can_create, need_confirm;
@@ -338,7 +377,7 @@ void menu_control_device_image::menu_activated()
{
if (need_confirm)
{
- menu::stack_push<menu_confirm_save_as>(ui(), container(), &m_create_confirmed);
+ menu::stack_push<menu_confirm_save_as>(ui(), container(), m_create_confirmed);
m_state = CREATE_CONFIRM;
}
else
@@ -368,9 +407,9 @@ void menu_control_device_image::menu_activated()
case DO_CREATE:
{
auto path = util::zippath_combine(m_current_directory, m_current_file);
- image_init_result err = m_image.create(path, nullptr, nullptr);
- if (err != image_init_result::PASS)
- machine().popmessage("Error: %s", m_image.error());
+ auto [err, msg] = m_image.create(path, nullptr, nullptr);
+ if (err)
+ machine().popmessage(_("Error creating media image: %1$s"), !msg.empty() ? msg : err.message());
stack_pop();
}
break;
diff --git a/src/frontend/mame/ui/imgcntrl.h b/src/frontend/mame/ui/imgcntrl.h
index 66a5ee5f81a..c081291f2e1 100644
--- a/src/frontend/mame/ui/imgcntrl.h
+++ b/src/frontend/mame/ui/imgcntrl.h
@@ -4,7 +4,7 @@
ui/imgcntrl.h
- MESS's clunky built-in file manager
+ MAME's clunky built-in file manager
***************************************************************************/
@@ -13,7 +13,6 @@
#pragma once
-#include "ui/filesel.h"
#include "ui/menu.h"
#include "ui/swlist.h"
@@ -33,7 +32,7 @@ protected:
{
START_FILE, START_OTHER_PART, START_SOFTLIST,
SELECT_PARTLIST, SELECT_ONE_PART, SELECT_OTHER_PART,
- SELECT_FILE, CREATE_FILE, CREATE_CONFIRM, CHECK_CREATE, DO_CREATE, SELECT_SOFTLIST,
+ CREATE_FILE, CREATE_CONFIRM, CHECK_CREATE, DO_CREATE, SELECT_SOFTLIST,
LAST_ID
};
@@ -41,9 +40,7 @@ protected:
// results we could get from child menus
union
{
- menu_file_selector::result filesel;
menu_software_parts::result swparts;
- menu_select_rw::result rw;
int i;
} m_submenu_result;
@@ -56,7 +53,7 @@ protected:
// methods
virtual void menu_activated() override;
- virtual void handle(event const *ev) override;
+ virtual bool handle(event const *ev) override;
virtual void hook_load(const std::string &filename);
private:
@@ -68,7 +65,7 @@ private:
std::string m_software_info_name;
// methods
- virtual void populate(float &customtop, float &custombottom) override;
+ virtual void populate() override;
void test_create(bool &can_create, bool &need_confirm);
void load_software_part();
};
diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp
index f6fa969bbb4..945c18d32a2 100644
--- a/src/frontend/mame/ui/info.cpp
+++ b/src/frontend/mame/ui/info.cpp
@@ -14,12 +14,18 @@
#include "ui/systemlist.h"
#include "ui/ui.h"
+#include "infoxml.h"
+
#include "drivenum.h"
#include "emuopts.h"
#include "romload.h"
#include "screen.h"
#include "softlist.h"
+#include "speaker.h"
+
+#include "util/unicode.h"
+#include <locale>
#include <set>
#include <sstream>
#include <type_traits>
@@ -77,7 +83,7 @@ void get_general_warnings(std::ostream &buf, running_machine &machine, machine_f
{
if (bad_roms)
buf << '\n';
- buf << _("There are known problems with this system\n\n");
+ buf << _("There are known problems with this system:\n\n");
}
// add a warning if any ROMs are flagged BAD_DUMP/NO_DUMP
@@ -122,6 +128,8 @@ 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)
{
+ std::streampos start_position = buf.tellp();
+
// start with the unemulated/imperfect features
get_device_warnings(buf, unemulated, imperfect);
@@ -130,16 +138,34 @@ void get_system_warnings(std::ostream &buf, running_machine &machine, machine_fl
buf << _("Screen flipping in cocktail mode is not supported.\n");
if (flags & ::machine_flags::REQUIRES_ARTWORK)
buf << _("This system requires external artwork files.\n");
+
+ // add the 'BTANB' warnings
if (flags & ::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)
- buf << _("This system has no sound hardware, MAME will produce no sounds, this is expected behaviour.\n");
+ {
+ if (buf.tellp() > start_position)
+ buf << '\n';
+ buf << _("This system has no sound hardware, MAME will produce no sounds, this is expected behavior.\n");
+ }
// these are more severe warnings
- if (flags & ::machine_flags::NOT_WORKING)
- buf << _("\nTHIS 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_flags::MECHANICAL)
- buf << _("\nElements of this system cannot be emulated as they require physical interaction or consist of mechanical devices. It is not possible to fully experience this system.\n");
+ {
+ 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 (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))
{
@@ -208,7 +234,7 @@ machine_static_info::machine_static_info(const ui_options &options, machine_conf
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
- if (dynamic_cast<device_sound_interface *>(&device))
+ if (dynamic_cast<speaker_device *>(&device))
m_flags &= ~::machine_flags::NO_SOUND_HW;
// build overall emulation status
@@ -361,18 +387,19 @@ std::string machine_info::game_info_string() const
{
std::ostringstream buf;
+ // get decimal separator
+ std::string point;
+ {
+ wchar_t const s(std::use_facet<std::numpunct<wchar_t> >(std::locale()).decimal_point());
+ point = utf8_from_wstring(std::wstring_view(&s, 1));
+ }
+
// print description, manufacturer, and CPU:
- std::string_view src(m_machine.system().type.source());
- auto prefix(src.find("src/mame/"));
- if (std::string_view::npos == prefix)
- prefix = src.find("src\\mame\\");
- if (std::string_view::npos != prefix)
- src.remove_prefix(prefix + 9);
- util::stream_format(buf, _("%1$s\n%2$s %3$s\nDriver: %4$s\n\nCPU:\n"),
+ util::stream_format(buf, _("%1$s\n%2$s %3$s\nSource file: %4$s\n\nCPU:\n"),
system_list::instance().systems()[driver_list::find(m_machine.system().name)].description,
m_machine.system().year,
m_machine.system().manufacturer,
- src);
+ info_xml_creator::format_sourcefile(m_machine.system().type.source()));
// loop over all CPUs
execute_interface_enumerator execiter(m_machine.root_device());
@@ -399,7 +426,7 @@ std::string machine_info::game_info_string() const
if (d > 0)
{
size_t dpos = hz.length() - d;
- hz.insert(dpos, ".");
+ hz.insert(dpos, point);
size_t last = hz.find_last_not_of('0');
hz = hz.substr(0, last + (last != dpos ? 1 : 0));
}
@@ -407,8 +434,8 @@ std::string machine_info::game_info_string() const
// if more than one, prepend a #x in front of the CPU name and display clock
util::stream_format(buf,
(count > 1)
- ? ((clock != 0) ? "%1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%1$d" UTF8_MULTIPLY "%2$s\n")
- : ((clock != 0) ? "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%2$s\n"),
+ ? ((clock != 0) ? u8"%1$d×%2$s %3$s\u00a0%4$s\n" : u8"%1$d×%2$s\n")
+ : ((clock != 0) ? u8"%2$s %3$s\u00a0%4$s\n" : "%2$s\n"),
count, name, hz,
(d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz"));
}
@@ -442,7 +469,7 @@ std::string machine_info::game_info_string() const
if (d > 0)
{
size_t dpos = hz.length() - d;
- hz.insert(dpos, ".");
+ hz.insert(dpos, point);
size_t last = hz.find_last_not_of('0');
hz = hz.substr(0, last + (last != dpos ? 1 : 0));
}
@@ -450,8 +477,8 @@ std::string machine_info::game_info_string() const
// if more than one, prepend a #x in front of the soundchip name and display clock
util::stream_format(buf,
(count > 1)
- ? ((clock != 0) ? "%1$d" UTF8_MULTIPLY "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%1$d" UTF8_MULTIPLY "%2$s\n")
- : ((clock != 0) ? "%2$s %3$s" UTF8_NBSP "%4$s\n" : "%2$s\n"),
+ ? ((clock != 0) ? u8"%1$d×%2$s %3$s\u00a0%4$s\n" : u8"%1$d×%2$s\n")
+ : ((clock != 0) ? u8"%2$s %3$s\u00a0%4$s\n" : "%2$s\n"),
count, sound.device().name(), hz,
(d == 9) ? _("GHz") : (d == 6) ? _("MHz") : (d == 3) ? _("kHz") : _("Hz"));
}
@@ -477,13 +504,13 @@ std::string machine_info::game_info_string() const
if (valid)
{
size_t dpos = hz.length() - 6;
- hz.insert(dpos, ".");
+ hz.insert(dpos, point);
size_t last = hz.find_last_not_of('0');
hz = hz.substr(0, last + (last != dpos ? 1 : 0));
}
const rectangle &visarea = screen.visible_area();
- detail = string_format("%d " UTF8_MULTIPLY " %d (%s) %s" UTF8_NBSP "Hz",
+ detail = string_format(u8"%d × %d (%s) %s\u00a0Hz",
visarea.width(), visarea.height(),
(screen.orientation() & ORIENTATION_SWAP_XY) ? "V" : "H",
hz);
@@ -538,23 +565,17 @@ void menu_game_info::populate_text(std::optional<text_layout> &layout, float &wi
if (!layout || (layout->width() != width))
{
rgb_t const color = ui().colors().text_color();
- layout.emplace(ui().create_layout(container(), width));
+ layout.emplace(create_layout(width));
layout->add_text(ui().machine_info().game_info_string(), color);
lines = layout->lines();
}
width = layout->actual_width();
}
-void menu_game_info::populate(float &customtop, float &custombottom)
+void menu_game_info::populate()
{
}
-void menu_game_info::handle(event const *ev)
-{
- if (ev)
- handle_key(ev->iptkey);
-}
-
/*-------------------------------------------------
menu_warn_info - handle the emulation warnings menu
@@ -603,21 +624,15 @@ void menu_warn_info::populate_text(std::optional<text_layout> &layout, float &wi
}
rgb_t const color(ui().colors().text_color());
- layout.emplace(ui().create_layout(container(), width));
+ layout.emplace(create_layout(width));
layout->add_text(std::move(buf).str(), color);
lines = layout->lines();
}
width = layout->actual_width();
}
-void menu_warn_info::populate(float &customtop, float &custombottom)
-{
-}
-
-void menu_warn_info::handle(event const *ev)
+void menu_warn_info::populate()
{
- if (ev)
- handle_key(ev->iptkey);
}
@@ -639,14 +654,15 @@ void menu_image_info::menu_activated()
reset(reset_options::REMEMBER_POSITION);
}
-void menu_image_info::populate(float &customtop, float &custombottom)
+void menu_image_info::populate()
{
for (device_image_interface &image : image_interface_enumerator(machine().root_device()))
- image_info(&image);
+ image_info(image);
}
-void menu_image_info::handle(event const *ev)
+bool menu_image_info::handle(event const *ev)
{
+ return false;
}
@@ -655,17 +671,22 @@ void menu_image_info::handle(event const *ev)
image interface device
-------------------------------------------------*/
-void menu_image_info::image_info(device_image_interface *image)
+void menu_image_info::image_info(device_image_interface &image)
{
- if (image->exists())
+ if (!image.user_loadable())
+ return;
+
+ m_notifiers.emplace_back(image.add_media_change_notifier(delegate(&menu_image_info::reload, this)));
+
+ if (image.exists())
{
// display device type and filename
- item_append(image->brief_instance_name(), image->basename(), 0, nullptr);
+ item_append(image.brief_instance_name(), image.basename(), 0, &image);
// if image has been loaded through softlist, let's add some more info
- if (image->loaded_through_softlist())
+ if (image.loaded_through_softlist())
{
- software_info const &swinfo(*image->software_entry());
+ software_info const &swinfo(*image.software_entry());
// display full name, publisher and year
item_append(swinfo.longname(), FLAG_DISABLE, nullptr);
@@ -687,9 +708,20 @@ void menu_image_info::image_info(device_image_interface *image)
}
else
{
- item_append(image->brief_instance_name(), _("[empty]"), 0, nullptr);
+ item_append(image.brief_instance_name(), _("[empty]"), 0, &image);
}
item_append(menu_item_type::SEPARATOR);
}
+
+/*-------------------------------------------------
+ reload - refresh the menu after a media change
+-------------------------------------------------*/
+
+void menu_image_info::reload(device_image_interface::media_change_event ev)
+{
+ m_notifiers.clear();
+ reset(reset_options::REMEMBER_REF);
+}
+
} // namespace ui
diff --git a/src/frontend/mame/ui/info.h b/src/frontend/mame/ui/info.h
index 5c8f153d6ae..c1b5d318a69 100644
--- a/src/frontend/mame/ui/info.h
+++ b/src/frontend/mame/ui/info.h
@@ -15,7 +15,10 @@
#include "ui/textbox.h"
+#include "notifier.h"
+
#include <string>
+#include <vector>
namespace ui {
@@ -100,8 +103,7 @@ protected:
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
};
@@ -115,8 +117,7 @@ protected:
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
};
@@ -130,9 +131,12 @@ protected:
virtual void menu_activated() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
- void image_info(device_image_interface *image);
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+ void image_info(device_image_interface &image);
+ void reload(device_image_interface::media_change_event ev);
+
+ std::vector<util::notifier_subscription> m_notifiers;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/info_pty.cpp b/src/frontend/mame/ui/info_pty.cpp
index 1aadb85deb9..6aa4826e241 100644
--- a/src/frontend/mame/ui/info_pty.cpp
+++ b/src/frontend/mame/ui/info_pty.cpp
@@ -26,7 +26,7 @@ menu_pty_info::~menu_pty_info()
{
}
-void menu_pty_info::populate(float &customtop, float &custombottom)
+void menu_pty_info::populate()
{
for (device_pty_interface &pty : pty_interface_enumerator(machine().root_device()))
{
@@ -39,8 +39,9 @@ void menu_pty_info::populate(float &customtop, float &custombottom)
}
}
-void menu_pty_info::handle(event const *ev)
+bool menu_pty_info::handle(event const *ev)
{
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/info_pty.h b/src/frontend/mame/ui/info_pty.h
index 16f65435be2..203209fddc4 100644
--- a/src/frontend/mame/ui/info_pty.h
+++ b/src/frontend/mame/ui/info_pty.h
@@ -24,8 +24,8 @@ public:
virtual ~menu_pty_info() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/inifile.cpp b/src/frontend/mame/ui/inifile.cpp
index 6cf9886324d..5e4be6502ef 100644
--- a/src/frontend/mame/ui/inifile.cpp
+++ b/src/frontend/mame/ui/inifile.cpp
@@ -20,10 +20,12 @@
#include "softlist_dev.h"
#include "corestr.h"
+#include "path.h"
#include <algorithm>
#include <cstring>
#include <iterator>
+#include <locale>
namespace {
@@ -56,7 +58,18 @@ inifile_manager::inifile_manager(ui_options &options)
}
}
}
- std::stable_sort(m_ini_index.begin(), m_ini_index.end(), [] (auto const &x, auto const &y) { return 0 > core_stricmp(x.first.c_str(), y.first.c_str()); });
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
+ std::stable_sort(
+ m_ini_index.begin(),
+ m_ini_index.end(),
+ [&coll] (auto const &x, auto const &y)
+ {
+ std::wstring const wx = wstring_from_utf8(x.first);
+ std::wstring const wy = wstring_from_utf8(y.first);
+ return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size());
+ }
+ );
}
//-------------------------------------------------
@@ -118,9 +131,22 @@ void inifile_manager::init_category(std::string &&filename, util::core_file &fil
}
}
}
- std::stable_sort(index.begin(), index.end(), [] (auto const &x, auto const &y) { return 0 > core_stricmp(x.first.c_str(), y.first.c_str()); });
if (!index.empty())
+ {
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
+ std::stable_sort(
+ index.begin(),
+ index.end(),
+ [&coll] (auto const &x, auto const &y)
+ {
+ std::wstring const wx = wstring_from_utf8(x.first);
+ std::wstring const wy = wstring_from_utf8(y.first);
+ return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size());
+ }
+ );
m_ini_index.emplace_back(std::move(filename), std::move(index));
+ }
}
@@ -319,7 +345,7 @@ void favorite_manager::add_favorite(running_machine &machine)
driver,
imagedev->software_list_name(),
imagedev->instance_name(),
- strensure(imagedev->image_type_name()));
+ imagedev->image_type_name() ? imagedev->image_type_name() : "");
// assume it's available if it's mounted
info.available = true;
@@ -464,31 +490,23 @@ void favorite_manager::apply_running_machine(running_machine &machine, T &&actio
{
bool done(false);
- // TODO: this should be changed - it interacts poorly with cartslots on arcade systems
- if ((machine.system().flags & machine_flags::MASK_TYPE) == machine_flags::TYPE_ARCADE)
- {
- action(machine.system(), nullptr, nullptr, done);
- }
- else
+ bool have_software(false);
+ for (device_image_interface &image_dev : image_interface_enumerator(machine.root_device()))
{
- bool have_software(false);
- for (device_image_interface &image_dev : image_interface_enumerator(machine.root_device()))
+ software_info const *const sw(image_dev.software_entry());
+ if (image_dev.exists() && image_dev.loaded_through_softlist() && sw)
{
- software_info const *const sw(image_dev.software_entry());
- if (image_dev.exists() && image_dev.loaded_through_softlist() && sw)
- {
- assert(image_dev.software_list_name());
+ assert(image_dev.software_list_name());
- have_software = true;
- action(machine.system(), &image_dev, sw, done);
- if (done)
- return;
- }
+ have_software = true;
+ action(machine.system(), &image_dev, sw, done);
+ if (done)
+ return;
}
-
- if (!have_software)
- action(machine.system(), nullptr, nullptr, done);
}
+
+ if (!have_software)
+ action(machine.system(), nullptr, nullptr, done);
}
void favorite_manager::update_sorted()
@@ -509,7 +527,7 @@ void favorite_manager::update_sorted()
int cmp;
- cmp = core_stricmp(lhs.longname.c_str(), rhs.longname.c_str());
+ cmp = core_stricmp(lhs.longname, rhs.longname);
if (0 > cmp)
return true;
else if (0 < cmp)
diff --git a/src/frontend/mame/ui/inputdevices.cpp b/src/frontend/mame/ui/inputdevices.cpp
index 6dba1d49f92..1ee03ad5c81 100644
--- a/src/frontend/mame/ui/inputdevices.cpp
+++ b/src/frontend/mame/ui/inputdevices.cpp
@@ -13,6 +13,9 @@
#include "inputdev.h"
+// FIXME: allow OSD module headers to be included in a less ugly way
+#include "../osd/modules/lib/osdlib.h"
+
namespace ui {
@@ -24,6 +27,7 @@ public:
menu_input_device(mame_ui_manager &mui, render_container &container, input_device &device)
: menu(mui, container)
, m_device(device)
+ , m_have_analog(false)
{
set_heading(
util::string_format(_("menu-inputdev", "%1$s (%2$s %3$d)"),
@@ -33,37 +37,64 @@ public:
}
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override
+ {
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(0.0F, (line_height() * (m_have_analog ? 2.0F : 1.0F)) + (tb_border() * 3.0F));
+ }
+
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
{
if (selectedref)
{
+ // get the complete token for the highlighted input
input_device_item &input = *reinterpret_cast<input_device_item *>(selectedref);
+ input_code code = input.code();
+ if (!machine().input().device_class(m_device.devclass()).multi())
+ code.set_device_index(0);
+ std::string const token = machine().input().code_to_token(code);
+
+ // 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);
+ 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,
+ ui().colors().background_color());
+
+ // show the token
+ draw_text_normal(
+ token,
+ (1.0F - boxwidth) * 0.5F, y2 + (tb_border() * 2.0F), boxwidth,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
+ fgcolor);
+
+ // first show the token
switch (input.itemclass())
{
case ITEM_CLASS_ABSOLUTE:
case ITEM_CLASS_RELATIVE:
{
- // draw the outer box
- ui().draw_outlined_box(container(), x, y2 + ui().box_tb_border(), x2, y2 + bottom, ui().colors().background_color());
-
// draw the indicator
- rgb_t const fgcolor(ui().colors().text_color());
- float const border = ui().box_lr_border() * machine().render().ui_aspect(&container());
- float const lineheight = ui().get_line_height();
- float const indleft = x + border;
- float const indright = x2 - border;
- float const indtop = y2 + (ui().box_tb_border() * 2.0F) + (lineheight * 0.2F);
- float const indbottom = y2 + (ui().box_tb_border() * 2.0F) + (lineheight * 0.8F);
+ 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;
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)
{
- float const fillright = indcentre + (float(value) / float(osd::INPUT_ABSOLUTE_MAX) * (indright - indcentre));
+ float const fillright = indcentre + (float(value) / float(osd::input_device::ABSOLUTE_MAX) * (indright - indcentre));
container().add_rect(indcentre, indtop, (std::min)(fillright, indright), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
else if (0 > value)
{
- float const fillleft = indcentre - (float(value) / float(osd::INPUT_ABSOLUTE_MIN) * (indcentre - indleft));
+ float const fillleft = indcentre - (float(value) / float(osd::input_device::ABSOLUTE_MIN) * (indcentre - indleft));
container().add_rect((std::max)(fillleft, indleft), indtop, indcentre, indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
container().add_line(indleft, indtop, indright, indtop, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
@@ -80,9 +111,11 @@ protected:
}
private:
- virtual void populate(float &customtop, float &custombottom) override
+ virtual void populate() override
{
- bool haveanalog = false;
+ item_append(_("menu-inputdev", "Copy Device ID"), 0U, nullptr);
+ item_append(menu_item_type::SEPARATOR);
+
for (input_item_id itemid = ITEM_ID_FIRST_VALID; m_device.maxitem() >= itemid; ++itemid)
{
input_device_item *const input = m_device.item(itemid);
@@ -92,7 +125,7 @@ private:
{
case ITEM_CLASS_ABSOLUTE:
case ITEM_CLASS_RELATIVE:
- haveanalog = true;
+ m_have_analog = true;
break;
default:
break;
@@ -103,21 +136,37 @@ private:
item_append(menu_item_type::SEPARATOR);
- if (haveanalog)
- custombottom = ui().get_line_height() + (ui().box_tb_border() * 3.0F);
+ set_custom_space(0.0F, (line_height() * (m_have_analog ? 2.0F : 1.0F)) + (tb_border() * 3.0F));
}
- virtual void handle(event const *ev) override
+ virtual bool handle(event const *ev) override
{
+ // FIXME: hacky, depending on first item being "copy ID", but need a better model for item reference values
+ if (ev && ev->item && (IPT_UI_SELECT == ev->iptkey) && (&item(0) == ev->item))
+ {
+ if (!osd_set_clipboard_text(m_device.id()))
+ machine().popmessage(_("menu-inputdev", "Copied device ID to clipboard"));
+ else
+ machine().popmessage(_("menu-inputdev", "Error copying device ID to clipboard"));
+ }
+
+ bool updated = false;
for (int i = 0; item_count() > i; ++i)
{
void *const ref(item(i).ref());
if (ref)
{
input_device_item &input = *reinterpret_cast<input_device_item *>(ref);
- item(i).set_subtext(format_value(input));
+ std::string value(format_value(input));
+ if (item(i).subtext() != value)
+ {
+ item(i).set_subtext(std::move(value));
+ updated = true;
+ }
}
}
+
+ return updated;
}
static std::string format_value(input_device_item &input)
@@ -135,6 +184,7 @@ private:
}
input_device &m_device;
+ bool m_have_analog;
};
} // anonymous namespace
@@ -153,7 +203,7 @@ menu_input_devices::~menu_input_devices()
}
-void menu_input_devices::populate(float &customtop, float &custombottom)
+void menu_input_devices::populate()
{
// iterate input device classes and devices within each class
bool found = false;
@@ -191,68 +241,70 @@ void menu_input_devices::populate(float &customtop, float &custombottom)
}
-void menu_input_devices::handle(event const *ev)
+bool menu_input_devices::handle(event const *ev)
{
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ input_device &dev = *reinterpret_cast<input_device *>(ev->itemref);
+ switch (ev->iptkey)
{
- input_device &dev = *reinterpret_cast<input_device *>(ev->itemref);
- switch (ev->iptkey)
- {
- case IPT_UI_SELECT:
- stack_push<menu_input_device>(ui(), container(), dev);
- break;
+ case IPT_UI_SELECT:
+ stack_push<menu_input_device>(ui(), container(), dev);
+ break;
- case IPT_UI_PREV_GROUP:
+ case IPT_UI_PREV_GROUP:
+ {
+ auto group = dev.devclass();
+ bool found_break = false;
+ int target = 0;
+ for (auto i = selected_index(); 0 < i--; )
{
- auto group = dev.devclass();
- bool found_break = false;
- int target = 0;
- for (auto i = selected_index(); 0 < i--; )
+ input_device *const candidate = reinterpret_cast<input_device *>(item(i).ref());
+ if (candidate)
{
- input_device *const candidate = reinterpret_cast<input_device *>(item(i).ref());
- if (candidate)
+ if (candidate->devclass() == group)
{
- if (candidate->devclass() == group)
- {
- target = i;
- }
- else if (!found_break)
- {
- group = candidate->devclass();
- found_break = true;
- target = i;
- }
- else
- {
- set_selected_index(target);
- break;
- }
+ target = i;
}
- if (!i && found_break)
+ else if (!found_break)
+ {
+ group = candidate->devclass();
+ found_break = true;
+ target = i;
+ }
+ else
{
set_selected_index(target);
- break;
+ return true;
}
}
+ if (!i && found_break)
+ {
+ set_selected_index(target);
+ return true;
+ }
}
- break;
+ }
+ break;
- case IPT_UI_NEXT_GROUP:
+ case IPT_UI_NEXT_GROUP:
+ {
+ auto const group = dev.devclass();
+ for (auto i = selected_index(); item_count() > ++i; )
{
- auto const group = dev.devclass();
- for (auto i = selected_index(); item_count() > ++i; )
+ input_device *const candidate = reinterpret_cast<input_device *>(item(i).ref());
+ if (candidate && (candidate->devclass() != group))
{
- input_device *const candidate = reinterpret_cast<input_device *>(item(i).ref());
- if (candidate && (candidate->devclass() != group))
- {
- set_selected_index(i);
- break;
- }
+ set_selected_index(i);
+ return true;
}
}
- break;
}
+ break;
}
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/inputdevices.h b/src/frontend/mame/ui/inputdevices.h
index cb81caaaf47..3d4bfb51e4e 100644
--- a/src/frontend/mame/ui/inputdevices.h
+++ b/src/frontend/mame/ui/inputdevices.h
@@ -25,8 +25,8 @@ public:
virtual ~menu_input_devices();
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/inputmap.cpp b/src/frontend/mame/ui/inputmap.cpp
index 161ce0f03c6..d8a4d9656f4 100644
--- a/src/frontend/mame/ui/inputmap.cpp
+++ b/src/frontend/mame/ui/inputmap.cpp
@@ -33,7 +33,7 @@ menu_input_groups::~menu_input_groups()
{
}
-void menu_input_groups::populate(float &customtop, float &custombottom)
+void menu_input_groups::populate()
{
// build up the menu
item_append(_("User Interface"), 0, (void *)uintptr_t(IPG_UI + 1));
@@ -46,9 +46,8 @@ void menu_input_groups::populate(float &customtop, float &custombottom)
item_append(menu_item_type::SEPARATOR);
}
-void menu_input_groups::handle(event const *ev)
+bool menu_input_groups::handle(event const *ev)
{
- // process the menu
if (ev && (ev->iptkey == IPT_UI_SELECT))
{
menu::stack_push<menu_input_general>(
@@ -57,6 +56,8 @@ void menu_input_groups::handle(event const *ev)
int(uintptr_t(ev->itemref) - 1),
util::string_format(_("Input Assignments (%1$s)"), ev->item->text()));
}
+
+ return false;
}
@@ -82,7 +83,7 @@ void menu_input_general::menu_activated()
reset(reset_options::REMEMBER_POSITION);
}
-void menu_input_general::populate(float &customtop, float &custombottom)
+void menu_input_general::populate()
{
if (data.empty())
{
@@ -130,7 +131,7 @@ void menu_input_general::populate(float &customtop, float &custombottom)
}
// populate the menu in a standard fashion
- populate_sorted(customtop, custombottom);
+ populate_sorted();
item_append(menu_item_type::SEPARATOR);
}
@@ -164,7 +165,7 @@ void menu_input_specific::menu_activated()
reset(reset_options::REMEMBER_POSITION);
}
-void menu_input_specific::populate(float &customtop, float &custombottom)
+void menu_input_specific::populate()
{
if (data.empty())
{
@@ -249,7 +250,7 @@ void menu_input_specific::populate(float &customtop, float &custombottom)
// populate the menu in a standard fashion
if (!data.empty())
- populate_sorted(customtop, custombottom);
+ populate_sorted();
else
item_append(_("[no assignable inputs are enabled]"), FLAG_DISABLE, nullptr);
@@ -311,7 +312,17 @@ void menu_input::toggle_none_default(input_seq &selected_seq, input_seq &origina
selected_seq.reset();
}
-void menu_input::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+
+void menu_input::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ // leave space for showing the input sequence below the menu
+ set_custom_space(0.0F, 2.0F * line_height() + 3.0F * tb_border());
+}
+
+
+void menu_input::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
if (pollingitem)
{
@@ -319,9 +330,9 @@ 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 + ui().box_tb_border(), y2 + bottom,
+ x1, x2, y2 + tb_border(), y2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
}
else
{
@@ -336,9 +347,9 @@ 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 + ui().box_tb_border(), y2 + bottom,
+ x1, x2, y2 + tb_border(), y2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), UI_RED_COLOR, 1.0f);
+ ui().colors().text_color(), UI_RED_COLOR);
}
else if (selectedref)
{
@@ -348,9 +359,9 @@ 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 + ui().box_tb_border(), y2 + bottom,
+ x1, x2, y2 + tb_border(), y2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
}
else
{
@@ -359,18 +370,19 @@ 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 + ui().box_tb_border(), y2 + bottom,
+ x1, x2, y2 + tb_border(), y2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
}
}
}
}
-void menu_input::handle(event const *ev)
+bool menu_input::handle(event const *ev)
{
input_item_data *seqchangeditem = nullptr;
bool invalidate = false;
+ bool redraw = false;
// process the menu
if (pollingitem)
@@ -384,22 +396,12 @@ void menu_input::handle(event const *ev)
if (machine().ui_input().pressed(IPT_UI_CANCEL))
{
- // if UI_CANCEL is pressed, abort
+ // if UI_CANCEL is pressed, abort and abandon changes
pollingitem = nullptr;
set_process_flags(PROCESS_LR_ALWAYS);
- if (!seq_poll->modified() || modified_ticks == osd_ticks())
- {
- // cancelled immediately - toggle between default and none
- record_next = false;
- toggle_none_default(item->seq, starting_seq, *item->defseq);
- seqchangeditem = item;
- }
- else
- {
- // entered something before cancelling - abandon change
- invalidate = true;
- }
+ invalidate = true;
seq_poll.reset();
+ machine().ui_input().reset();
}
else if (seq_poll->poll()) // poll again; if finished, update the sequence
{
@@ -419,6 +421,12 @@ void menu_input::handle(event const *ev)
erroritem = item;
}
seq_poll.reset();
+ machine().ui_input().reset();
+ }
+ else
+ {
+ // always redraw to ensure it updates as soon as possible in response to changes
+ redraw = true;
}
}
else if (ev && ev->itemref)
@@ -515,6 +523,7 @@ void menu_input::handle(event const *ev)
}
record_next = false;
lastitem = &item;
+ redraw = true;
}
// flip between set and append
@@ -530,6 +539,7 @@ void menu_input::handle(event const *ev)
{
record_next = !record_next;
}
+ redraw = true;
}
}
@@ -545,6 +555,8 @@ void menu_input::handle(event const *ev)
// if the menu is invalidated, clear it now
if (invalidate)
reset(reset_options::REMEMBER_POSITION);
+
+ return redraw && !invalidate;
}
@@ -554,7 +566,7 @@ void menu_input::handle(event const *ev)
// menu from them
//-------------------------------------------------
-void menu_input::populate_sorted(float &customtop, float &custombottom)
+void menu_input::populate_sorted()
{
const char *nameformat[INPUT_TYPE_TOTAL] = { nullptr };
@@ -608,9 +620,6 @@ void menu_input::populate_sorted(float &customtop, float &custombottom)
appendprompt = util::string_format(_("Press %1$s to append\n"), ui().get_general_input_setting(IPT_UI_SELECT));
clearprompt = util::string_format(_("Press %1$s to clear\n"), ui().get_general_input_setting(IPT_UI_CLEAR));
defaultprompt = util::string_format(_("Press %1$s to restore default\n"), ui().get_general_input_setting(IPT_UI_CLEAR));
-
- // leave space for showing the input sequence below the menu
- custombottom = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
}
} // namespace ui
diff --git a/src/frontend/mame/ui/inputmap.h b/src/frontend/mame/ui/inputmap.h
index c651a8cc3d3..585d8002a8c 100644
--- a/src/frontend/mame/ui/inputmap.h
+++ b/src/frontend/mame/ui/inputmap.h
@@ -28,8 +28,8 @@ public:
virtual ~menu_input_groups() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
@@ -64,7 +64,10 @@ protected:
menu_input(mame_ui_manager &mui, render_container &container);
- void populate_sorted(float &customtop, float &custombottom);
+ 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 x1, float y1, float x2, float y2) override;
+
+ void populate_sorted();
void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq);
data_vector data;
@@ -81,8 +84,7 @@ private:
osd_ticks_t modified_ticks;
input_seq starting_seq;
- virtual void custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) override;
- virtual void handle(event const *ev) override;
+ virtual bool handle(event const *ev) override;
virtual void update_input(input_item_data &seqchangeditem) = 0;
};
@@ -97,7 +99,7 @@ protected:
virtual void menu_activated() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
+ virtual void populate() override;
virtual void update_input(input_item_data &seqchangeditem) override;
const int group;
@@ -114,7 +116,7 @@ protected:
virtual void menu_activated() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
+ virtual void populate() override;
virtual void update_input(input_item_data &seqchangeditem) override;
};
diff --git a/src/frontend/mame/ui/inputopts.cpp b/src/frontend/mame/ui/inputopts.cpp
index ff433e72e7e..48db7698e71 100644
--- a/src/frontend/mame/ui/inputopts.cpp
+++ b/src/frontend/mame/ui/inputopts.cpp
@@ -87,7 +87,7 @@ void menu_input_options::menu_activated()
}
-void menu_input_options::populate(float &customtop, float &custombottom)
+void menu_input_options::populate()
{
bool inputmap, analog, toggle;
scan_inputs(machine(), inputmap, analog, toggle);
@@ -111,7 +111,7 @@ void menu_input_options::populate(float &customtop, float &custombottom)
}
-void menu_input_options::handle(event const *ev)
+bool menu_input_options::handle(event const *ev)
{
if (ev && (IPT_UI_SELECT == ev->iptkey))
{
@@ -137,6 +137,8 @@ void menu_input_options::handle(event const *ev)
break;
}
}
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/inputopts.h b/src/frontend/mame/ui/inputopts.h
index 11432ce5035..bf6afebabf5 100644
--- a/src/frontend/mame/ui/inputopts.h
+++ b/src/frontend/mame/ui/inputopts.h
@@ -28,8 +28,8 @@ protected:
virtual void menu_activated() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/inputtoggle.cpp b/src/frontend/mame/ui/inputtoggle.cpp
index b21cd572c1d..7a307c01c66 100644
--- a/src/frontend/mame/ui/inputtoggle.cpp
+++ b/src/frontend/mame/ui/inputtoggle.cpp
@@ -35,7 +35,7 @@ void menu_input_toggles::menu_activated()
}
-void menu_input_toggles::populate(float &customtop, float &custombottom)
+void menu_input_toggles::populate()
{
// find toggle fields
if (m_fields.empty())
@@ -110,110 +110,111 @@ void menu_input_toggles::populate(float &customtop, float &custombottom)
}
-void menu_input_toggles::handle(event const *ev)
+bool menu_input_toggles::handle(event const *ev)
{
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ auto const ref = reinterpret_cast<std::reference_wrapper<ioport_field> *>(ev->itemref);
+ ioport_field &field = ref->get();
+ bool invalidate = false;
+ switch (ev->iptkey)
{
- auto const ref = reinterpret_cast<std::reference_wrapper<ioport_field> *>(ev->itemref);
- ioport_field &field = ref->get();
- bool invalidate = false;
- switch (ev->iptkey)
+ case IPT_UI_SELECT: // toggle regular items, cycle multi-value items
+ if (field.settings().empty())
+ field.live().value ^= field.mask();
+ else
+ field.select_next_setting();
+ invalidate = true;
+ break;
+
+ case IPT_UI_CLEAR: // set to default
+ if (field.defvalue() != field.live().value)
{
- case IPT_UI_SELECT: // toggle regular items, set multi-value items to default
- if (field.settings().empty())
- {
- field.live().value ^= field.mask();
- invalidate = true;
- break;
- }
- [[fallthrough]];
-
- case IPT_UI_CLEAR: // set to default
- if (field.defvalue() != field.live().value)
- {
- field.live().value = field.defvalue();
- invalidate = true;
- }
- break;
-
- case IPT_UI_LEFT: // toggle or select previous setting
- if (field.settings().empty())
- field.live().value ^= field.mask();
- else
- field.select_previous_setting();
+ field.live().value = field.defvalue();
invalidate = true;
- break;
-
- case IPT_UI_RIGHT: // toggle or select next setting
- if (field.settings().empty())
- field.live().value ^= field.mask();
- else
- field.select_next_setting();
- invalidate = true;
- break;
-
- case IPT_UI_PREV_GROUP: // previous device if any
+ }
+ break;
+
+ case IPT_UI_LEFT: // toggle or select previous setting
+ if (field.settings().empty())
+ field.live().value ^= field.mask();
+ else
+ field.select_previous_setting();
+ invalidate = true;
+ break;
+
+ case IPT_UI_RIGHT: // toggle or select next setting
+ if (field.settings().empty())
+ field.live().value ^= field.mask();
+ else
+ field.select_next_setting();
+ invalidate = true;
+ break;
+
+ case IPT_UI_PREV_GROUP: // previous device if any
+ {
+ auto current = std::distance(m_fields.data(), ref);
+ device_t const *dev = &field.device();
+ bool found_break = false;
+ void *candidate = nullptr;
+ while (0 < current)
{
- auto current = std::distance(m_fields.data(), ref);
- device_t const *dev = &field.device();
- bool found_break = false;
- void *candidate = nullptr;
- while (0 < current)
+ if (!found_break)
{
- if (!found_break)
+ if (m_fields[--current].get().enabled())
{
- if (m_fields[--current].get().enabled())
+ device_t const *prev = &m_fields[current].get().device();
+ if (prev != dev)
{
- device_t const *prev = &m_fields[current].get().device();
- if (prev != dev)
- {
- dev = prev;
- found_break = true;
- candidate = &m_fields[current];
- }
+ dev = prev;
+ found_break = true;
+ candidate = &m_fields[current];
}
}
- else if (&m_fields[--current].get().device() != dev)
- {
- set_selection(candidate);
- set_top_line(selected_index() - 1);
- break;
- }
- else if (m_fields[current].get().enabled())
- {
- candidate = &m_fields[current];
- }
- if (found_break && !current)
- {
- set_selection(candidate);
- set_top_line(selected_index() - 1);
- break;
- }
+ }
+ else if (&m_fields[--current].get().device() != dev)
+ {
+ set_selection(candidate);
+ set_top_line(selected_index() - 1);
+ return true;
+ }
+ else if (m_fields[current].get().enabled())
+ {
+ candidate = &m_fields[current];
+ }
+ if (found_break && !current)
+ {
+ set_selection(candidate);
+ set_top_line(selected_index() - 1);
+ return true;
}
}
- break;
+ }
+ break;
- case IPT_UI_NEXT_GROUP: // next device if any
+ case IPT_UI_NEXT_GROUP: // next device if any
+ {
+ auto current = std::distance(m_fields.data(), ref);
+ device_t const *const dev = &field.device();
+ while (m_fields.size() > ++current)
{
- auto current = std::distance(m_fields.data(), ref);
- device_t const *const dev = &field.device();
- while (m_fields.size() > ++current)
+ if (m_fields[current].get().enabled() && (&m_fields[current].get().device() != dev))
{
- if (m_fields[current].get().enabled() && (&m_fields[current].get().device() != dev))
- {
- set_selection(&m_fields[current]);
- set_top_line(selected_index() - 1);
- break;
- }
+ set_selection(&m_fields[current]);
+ set_top_line(selected_index() - 1);
+ return true;
}
}
- break;
}
-
- // changing value can enable or disable other fields
- if (invalidate)
- reset(reset_options::REMEMBER_REF);
+ break;
}
+
+ // changing value can enable or disable other fields
+ if (invalidate)
+ reset(reset_options::REMEMBER_REF);
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/inputtoggle.h b/src/frontend/mame/ui/inputtoggle.h
index 7a3016f6254..676be71e58d 100644
--- a/src/frontend/mame/ui/inputtoggle.h
+++ b/src/frontend/mame/ui/inputtoggle.h
@@ -31,8 +31,8 @@ protected:
virtual void menu_activated() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
std::vector<std::reference_wrapper<ioport_field> > m_fields;
};
diff --git a/src/frontend/mame/ui/keyboard.cpp b/src/frontend/mame/ui/keyboard.cpp
index 7efebefd64c..9eaeb5c94ee 100644
--- a/src/frontend/mame/ui/keyboard.cpp
+++ b/src/frontend/mame/ui/keyboard.cpp
@@ -35,7 +35,7 @@ void menu_keyboard_mode::menu_activated()
reset(reset_options::REMEMBER_POSITION);
}
-void menu_keyboard_mode::populate(float &customtop, float &custombottom)
+void menu_keyboard_mode::populate()
{
natural_keyboard &natkbd(machine().natkeyboard());
@@ -71,47 +71,51 @@ menu_keyboard_mode::~menu_keyboard_mode()
{
}
-void menu_keyboard_mode::handle(event const *ev)
+bool menu_keyboard_mode::handle(event const *ev)
{
- if (ev && uintptr_t(ev->itemref))
+ if (!ev || !uintptr_t(ev->itemref))
+ return false;
+
+ natural_keyboard &natkbd(machine().natkeyboard());
+ uintptr_t const ref(uintptr_t(ev->itemref));
+ bool left(IPT_UI_LEFT == ev->iptkey);
+ bool right(IPT_UI_RIGHT == ev->iptkey);
+ if (ITEM_KBMODE == ref)
{
- natural_keyboard &natkbd(machine().natkeyboard());
- uintptr_t const ref(uintptr_t(ev->itemref));
- bool left(IPT_UI_LEFT == ev->iptkey);
- bool right(IPT_UI_RIGHT == ev->iptkey);
- if (ITEM_KBMODE == ref)
+ if (IPT_UI_SELECT == ev->iptkey)
{
- if (IPT_UI_SELECT == ev->iptkey)
- {
- left = natkbd.in_use();
- right = !left;
- }
- if ((left || right) && (natkbd.in_use() != right))
- {
- natkbd.set_in_use(right);
- ev->item->set_subtext(right ? _("menu-keyboard", "Natural") : _("menu-keyboard", "Emulated"));
- ev->item->set_flags(right ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW);
- }
+ left = natkbd.in_use();
+ right = !left;
}
- else if (ITEM_KBDEV_FIRST <= ref)
+ if ((left || right) && (natkbd.in_use() != right))
{
- auto const kbdno(ref - ITEM_KBDEV_FIRST);
- if (IPT_UI_SELECT == ev->iptkey)
- {
- left = natkbd.keyboard_enabled(kbdno);
- right = !left;
- }
- if ((left || right) && (natkbd.keyboard_enabled(kbdno) != right))
- {
- if (right)
- natkbd.enable_keyboard(kbdno);
- else
- natkbd.disable_keyboard(kbdno);
- ev->item->set_subtext(right ? _("Enabled") : _("Disabled"));
- ev->item->set_flags(right ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW);
- }
+ natkbd.set_in_use(right);
+ ev->item->set_subtext(right ? _("menu-keyboard", "Natural") : _("menu-keyboard", "Emulated"));
+ ev->item->set_flags(right ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW);
+ return true;
}
}
+ else if (ITEM_KBDEV_FIRST <= ref)
+ {
+ auto const kbdno(ref - ITEM_KBDEV_FIRST);
+ if (IPT_UI_SELECT == ev->iptkey)
+ {
+ left = natkbd.keyboard_enabled(kbdno);
+ right = !left;
+ }
+ if ((left || right) && (natkbd.keyboard_enabled(kbdno) != right))
+ {
+ if (right)
+ natkbd.enable_keyboard(kbdno);
+ else
+ natkbd.disable_keyboard(kbdno);
+ ev->item->set_subtext(right ? _("Enabled") : _("Disabled"));
+ ev->item->set_flags(right ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW);
+ return true;
+ }
+ }
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/keyboard.h b/src/frontend/mame/ui/keyboard.h
index 36371f5711c..dd51e6c9d1e 100644
--- a/src/frontend/mame/ui/keyboard.h
+++ b/src/frontend/mame/ui/keyboard.h
@@ -27,8 +27,8 @@ protected:
virtual void menu_activated() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/mainmenu.cpp b/src/frontend/mame/ui/mainmenu.cpp
index aebe9a5886b..22a2c01f6c1 100644
--- a/src/frontend/mame/ui/mainmenu.cpp
+++ b/src/frontend/mame/ui/mainmenu.cpp
@@ -37,6 +37,7 @@
#include "machine/bcreader.h"
#include "crsshair.h"
+#include "dinetwork.h"
#include "dipty.h"
#include "emuopts.h"
@@ -64,8 +65,7 @@ enum : unsigned {
BARCODE_READ,
PTY_INFO,
EXTERNAL_DATS,
- ADD_FAVORITE,
- REMOVE_FAVORITE,
+ FAVORITE,
ABOUT,
QUIT_GAME,
DISMISS,
@@ -107,7 +107,7 @@ void menu_main::menu_activated()
populate - populate main menu items
-------------------------------------------------*/
-void menu_main::populate(float &customtop, float &custombottom)
+void menu_main::populate()
{
m_phase = machine().phase();
@@ -126,16 +126,18 @@ void menu_main::populate(float &customtop, float &custombottom)
item_append(_("menu-main", "Warning Information"), 0, (void *)WARN_INFO);
for (device_image_interface &image : image_interface_enumerator(machine().root_device()))
- {
if (image.user_loadable())
{
item_append(_("menu-main", "Media Image Information"), 0, (void *)IMAGE_MENU_IMAGE_INFO);
+ break;
+ }
+ for (device_image_interface &image : image_interface_enumerator(machine().root_device()))
+ if (image.user_loadable() || image.has_preset_images_selection())
+ {
item_append(_("menu-main", "File Manager"), 0, (void *)IMAGE_MENU_FILE_MANAGER);
-
break;
}
- }
if (cassette_device_enumerator(machine().root_device()).first() != nullptr)
item_append(_("menu-main", "Tape Control"), 0, (void *)TAPE_CONTROL);
@@ -163,7 +165,7 @@ void menu_main::populate(float &customtop, float &custombottom)
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)
{
@@ -177,9 +179,9 @@ void menu_main::populate(float &customtop, float &custombottom)
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);
@@ -205,7 +207,7 @@ void menu_main::populate(float &customtop, float &custombottom)
handle - handle main menu events
-------------------------------------------------*/
-void menu_main::handle(event const *ev)
+bool menu_main::handle(event const *ev)
{
// process the menu
if (ev && (ev->iptkey == IPT_UI_SELECT))
@@ -303,15 +305,16 @@ void 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();
@@ -320,12 +323,14 @@ void menu_main::handle(event const *ev)
case DISMISS:
stack_pop();
- return;
+ break;
default:
fatalerror("ui::menu_main::handle - unknown reference\n");
}
}
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/mainmenu.h b/src/frontend/mame/ui/mainmenu.h
index 6671dd9f554..d34da5e009e 100644
--- a/src/frontend/mame/ui/mainmenu.h
+++ b/src/frontend/mame/ui/mainmenu.h
@@ -28,8 +28,8 @@ protected:
virtual void menu_activated() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
machine_phase m_phase;
};
diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp
index dbe1b29a813..00710e073a9 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"
@@ -25,9 +24,12 @@
#include "rendutil.h"
#include "uiinput.h"
-#include <algorithm>
+#include "osdepend.h"
+
#include <cassert>
-#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
namespace ui {
@@ -74,6 +76,12 @@ menu::global_state::global_state(mame_ui_manager &ui)
, m_stack()
, m_free()
, m_hide(false)
+ , m_current_pointer(-1)
+ , m_pointer_type(ui_event::pointer::UNKNOWN)
+ , m_pointer_buttons(0U)
+ , m_pointer_x(-1.0F)
+ , m_pointer_y(-1.0F)
+ , m_pointer_hit(false)
{
render_manager &render(ui.machine().render());
@@ -119,6 +127,21 @@ void menu::global_state::stack_push(std::unique_ptr<menu> &&menu)
}
menu->m_parent = std::move(m_stack);
m_stack = std::move(menu);
+
+ ui_event uievt;
+ while (m_stack->machine().ui_input().pop_event(&uievt))
+ {
+ switch (uievt.event_type)
+ {
+ case ui_event::type::POINTER_UPDATE:
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ use_pointer(m_stack->machine().render().ui_target(), m_stack->container(), uievt);
+ break;
+ default:
+ break;
+ }
+ }
m_stack->machine().ui_input().reset();
}
@@ -139,7 +162,22 @@ void menu::global_state::stack_pop()
m_stack = std::move(menu->m_parent);
menu->m_parent = std::move(m_free);
m_free = std::move(menu);
- m_ui.machine().ui_input().reset();
+
+ ui_event uievt;
+ while (m_free->machine().ui_input().pop_event(&uievt))
+ {
+ switch (uievt.event_type)
+ {
+ case ui_event::type::POINTER_UPDATE:
+ case ui_event::type::POINTER_LEAVE:
+ case ui_event::type::POINTER_ABORT:
+ use_pointer(m_free->machine().render().ui_target(), m_free->container(), uievt);
+ break;
+ default:
+ break;
+ }
+ }
+ m_free->machine().ui_input().reset();
}
}
@@ -184,40 +222,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));
- // update the menu state
- m_hide = false;
- if (m_stack)
- m_stack->do_handle();
+ return mame_ui_manager::HANDLER_UPDATE;
+ }
+ }
- // clear up anything pending being released
- clear_free_list();
+ // update the menu state
+ m_hide = false;
+ bool need_update(m_stack && m_stack->do_handle());
- // if the menus are to be hidden, return a cancel here
- if (m_ui.is_menu_active() && (m_hide || !m_stack))
- {
- if (m_stack)
+ // 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->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 UI_HANDLER_CANCEL;
- }
- return 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();
+ }
}
@@ -238,22 +388,40 @@ menu::menu(mame_ui_manager &mui, render_container &container)
, m_heading()
, m_items()
, m_rebuilding(false)
+ , m_last_size(0, 0)
+ , m_last_aspect(0.0F)
+ , m_line_height(0.0F)
+ , m_gutter_width(0.0F)
+ , m_tb_border(0.0F)
+ , 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);
@@ -278,6 +446,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;
@@ -309,10 +481,13 @@ void menu::set_special_main_menu(bool special)
// end of the menu
//-------------------------------------------------
-void menu::item_append(menu_item_type type, uint32_t flags)
+int menu::item_append(menu_item_type type, uint32_t flags)
{
+ assert(menu_item_type::SEPARATOR == type);
if (type == menu_item_type::SEPARATOR)
- item_append(MENU_SEPARATOR_ITEM, flags, nullptr, menu_item_type::SEPARATOR);
+ return item_append(MENU_SEPARATOR_ITEM, flags, nullptr, menu_item_type::SEPARATOR);
+ else
+ return -1;
}
//-------------------------------------------------
@@ -320,7 +495,7 @@ void menu::item_append(menu_item_type type, uint32_t flags)
// end of the menu
//-------------------------------------------------
-void menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type)
+int menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type)
{
assert(m_rebuilding);
@@ -346,6 +521,8 @@ void menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags
m_selected = index;
if (m_resetpos == (m_items.size() - 1))
m_selected = m_items.size() - 1;
+
+ return int(std::make_signed_t<decltype(index)>(index));
}
@@ -354,68 +531,26 @@ void menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags
// item to the end of the menu
//-------------------------------------------------
-void menu::item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type)
+int menu::item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type)
{
if (flags & FLAG_DISABLE)
ref = nullptr;
else
flags |= state ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW;
- item_append(std::string(text), state ? _("On") : _("Off"), flags, ref, type);
+ return item_append(std::string(text), state ? _("On") : _("Off"), flags, ref, type);
}
//-------------------------------------------------
-// process - process a menu, drawing it
-// and returning any interesting events
+// set_custom_space - set space required for
+// custom rendering above and below menu
//-------------------------------------------------
-const menu::event *menu::process()
+void menu::set_custom_space(float top, float bottom)
{
- // 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
- 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 &m_event;
- }
- else
- {
- return nullptr;
- }
+ m_customtop = top;
+ m_custombottom = bottom;
}
@@ -444,33 +579,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 aspect = machine().render().ui_aspect(&container());
- float const line_height = ui().get_line_height();
- float const lr_arrow_width = 0.4F * line_height * aspect;
- float const ud_arrow_width = line_height * aspect;
- float const gutter_width = 0.5F * line_height * aspect;
- float const lr_border = ui().box_lr_border() * aspect;
- float const max_width = 1.0F - ((lr_border + (aspect * 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();
@@ -481,31 +620,34 @@ void menu::draw(uint32_t flags)
for (auto const &pitem : m_items)
{
// compute width of left hand side
- float total_width = gutter_width + ui().get_string_width(pitem.text()) + gutter_width;
+ float total_width = gutter_width() + get_string_width(pitem.text()) + gutter_width();
// add in width of right hand side
if (!pitem.subtext().empty())
- total_width += 2.0F * gutter_width + ui().get_string_width(pitem.subtext());
+ total_width += 2.0F * gutter_width() + get_string_width(pitem.subtext());
else if (pitem.flags() & FLAG_UI_HEADING)
- total_width += 4.0F * ud_arrow_width;
+ total_width += 4.0F * ud_arrow_width();
// track the maximum
visible_width = std::max(total_width, visible_width);
// track the height as well
- visible_main_menu_height += line_height;
+ visible_main_menu_height += line_height();
}
// lay out the heading if present
std::optional<text_layout> heading_layout;
if (m_heading)
{
- heading_layout.emplace(ui().create_layout(container(), max_width - (gutter_width * 2.0F), text_layout::text_justify::CENTER));
+ 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
- float const top_extra_menu_height = m_customtop + (heading_layout ? (heading_layout->actual_height() + (ui().box_tb_border() * 3.0F)) : 0.0F);
+ float const top_extra_menu_height = m_customtop + (heading_layout ? (heading_layout->actual_height() + (tb_border() * 3.0F)) : 0.0F);
float const visible_extra_menu_height = top_extra_menu_height + m_custombottom;
// add a little bit of slop for rounding
@@ -516,36 +658,33 @@ void menu::draw(uint32_t flags)
visible_width = std::min(visible_width, max_width);
// if the menu and extra menu won't fit, take away part of the regular menu, it will scroll
- if (visible_main_menu_height + visible_extra_menu_height + 2.0F * ui().box_tb_border() > 1.0F)
- visible_main_menu_height = 1.0F - 2.0F * ui().box_tb_border() - visible_extra_menu_height;
+ if (visible_main_menu_height + visible_extra_menu_height + 2.0F * tb_border() > 1.0F)
+ visible_main_menu_height = 1.0F - 2.0F * tb_border() - visible_extra_menu_height;
- m_visible_lines = std::min(int(std::floor(visible_main_menu_height / line_height)), int(unsigned(m_items.size())));
- visible_main_menu_height = float(m_visible_lines) * line_height;
+ m_visible_lines = std::min(int(std::floor(visible_main_menu_height / line_height())), int(unsigned(m_items.size())));
+ visible_main_menu_height = float(m_visible_lines) * line_height();
// compute top/left of inner menu area by centering
float const visible_left = (1.0F - visible_width) * 0.5F;
- float const visible_top = ((1.0F - visible_main_menu_height - visible_extra_menu_height) * 0.5F) + top_extra_menu_height;
+ m_items_top = std::round((((1.0F - visible_main_menu_height - visible_extra_menu_height) * 0.5F) + top_extra_menu_height) * float(m_last_size.second)) / float(m_last_size.second);
// first add us a box
- float const x1 = visible_left - lr_border;
- float const y1 = visible_top - ui().box_tb_border();
- float const x2 = visible_left + visible_width + lr_border;
- float const y2 = visible_top + visible_main_menu_height + ui().box_tb_border();
+ float const x1 = visible_left - lr_border();
+ float const y1 = m_items_top - tb_border();
+ float const x2 = visible_left + visible_width + lr_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 - ui().box_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 + ui().box_tb_border());
+ 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,
@@ -563,29 +702,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();
+ float const effective_width = visible_width - 2.0F * gutter_width();
+ float const effective_left = visible_left + gutter_width();
// 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;
@@ -595,77 +748,80 @@ 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_y1 = line_y0 + 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)
{
// if we're just a divider, draw a line
- container().add_line(visible_left, line_y0 + 0.5F * line_height, visible_left + visible_width, line_y0 + 0.5F * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_line(visible_left, line_y0 + 0.5F * line_height(), visible_left + visible_width, line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
else if (pitem.subtext().empty())
{
// if we don't have a subitem, just draw the string centered
if (pitem.flags() & FLAG_UI_HEADING)
{
- float heading_width = ui().get_string_width(itemtext);
- container().add_line(visible_left, line_y0 + 0.5F * line_height, visible_left + ((visible_width - heading_width) / 2) - lr_border, line_y0 + 0.5F * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- container().add_line(visible_left + visible_width - ((visible_width - heading_width) / 2) + lr_border, line_y0 + 0.5F * line_height, visible_left + visible_width, line_y0 + 0.5F * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ float heading_width = get_string_width(itemtext);
+ container().add_line(visible_left, line_y0 + 0.5F * line_height(), visible_left + ((visible_width - heading_width) / 2) - lr_border(), line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_line(visible_left + visible_width - ((visible_width - heading_width) / 2) + lr_border(), line_y0 + 0.5F * line_height(), visible_left + visible_width, line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
ui().draw_text_full(
container(),
@@ -673,7 +829,8 @@ void menu::draw(uint32_t flags)
effective_left, line_y0, effective_width,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
mame_ui_manager::NORMAL, fgcolor, bgcolor,
- nullptr, nullptr);
+ nullptr, nullptr,
+ line_height());
}
else
{
@@ -688,14 +845,15 @@ void menu::draw(uint32_t flags)
effective_left, line_y0, effective_width,
text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
mame_ui_manager::NORMAL, fgcolor, bgcolor,
- &item_width, nullptr);
+ &item_width, nullptr,
+ line_height());
if (pitem.flags() & FLAG_COLOR_BOX)
{
rgb_t color = rgb_t((uint32_t)strtoul(pitem.subtext().c_str(), nullptr, 16));
// give 2 spaces worth of padding
- subitem_width = ui().get_string_width("FF00FF00");
+ subitem_width = get_string_width("FF00FF00");
ui().draw_outlined_box(
container(),
@@ -708,10 +866,10 @@ void menu::draw(uint32_t flags)
std::string_view subitem_text(pitem.subtext());
// give 2 spaces worth of padding
- item_width += 2.0F * gutter_width;
+ item_width += 2.0F * gutter_width();
// if the subitem doesn't fit here, display dots
- if (ui().get_string_width(subitem_text) > effective_width - item_width)
+ if (get_string_width(subitem_text) > effective_width - item_width)
{
subitem_text = "...";
if (is_selected(itemnum))
@@ -719,13 +877,13 @@ void menu::draw(uint32_t flags)
}
// customize subitem text color
- if (!core_stricmp(pitem.subtext().c_str(), _("On")))
+ if (!core_stricmp(pitem.subtext(), _("On")))
fgcolor2 = rgb_t(0x00,0xff,0x00);
- if (!core_stricmp(pitem.subtext().c_str(), _("Off")))
+ if (!core_stricmp(pitem.subtext(), _("Off")))
fgcolor2 = rgb_t(0xff,0x00,0x00);
- if (!core_stricmp(pitem.subtext().c_str(), _("Auto")))
+ if (!core_stricmp(pitem.subtext(), _("Auto")))
fgcolor2 = rgb_t(0xff,0xff,0x00);
// draw the subitem right-justified
@@ -735,31 +893,27 @@ void menu::draw(uint32_t flags)
effective_left + item_width, line_y0, effective_width - item_width,
text_layout::text_justify::RIGHT, text_layout::word_wrapping::TRUNCATE,
mame_ui_manager::NORMAL, subitem_invert ? fgcolor3 : fgcolor2, bgcolor,
- &subitem_width, nullptr);
+ &subitem_width, nullptr,
+ line_height());
}
// apply arrows
- if (is_selected(itemnum) && (pitem.flags() & FLAG_LEFT_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))
+ if (is_selected(itemnum) && (pitem.flags() & (FLAG_LEFT_ARROW | 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);
+ }
}
}
}
@@ -771,29 +925,25 @@ 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 target_width, target_height;
+ float const line_y = m_items_top + float(linenum) * line_height();
// compute the multi-line target width/height
- ui().draw_text_full(
- container(),
+ auto const [target_width, target_height] = get_text_dimensions(
pitem.subtext(),
0, 0, visible_width * 0.75F,
- text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
- &target_width, &target_height);
+ text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD);
// determine the target location
- float const target_x = visible_left + visible_width - target_width - lr_border;
- float target_y = line_y + line_height + ui().box_tb_border();
- if (target_y + target_height + ui().box_tb_border() > visible_main_menu_height)
- target_y = line_y - target_height - ui().box_tb_border();
+ float const target_x = visible_left + visible_width - target_width - lr_border();
+ float target_y = line_y + line_height() + tb_border();
+ if (target_y + target_height + tb_border() > visible_main_menu_height)
+ target_y = line_y - target_height - tb_border();
// add a box around that
ui().draw_outlined_box(
container(),
- target_x - lr_border, target_y - ui().box_tb_border(),
- target_x + target_width + lr_border, target_y + target_height + ui().box_tb_border(),
+ target_x - lr_border(), target_y - tb_border(),
+ target_x + target_width + lr_border(), target_y + target_height + tb_border(),
subitem_invert ? ui().colors().selected_bg_color() : ui().colors().background_color());
ui().draw_text_full(
@@ -806,43 +956,30 @@ 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::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
-}
+ float const ui_line_height = ui().get_line_height();
+ // force whole pixels for line height, gutters and borders
+ m_line_height = std::floor(ui_line_height * float(height)) / float(height);
+ m_gutter_width = std::floor(0.5F * ui_line_height * aspect * float(width)) / float(width);
+ m_tb_border = std::floor(ui().box_tb_border() * float(height)) / float(height);
+ m_lr_border = std::floor(ui().box_lr_border() * aspect * float(width)) / float(width);
-//-------------------------------------------------
-// map_mouse - map mouse pointer location to menu
-// coordinates
-//-------------------------------------------------
-
-void menu::map_mouse()
-{
- ignore_mouse();
- int32_t mouse_target_x, mouse_target_y;
- render_target *const mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &m_mouse_button);
- if (mouse_target)
- {
- if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, container(), m_mouse_x, m_mouse_y))
- m_mouse_hit = true;
- }
-}
+ m_lr_arrow_width = 0.4F * m_line_height * aspect;
+ m_ud_arrow_width = m_line_height * aspect;
+ // don't accept pointer input until the menu has been redrawn
+ m_items_drawn = false;
+ m_pointer_state = track_pointer::IDLE;
-//-------------------------------------------------
-// ignore_mouse - set members to ignore mouse
-// input
-//-------------------------------------------------
+}
-void menu::ignore_mouse()
+void menu::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
- m_mouse_hit = false;
- m_mouse_button = false;
- m_mouse_x = -1.0F;
- m_mouse_y = -1.0F;
}
@@ -851,8 +988,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;
@@ -861,119 +999,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())
- {
- m_selected = m_hover;
- }
- else if (m_hover == HOVER_ARROW_UP)
- {
- if (flags & PROCESS_CUSTOM_NAV)
- {
- ev.iptkey = IPT_UI_PAGE_UP;
- stop = true;
- }
- else
- {
- m_selected -= m_visible_items;
- if (m_selected < 0)
- m_selected = 0;
- top_line -= m_visible_items - (last_item_visible() ? 1 : 0);
- }
- }
- else if (m_hover == HOVER_ARROW_DOWN)
- {
- if (flags & PROCESS_CUSTOM_NAV)
- {
- ev.iptkey = IPT_UI_PAGE_DOWN;
- stop = true;
- }
- else
- {
- m_selected += m_visible_lines - 2 + is_first_selected();
- if (m_selected > m_items.size() - 1)
- m_selected = m_items.size() - 1;
- top_line += m_visible_lines - 2;
- }
- }
- else if (m_hover == HOVER_UI_LEFT)
+ auto const [key, redraw] = handle_pointer_update(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_LEFT;
+ ev.iptkey = key;
stop = true;
}
- else if (m_hover == HOVER_UI_RIGHT)
+ }
+ break;
+
+ // pointer left the normal way, possibly releasing buttons
+ case ui_event::type::POINTER_LEAVE:
+ {
+ auto const [key, redraw] = handle_pointer_leave(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_RIGHT;
+ ev.iptkey = key;
stop = true;
}
}
break;
- // if we are hovering over a valid item, fake a UI_SELECT with a double-click
- case ui_event::type::MOUSE_DOUBLE_CLICK:
- if (!(flags & PROCESS_ONLYCHAR) && m_hover >= 0 && m_hover < m_items.size())
+ // pointer left in some abnormal way - cancel any associated actions
+ case ui_event::type::POINTER_ABORT:
{
- m_selected = m_hover;
- ev.iptkey = IPT_UI_SELECT;
- if (is_last_selected() && m_needs_prev_menu_item)
+ auto const [key, redraw] = handle_pointer_abort(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_CANCEL;
- 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
@@ -981,6 +1100,536 @@ void menu::handle_events(uint32_t flags, event &ev)
break;
}
}
+
+ // deal with repeating scroll arrows
+ if ((track_pointer::TRACK_LINE == m_pointer_state) && ((!m_pointer_line && m_show_up_arrow) || ((m_pointer_line == (m_visible_lines - 1)) && m_show_down_arrow)))
+ {
+ float const linetop(m_items_top + (float(m_pointer_line) * line_height()));
+ float const linebottom(m_items_top + (float(m_pointer_line + 1) * line_height()));
+ if (pointer_in_rect(m_items_left, linetop, m_items_right, linebottom))
+ {
+ if (std::chrono::steady_clock::now() >= m_pointer_repeat)
+ {
+ if (!m_pointer_line)
+ {
+ // scroll up
+ assert(0 < top_line);
+ --top_line;
+ if (!top_line)
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ else
+ {
+ // scroll down
+ assert(m_items.size() > (top_line + m_visible_lines));
+ ++top_line;
+ if (m_items.size() == (top_line + m_visible_lines))
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ force_visible_selection();
+ need_update = true;
+ m_pointer_repeat += std::chrono::milliseconds(100);
+ }
+ }
+ }
+
+ return need_update;
+}
+
+
+//-------------------------------------------------
+// handle_pointer_update - handle a regular
+// pointer update
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_pointer_update(uint32_t flags, ui_event const &uievt)
+{
+ // decide whether to make this our current pointer
+ render_target &target(machine().render().ui_target());
+ auto const [ours, changed] = m_global_state.use_pointer(target, container(), uievt);
+ if (!ours)
+ {
+ return std::make_pair(IPT_INVALID, false);
+ }
+ else if (changed)
+ {
+ // if the active pointer changed, ignore if any buttons were already down
+ if (uievt.pointer_buttons != uievt.pointer_pressed)
+ {
+ m_pointer_state = track_pointer::IGNORED;
+ return std::make_pair(IPT_INVALID, false);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::IDLE;
+ }
+ }
+ else if ((track_pointer::IGNORED == m_pointer_state) || (track_pointer::COMPLETED == m_pointer_state))
+ {
+ // stop ignoring the pointer if all buttons were released
+ if (uievt.pointer_buttons == uievt.pointer_pressed)
+ m_pointer_state = track_pointer::IDLE;
+ else
+ return std::make_pair(IPT_INVALID, false);
+ }
+
+ // give derived class a chance to handle it
+ if ((track_pointer::IDLE == m_pointer_state) || (track_pointer::CUSTOM == m_pointer_state))
+ {
+ auto const [key, take, redraw] = custom_pointer_updated(changed, uievt);
+ if (take)
+ {
+ m_pointer_state = track_pointer::CUSTOM;
+ return std::make_pair(key, redraw);
+ }
+ else if (track_pointer::CUSTOM == m_pointer_state)
+ {
+ if (uievt.pointer_buttons)
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ return std::make_pair(key, redraw);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::IDLE;
+ }
+ }
+
+ if (IPT_INVALID != key)
+ return std::make_pair(key, redraw);
+ }
+
+ // ignore altogether if menu hasn't been drawn or flags say so
+ if (!m_items_drawn || (flags & (PROCESS_CUSTOM_ONLY | PROCESS_ONLYCHAR)))
+ {
+ if (uievt.pointer_pressed)
+ {
+ if (track_pointer::IDLE == m_pointer_state)
+ m_pointer_state = track_pointer::IGNORED;
+ }
+ else if (!uievt.pointer_buttons)
+ {
+ if ((track_pointer::IGNORED == m_pointer_state) || (track_pointer::COMPLETED == m_pointer_state))
+ m_pointer_state = track_pointer::IDLE;
+ }
+ return std::make_pair(IPT_INVALID, false);
+ }
+
+ switch (m_pointer_state)
+ {
+ case track_pointer::IDLE:
+ // ignore anything other than left click for now
+ if ((uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x1)))
+ return handle_primary_down(flags, uievt);
+ else if (uievt.pointer_pressed)
+ m_pointer_state = track_pointer::IGNORED;
+ break;
+
+ case track_pointer::IGNORED:
+ case track_pointer::COMPLETED:
+ case track_pointer::CUSTOM:
+ std::abort(); // won't get here - handled earlier
+
+ case track_pointer::TRACK_LINE:
+ {
+ auto const result(update_line_click(uievt));
+
+ // treat anything else being pressed as cancelling the click sequence
+ if (uievt.pointer_buttons & ~u32(0x01))
+ m_pointer_state = track_pointer::COMPLETED;
+ else if (!uievt.pointer_buttons)
+ m_pointer_state = track_pointer::IDLE;
+
+ return result;
+ }
+
+ case track_pointer::SCROLL:
+ {
+ bool const redraw(update_drag_scroll(uievt));
+
+ // treat anything else being pressed as cancelling the drag
+ if (uievt.pointer_buttons & ~u32(0x01))
+ m_pointer_state = track_pointer::COMPLETED;
+ else if (!uievt.pointer_buttons)
+ m_pointer_state = track_pointer::IDLE;
+
+ return std::make_pair(IPT_INVALID, redraw);
+ }
+
+ case track_pointer::ADJUST:
+ {
+ auto const result(update_drag_adjust(uievt));
+
+ // treat anything else being pressed as cancelling the drag
+ if (uievt.pointer_buttons & ~u32(0x01))
+ m_pointer_state = track_pointer::COMPLETED;
+ else if (!uievt.pointer_buttons)
+ m_pointer_state = track_pointer::IDLE;
+
+ return result;
+ }
+ }
+
+ return std::make_pair(IPT_INVALID, false);
+}
+
+
+//-------------------------------------------------
+// handle_pointer_leave - handle a pointer
+// leaving the window
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_pointer_leave(uint32_t flags, ui_event const &uievt)
+{
+ // ignore pointer input in windows other than the one that displays the UI
+ render_target &target(machine().render().ui_target());
+ auto const [ours, changed] = m_global_state.use_pointer(target, container(), uievt);
+ assert(!changed);
+ if (!ours)
+ return std::make_pair(IPT_INVALID, false);
+
+ int key(IPT_INVALID);
+ bool redraw(false);
+ switch (m_pointer_state)
+ {
+ case track_pointer::IDLE:
+ case track_pointer::CUSTOM:
+ std::tie(key, std::ignore, redraw) = custom_pointer_updated(changed, uievt);
+ break;
+
+ case track_pointer::IGNORED:
+ case track_pointer::COMPLETED:
+ break; // nothing to do
+
+ case track_pointer::TRACK_LINE:
+ std::tie(key, redraw) = update_line_click(uievt);
+ break;
+
+ case track_pointer::SCROLL:
+ redraw = update_drag_scroll(uievt);
+ break;
+
+ case track_pointer::ADJUST:
+ std::tie(key, redraw) = update_drag_adjust(uievt);
+ break;
+ }
+
+ m_pointer_state = track_pointer::IDLE;
+ return std::make_pair(key, redraw);
+}
+
+
+//-------------------------------------------------
+// handle_pointer_abort - handle a pointer
+// leaving in an abnormal way
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_pointer_abort(uint32_t flags, ui_event const &uievt)
+{
+ // ignore pointer input in windows other than the one that displays the UI
+ render_target &target(machine().render().ui_target());
+ auto const [ours, changed] = m_global_state.use_pointer(target, container(), uievt);
+ assert(!changed);
+ if (!ours)
+ return std::make_pair(IPT_INVALID, false);
+
+ int key(IPT_INVALID);
+ bool redraw(false);
+ if (track_pointer::CUSTOM == m_pointer_state)
+ std::tie(key, std::ignore, redraw) = custom_pointer_updated(false, uievt);
+ else if (track_pointer::TRACK_LINE == m_pointer_state)
+ redraw = true;
+ m_pointer_state = track_pointer::IDLE;
+ return std::make_pair(key, redraw);
+}
+
+
+//-------------------------------------------------
+// handle_primary_down - handle the primary
+// action for a pointer device
+//-------------------------------------------------
+
+std::pair<int, bool> menu::handle_primary_down(uint32_t flags, ui_event const &uievt)
+{
+ // we handle touch differently to mouse or pen
+ bool const is_touch(ui_event::pointer::TOUCH == uievt.pointer_type);
+ auto const [x, y] = pointer_location(); // FIXME: need starting location for multi-click actions
+
+ // check increase/decrease arrows first
+ // FIXME: should repeat if appropriate
+ if (!is_touch && (y >= m_adjust_top) && (y < m_adjust_bottom))
+ {
+ if ((x >= m_decrease_left) && (x < (m_decrease_left + lr_arrow_width())))
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ return std::make_pair(IPT_UI_LEFT, false);
+ }
+ else if ((x >= m_increase_left) && (x < (m_increase_left + lr_arrow_width())))
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ return std::make_pair(IPT_UI_RIGHT, false);
+ }
+ }
+
+ // work out if we’re pointing at an item
+ if ((x < m_items_left) || (x >= m_items_right) || (y < m_items_top) || (y >= (m_items_top + (float(m_visible_lines) * line_height()))))
+ {
+ m_pointer_state = track_pointer::IGNORED;
+ return std::make_pair(IPT_INVALID, false);
+ }
+ auto const lineno(int((y - m_items_top) / line_height()));
+ assert(lineno >= 0);
+ assert(lineno < m_visible_lines);
+
+ // map to an action
+ if (!lineno && m_show_up_arrow)
+ {
+ // scroll up
+ assert(0 < top_line);
+ --top_line;
+ force_visible_selection();
+ if (top_line)
+ {
+ m_pointer_state = track_pointer::TRACK_LINE;
+ m_pointer_down = std::make_pair(x, y);
+ m_pointer_updated = m_pointer_down;
+ m_pointer_line = lineno;
+ m_pointer_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ return std::make_pair(IPT_INVALID, true);
+ }
+ else if ((lineno == (m_visible_lines - 1)) && m_show_down_arrow)
+ {
+ // scroll down
+ assert(m_items.size() > (top_line + m_visible_lines));
+ ++top_line;
+ force_visible_selection();
+ if (m_items.size() > (top_line + m_visible_lines))
+ {
+ m_pointer_state = track_pointer::TRACK_LINE;
+ m_pointer_down = std::make_pair(x, y);
+ m_pointer_updated = m_pointer_down;
+ m_pointer_line = lineno;
+ m_pointer_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ return std::make_pair(IPT_INVALID, true);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::TRACK_LINE;
+ m_pointer_down = std::make_pair(x, y);
+ m_pointer_updated = m_pointer_down;
+ m_pointer_line = lineno;
+
+ int const itemno(lineno + top_line);
+ assert(itemno >= 0);
+ assert(itemno < m_items.size());
+ return std::make_pair(IPT_INVALID, is_selectable(m_items[itemno]));
+ }
+
+ // nothing to do
+ m_pointer_state = track_pointer::IGNORED;
+ return std::make_pair(IPT_INVALID, false);
+}
+
+
+//-------------------------------------------------
+// update_line_click - track pointer after
+// clicking a menu line
+//-------------------------------------------------
+
+std::pair<int, bool> menu::update_line_click(ui_event const &uievt)
+{
+ assert(track_pointer::TRACK_LINE == m_pointer_state);
+ assert((uievt.pointer_buttons | uievt.pointer_released) & 0x01);
+
+ // arrows should scroll while held
+ if ((!m_pointer_line && m_show_up_arrow) || ((m_pointer_line == (m_visible_lines - 1)) && m_show_down_arrow))
+ {
+ // check for re-entry
+ bool redraw(false);
+ auto const [x, y] = pointer_location();
+ float const linetop(m_items_top + (float(m_pointer_line) * line_height()));
+ float const linebottom(m_items_top + (float(m_pointer_line + 1) * line_height()));
+ bool const reentered(reentered_rect(m_pointer_updated.first, m_pointer_updated.second, x, y, m_items_left, linetop, m_items_right, linebottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (now >= m_pointer_repeat)
+ {
+ if (!m_pointer_line)
+ {
+ // scroll up
+ assert(0 < top_line);
+ --top_line;
+ if (!top_line)
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ else
+ {
+ // scroll down
+ assert(m_items.size() > (top_line + m_visible_lines));
+ ++top_line;
+ if (m_items.size() == (top_line + m_visible_lines))
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ force_visible_selection();
+ redraw = true;
+ m_pointer_repeat = now + std::chrono::milliseconds(100);
+ }
+ }
+
+ // keep the pointer location where we updated
+ m_pointer_updated = std::make_pair(x, y);
+ return std::make_pair(IPT_INVALID, redraw);
+ }
+
+ // check for conversion of a tap to a finger drag
+ auto const drag_result(check_touch_drag(uievt));
+ if (track_pointer::TRACK_LINE != m_pointer_state)
+ return drag_result;
+
+ // only take action if the primary button was released
+ if (!(uievt.pointer_released & 0x01))
+ return std::make_pair(IPT_INVALID, false);
+
+ // nothing to do if the item isn't selectable
+ int const itemno = m_pointer_line + top_line;
+ assert(itemno >= 0);
+ assert(itemno < m_items.size());
+ if (!is_selectable(m_items[itemno]))
+ return std::make_pair(IPT_INVALID, false);
+
+ // treat multi-click actions as not moving, otherwise check that pointer is still over the line
+ if (0 >= uievt.pointer_clicks)
+ {
+ auto const [x, y] = pointer_location();
+ if ((x < m_items_left) || (x >= m_items_right) || (int((y - m_items_top) / line_height()) != m_pointer_line))
+ return std::make_pair(IPT_INVALID, true);
+ }
+
+ // anything other than a double-click just selects the item
+ m_selected = itemno;
+ if (2 != uievt.pointer_clicks)
+ return std::make_pair(IPT_INVALID, true);
+
+ // activate regular items by simulating UI Select
+ if (!is_last_selected() || !m_needs_prev_menu_item)
+ return std::make_pair(IPT_UI_SELECT, true);
+
+ // handle the magic final item that dismisses the menu
+ stack_pop();
+ if (is_special_main_menu())
+ machine().schedule_exit();
+ return std::make_pair(IPT_UI_BACK, true);
+}
+
+
+//-------------------------------------------------
+// update_drag_scroll - update menu position in
+// response to a touch drag
+//-------------------------------------------------
+
+bool menu::update_drag_scroll(ui_event const &uievt)
+{
+ assert(track_pointer::SCROLL == m_pointer_state);
+ assert((uievt.pointer_buttons | uievt.pointer_released) & 0x01);
+
+ // get target location
+ int const newtop(drag_scroll(
+ pointer_location().second, m_pointer_down.second, m_pointer_updated.second, -line_height(),
+ m_pointer_line, 0, int(m_items.size() - m_visible_lines)));
+ if (newtop == top_line)
+ return false;
+
+ // scroll and move the selection if necessary to keep it in the visible range
+ top_line = newtop;
+ force_visible_selection();
+ return true;
+}
+
+
+//-------------------------------------------------
+// update_drag_adjust - adjust value on
+// horizontal drag
+//-------------------------------------------------
+
+std::pair<int, bool> menu::update_drag_adjust(ui_event const &uievt)
+{
+ assert(track_pointer::ADJUST == m_pointer_state);
+ assert((uievt.pointer_buttons | uievt.pointer_released) & 0x01);
+
+ // this is ugly because adjustment is implemented by faking keystrokes - can't give a count/distance
+
+ // set thresholds depending on the direction for hysteresis
+ int const target(drag_scroll(
+ pointer_location().first, m_pointer_updated.first, m_pointer_updated.first, line_height() * x_aspect(),
+ 0, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
+
+ // ensure the item under the pointer is selected and adjustable
+ if ((top_line + m_pointer_line) == m_selected)
+ {
+ if (0 < target)
+ {
+ if (m_items[m_selected].flags() & FLAG_RIGHT_ARROW)
+ return std::make_pair(IPT_UI_RIGHT, true);
+ }
+ else if (0 > target)
+ {
+ if (m_items[m_selected].flags() & FLAG_LEFT_ARROW)
+ return std::make_pair(IPT_UI_LEFT, true);
+ }
+ }
+
+ // looks like it wasn't to be
+ return std::make_pair(IPT_INVALID, false);
+}
+
+
+//-------------------------------------------------
+// check_touch_drag - check for conversion of a
+// touch to a scroll or adjust slide
+//-------------------------------------------------
+
+std::pair<int, bool> menu::check_touch_drag(ui_event const &uievt)
+{
+ // we handle touch differently to mouse or pen
+ if (ui_event::pointer::TOUCH != uievt.pointer_type)
+ return std::make_pair(IPT_INVALID, false);
+
+ // check distances
+ auto const [x, y] = pointer_location();
+ auto const [h, v] = check_drag_conversion(x, y, m_pointer_down.first, m_pointer_down.second, line_height());
+ if (h)
+ {
+ // only the selected line can be adjusted
+ if ((top_line + m_pointer_line) == m_selected)
+ {
+ m_pointer_state = track_pointer::ADJUST;
+ return update_drag_adjust(uievt);
+ }
+ else
+ {
+ m_pointer_state = track_pointer::COMPLETED;
+ }
+ }
+ else if (v)
+ {
+ m_pointer_state = track_pointer::SCROLL;
+ m_pointer_line = top_line;
+ return std::make_pair(IPT_INVALID, update_drag_scroll(uievt));
+ }
+
+ // no update needed
+ return std::make_pair(IPT_INVALID, false);
}
@@ -989,51 +1638,52 @@ 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))
{
if (is_last_selected() && m_needs_prev_menu_item)
{
- iptkey = IPT_UI_CANCEL;
+ iptkey = IPT_INVALID;
stack_pop();
if (is_special_main_menu())
machine().schedule_exit();
}
- return;
+ return false;
}
// UI configure hides the menus
- if (!(flags & PROCESS_NOKEYS) && exclusive_input_pressed(iptkey, IPT_UI_CONFIGURE, 0) && !m_global_state.stack_has_special_main_menu())
+ if (!(flags & PROCESS_NOKEYS) && exclusive_input_pressed(iptkey, IPT_UI_MENU, 0) && !m_global_state.stack_has_special_main_menu())
{
if (is_one_shot())
stack_pop();
else
m_global_state.hide_menu();
- return;
+ return true;
}
// bail out
if (flags & PROCESS_ONLYCHAR)
- return;
+ return false;
- // hitting cancel also pops the stack
- if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
+ // hitting back also pops the stack
+ if (exclusive_input_pressed(iptkey, IPT_UI_BACK, 0))
{
- if (!custom_ui_cancel())
+ if (!custom_ui_back())
{
+ iptkey = IPT_INVALID;
stack_pop();
if (is_special_main_menu())
machine().schedule_exit();
}
- return;
+ return false;
}
// validate the current selection
@@ -1045,16 +1695,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())
{
@@ -1068,6 +1721,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
@@ -1075,7 +1729,7 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
{
if (flags & PROCESS_CUSTOM_NAV)
{
- return;
+ return updated;
}
else if (is_last_selected())
{
@@ -1089,47 +1743,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
@@ -1141,10 +1800,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)
{
@@ -1169,6 +1824,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()
+{
}
@@ -1216,34 +1904,182 @@ 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) && (aspect == m_last_aspect))
+ return false;
+
+ m_last_size = uisize;
+ m_last_aspect = aspect;
+ recompute_metrics(uisize.first, uisize.second, aspect);
+ return true;
+}
+
+
+//-------------------------------------------------
+// do_rebuild - get the subclass to populate
+// the menu items
+//-------------------------------------------------
+
+bool menu::do_rebuild()
+{
+ if (!m_items.empty())
+ return false;
+
+ m_rebuilding = true;
+ try
+ {
+ // add an item to return - this is a really hacky way of doing this
+ if (m_needs_prev_menu_item)
+ item_append(_("Return to Previous Menu"), 0, nullptr);
+
+ // let implementation add other items
+ populate();
+ }
+ catch (...)
+ {
+ m_items.clear();
+ m_rebuilding = false;
+ throw;
+ }
+ m_rebuilding = false;
+ return true;
+}
+
+
+//-------------------------------------------------
+// force_visible_selection - if the selected item
+// is not visible, move the selection it it's
+// within the visible portion of the menu
+//-------------------------------------------------
+
+void menu::force_visible_selection()
+{
+ int const first(top_line ? (top_line + 1) : 0);
+ int const last(top_line + m_visible_lines - ((m_items.size() > (top_line + m_visible_lines)) ? 1 : 0));
+ if (first > m_selected)
+ {
+ m_selected = first;
+ while (!is_selectable(m_items[m_selected]))
+ ++m_selected;
+ assert(last > m_selected);
+ }
+ else if (last <= m_selected)
+ {
+ m_selected = last - 1;
+ while (!is_selectable(m_items[m_selected]))
+ --m_selected;
+ assert(first <= m_selected);
+ }
+}
+
+
/***************************************************************************
MENU STACK MANAGEMENT
***************************************************************************/
-void menu::do_handle()
+bool menu::do_handle()
{
- if (m_items.empty())
+ bool need_update = false;
+
+ // let OSD do its thing
+ machine().osd().check_osd_inputs();
+
+ // recompute metrics if necessary
+ if (check_metrics())
+ need_update = true;
+
+ // get the implementation to rebuild the list of items if necessary
+ if (do_rebuild())
+ need_update = true;
+ validate_selection(1);
+
+ // 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)))
{
- 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);
+ // read events
+ if (handle_events(flags, *result))
+ need_update = true;
- // let implementation add other items
- populate(m_customtop, m_custombottom);
- }
- 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;
}
- handle(process());
+
+ // 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;
}
@@ -1267,6 +2103,16 @@ delegate<uint32_t (render_container &)> menu::get_ui_handler(mame_ui_manager &mu
***************************************************************************/
//-------------------------------------------------
+// create_layout
+//-------------------------------------------------
+
+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() * x_aspect(), line_height(), width, justify, wrap);
+}
+
+
+//-------------------------------------------------
// highlight
//-------------------------------------------------
@@ -1294,7 +2140,7 @@ void menu::draw_arrow(float x0, float y0, float x1, float y1, rgb_t fgcolor, uin
void menu::extra_text_draw_box(float origx1, float origx2, float origy, float yspan, std::string_view text, int direction)
{
// get the size of the text
- auto layout = ui().create_layout(container());
+ auto layout = create_layout();
layout.add_text(text);
// position this extra text
@@ -1305,8 +2151,8 @@ void menu::extra_text_draw_box(float origx1, float origx2, float origy, float ys
ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
// take off the borders
- x1 += ui().box_lr_border() * machine().render().ui_aspect(&container());
- y1 += ui().box_tb_border();
+ x1 += lr_border();
+ y1 += tb_border();
// draw the text within it
layout.emit(container(), x1, y1);
@@ -1329,14 +2175,14 @@ void menu::draw_background()
void menu::extra_text_position(float origx1, float origx2, float origy, float yspan, text_layout &layout,
int direction, float &x1, float &y1, float &x2, float &y2)
{
- float width = layout.actual_width() + (2 * ui().box_lr_border() * machine().render().ui_aspect(&container()));
+ float width = layout.actual_width() + (2 * lr_border());
float maxwidth = std::max(width, origx2 - origx1);
// compute our bounds
x1 = 0.5F - (0.5F * maxwidth);
x2 = x1 + maxwidth;
y1 = origy + (yspan * direction);
- y2 = origy + (ui().box_tb_border() * direction);
+ y2 = origy + (tb_border() * direction);
if (y1 > y2)
std::swap(y1, y2);
diff --git a/src/frontend/mame/ui/menu.h b/src/frontend/mame/ui/menu.h
index ca1f3b7384d..14031fb2c08 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 {
/***************************************************************************
@@ -62,13 +75,16 @@ public:
}
// append a new item to the end of the menu
- void item_append(const std::string &text, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { item_append(std::string(text), std::string(), flags, ref, type); }
- void item_append(const std::string &text, const std::string &subtext, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { item_append(std::string(text), std::string(subtext), flags, ref, type); }
- void item_append(std::string &&text, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { item_append(text, std::string(), flags, ref, type); }
- void item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN);
- void item_append(menu_item item) { item_append(item.text(), item.subtext(), item.flags(), item.ref(), item.type()); }
- void item_append(menu_item_type type, uint32_t flags = 0);
- void item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN);
+ int item_append(const std::string &text, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { return item_append(std::string(text), std::string(), flags, ref, type); }
+ int item_append(const std::string &text, const std::string &subtext, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { return item_append(std::string(text), std::string(subtext), flags, ref, type); }
+ int item_append(std::string &&text, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN) { return item_append(text, std::string(), flags, ref, type); }
+ int item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN);
+ int item_append(menu_item item) { return item_append(item.text(), item.subtext(), item.flags(), item.ref(), item.type()); }
+ int item_append(menu_item_type type, uint32_t flags = 0);
+ int item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type = menu_item_type::UNKNOWN);
+
+ // set space required for drawing extra content
+ void set_custom_space(float top, float bottom);
// reset the menus, clearing everything
static void stack_reset(mame_ui_manager &ui) { get_global_state(ui).stack_reset(); }
@@ -99,7 +115,7 @@ public:
// Used by sliders
void validate_selection(int scandir);
- void do_handle();
+ bool do_handle();
protected:
using bitmap_ptr = widgets_manager::bitmap_ptr;
@@ -129,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);
@@ -178,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); }
@@ -193,6 +204,65 @@ 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; }
+ float lr_border() const { return m_lr_border; }
+ float lr_arrow_width() const { return m_lr_arrow_width; }
+ float ud_arrow_width() const { return m_ud_arrow_width; }
+
+ float get_string_width(std::string_view s) { return ui().get_string_width(s, line_height()); }
+ text_layout create_layout(float width = 1.0, text_layout::text_justify justify = text_layout::text_justify::LEFT, text_layout::word_wrapping wrap = text_layout::word_wrapping::WORD);
+
+ void draw_text_normal(
+ std::string_view text,
+ float x, float y, float width,
+ text_layout::text_justify justify, text_layout::word_wrapping wrap,
+ rgb_t color)
+ {
+ ui().draw_text_full(
+ container(),
+ text,
+ x, y, width, justify, wrap,
+ mame_ui_manager::NORMAL, color, ui().colors().text_bg_color(),
+ nullptr, nullptr,
+ line_height());
+ }
+
+ float get_text_width(
+ std::string_view text,
+ float x, float y, float width,
+ text_layout::text_justify justify, text_layout::word_wrapping wrap)
+ {
+ float result;
+ ui().draw_text_full(
+ container(),
+ text,
+ x, y, width, justify, wrap,
+ mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
+ &result, nullptr,
+ line_height());
+ return result;
+ }
+
+ std::pair<float, float> get_text_dimensions(
+ std::string_view text,
+ float x, float y, float width,
+ text_layout::text_justify justify, text_layout::word_wrapping wrap)
+ {
+ std::pair<float, float> result;
+ ui().draw_text_full(
+ container(),
+ text,
+ x, y, width, justify, wrap,
+ mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
+ &result.first, &result.second,
+ line_height());
+ return result;
+ }
+
// highlight
void highlight(float x0, float y0, float x1, float y1, rgb_t bgcolor);
render_texture *hilight_main_texture() { return m_global_state.hilight_main_texture(); }
@@ -214,16 +284,15 @@ protected:
rgb_t fgcolor, rgb_t bgcolor, float text_size)
{
// size up the text
- float const lrborder(ui().box_lr_border() * machine().render().ui_aspect(&container()));
- float const origwidth(origx2 - origx1 - (2.0f * lrborder));
+ float const origwidth(origx2 - origx1 - (2.0f * lr_border()));
float maxwidth(origwidth);
for (Iter it = begin; it != end; ++it)
{
std::string_view const &line(*it);
if (!line.empty())
{
- auto layout = ui().create_layout(container(), 1.0f, justify, wrap);
- layout.add_text(std::string_view(*it), rgb_t::white(), rgb_t::black(), text_size);
+ 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);
}
}
@@ -234,13 +303,13 @@ protected:
}
// draw containing box
- float const boxleft(0.5f - (maxwidth * 0.5f) - lrborder);
- float boxright(0.5f + (maxwidth * 0.5f) + lrborder);
+ float const boxleft(0.5f - (maxwidth * 0.5f) - lr_border());
+ float boxright(0.5f + (maxwidth * 0.5f) + lr_border());
ui().draw_outlined_box(container(), boxleft, y1, boxright, y2, bgcolor);
// inset box and draw content
float const textleft(0.5f - (maxwidth * 0.5f));
- y1 += ui().box_tb_border();
+ y1 += tb_border();
for (Iter it = begin; it != end; ++it)
{
ui().draw_text_full(
@@ -248,46 +317,53 @@ protected:
textleft, y1, maxwidth, justify, wrap,
mame_ui_manager::NORMAL, fgcolor, ui().colors().text_bg_color(),
nullptr, nullptr, text_size);
- y1 += ui().get_line_height();
+ y1 += text_size;
}
// in case you want another box of similar width
return maxwidth;
}
+ template <typename Iter>
+ float draw_text_box(
+ Iter begin, Iter end,
+ float origx1, float origx2, float y1, float y2,
+ ui::text_layout::text_justify justify, ui::text_layout::word_wrapping wrap, bool scale,
+ rgb_t fgcolor, rgb_t bgcolor)
+ {
+ return draw_text_box(begin, end, origx1, origx2, y1, y2, justify, wrap, scale, fgcolor, bgcolor, line_height());
+ }
+
void draw_background();
// draw additional menu content
- 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 recompute_metrics(uint32_t width, uint32_t height, float aspect);
+ virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2);
- // clear the mouse position
- void ignore_mouse();
+ // access to pointer state
+ bool have_pointer() const noexcept { return m_global_state.have_pointer(); }
+ bool pointer_idle() const noexcept { return (track_pointer::IDLE == m_pointer_state) && have_pointer(); }
+ bool pointer_in_rect(float x0, float y0, float x1, float y1) const noexcept { return m_global_state.pointer_in_rect(x0, y0, x1, y1); }
+ std::pair<float, float> pointer_location() const noexcept { return m_global_state.pointer_location(); }
+ osd::ui_event_handler::pointer pointer_type() const noexcept { return m_global_state.pointer_type(); }
- bool is_mouse_hit() const { return m_mouse_hit; } // is mouse pointer inside menu's render container?
- float get_mouse_x() const { return m_mouse_x; } // mouse x location in menu coordinates
- float get_mouse_y() const { return m_mouse_y; } // mouse y location in menu coordinates
-
- // mouse hit test - checks whether mouse_x is in [x0, x1) and mouse_y is in [y0, y1)
- bool mouse_in_rect(float x0, float y0, float x1, float y1) const
- {
- return m_mouse_hit && (m_mouse_x >= x0) && (m_mouse_x < x1) && (m_mouse_y >= y0) && (m_mouse_y < y1);
- }
+ // derived classes that override handle_events need to call these for pointer events
+ std::pair<int, bool> handle_pointer_update(uint32_t flags, ui_event const &uievt);
+ std::pair<int, bool> handle_pointer_leave(uint32_t flags, ui_event const &uievt);
+ std::pair<int, bool> handle_pointer_abort(uint32_t flags, ui_event const &uievt);
// overridable event handling
void set_process_flags(uint32_t flags) { m_process_flags = flags; }
- virtual void handle_events(uint32_t flags, event &ev);
- virtual void handle_keys(uint32_t flags, int &iptkey);
- virtual bool custom_ui_cancel() { 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)
{
@@ -301,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:
@@ -326,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
@@ -347,23 +489,34 @@ private:
global_state_wrapper(global_state_wrapper const &that) : global_state(that.m_ui) { }
};
- // process a menu, drawing it and returning any interesting events
- const event *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
void set_special_main_menu(bool disable);
// to be implemented in derived classes
- virtual void populate(float &customtop, float &custombottom) = 0;
+ virtual void populate() = 0;
// to be implemented in derived classes
- virtual void handle(event const *ev) = 0;
+ virtual bool handle(event const *ev) = 0;
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)); }
@@ -385,26 +538,45 @@ private:
std::vector<menu_item> m_items; // array of items
bool m_rebuilding; // ensure items are only added during rebuild
+ std::pair<uint32_t, uint32_t> m_last_size; // pixel size of UI container when metrics were computed
+ float m_last_aspect; // aspect ratio of UI container when metrics were computed
+ float m_line_height;
+ float m_gutter_width;
+ float m_tb_border;
+ float m_lr_border;
+ 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/miscmenu.cpp b/src/frontend/mame/ui/miscmenu.cpp
index 55a2aab986f..df5754a380e 100644
--- a/src/frontend/mame/ui/miscmenu.cpp
+++ b/src/frontend/mame/ui/miscmenu.cpp
@@ -23,17 +23,19 @@
#include "osdnet.h"
#include "mameopts.h"
#include "pluginopts.h"
+#include "dinetwork.h"
#include "drivenum.h"
#include "fileio.h"
#include "romload.h"
#include "uiinput.h"
-#include "corestr.h"
+#include "path.h"
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iterator>
+#include <locale>
namespace ui {
@@ -52,7 +54,7 @@ menu_bios_selection::menu_bios_selection(mame_ui_manager &mui, render_container
set_heading(_("BIOS Selection"));
}
-void menu_bios_selection::populate(float &customtop, float &custombottom)
+void menu_bios_selection::populate()
{
// cycle through all devices for this system
for (device_t &device : device_enumerator(machine().root_device()))
@@ -88,57 +90,64 @@ menu_bios_selection::~menu_bios_selection()
menu_bios_selection - menu that
-------------------------------------------------*/
-void menu_bios_selection::handle(event const *ev)
+bool menu_bios_selection::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
- {
- if ((uintptr_t)ev->itemref == 1 && ev->iptkey == IPT_UI_SELECT)
- machine().schedule_hard_reset();
- else
- {
- device_t *dev = (device_t *)ev->itemref;
- int bios_val = 0;
-
- switch (ev->iptkey)
- {
- // reset to default
- case IPT_UI_SELECT:
- bios_val = dev->default_bios();
- break;
+ if (!ev || !ev->itemref)
+ return false;
- // previous/next bios setting
- case IPT_UI_LEFT: case IPT_UI_RIGHT:
- {
- int const cnt = ([bioses = romload::entries(dev->rom_region()).get_system_bioses()] () { return std::distance(bioses.begin(), bioses.end()); })();
- bios_val = dev->system_bios() + ((ev->iptkey == IPT_UI_LEFT) ? -1 : +1);
+ if ((uintptr_t)ev->itemref == 1 && ev->iptkey == IPT_UI_SELECT)
+ {
+ machine().schedule_hard_reset();
+ return false;
+ }
- // wrap
- if (bios_val < 1)
- bios_val = cnt;
- if (bios_val > cnt)
- bios_val = 1;
+ device_t *const dev = (device_t *)ev->itemref;
+ int bios_val = 0;
- break;
- }
+ switch (ev->iptkey)
+ {
+ // reset to default
+ case IPT_UI_CLEAR:
+ bios_val = dev->default_bios();
+ break;
+
+ // previous/next BIOS setting
+ case IPT_UI_SELECT:
+ case IPT_UI_LEFT:
+ case IPT_UI_RIGHT:
+ {
+ int const cnt = ([bioses = romload::entries(dev->rom_region()).get_system_bioses()] () { return std::distance(bioses.begin(), bioses.end()); })();
+ bios_val = dev->system_bios() + ((ev->iptkey == IPT_UI_LEFT) ? -1 : +1);
+
+ // wrap
+ if (bios_val < 1)
+ bios_val = cnt;
+ if (bios_val > cnt)
+ bios_val = 1;
+ }
+ break;
- default:
- break;
- }
+ default:
+ break;
+ }
- if (bios_val > 0)
- {
- dev->set_system_bios(bios_val);
- if (strcmp(dev->tag(),":")==0) {
- machine().options().set_value("bios", bios_val-1, OPTION_PRIORITY_CMDLINE);
- } else {
- const char *slot_option_name = dev->owner()->tag() + 1;
- machine().options().slot_option(slot_option_name).set_bios(string_format("%d", bios_val - 1));
- }
- reset(reset_options::REMEMBER_REF);
- }
+ if (bios_val > 0)
+ {
+ dev->set_system_bios(bios_val);
+ if (!strcmp(dev->tag(), ":"))
+ {
+ machine().options().set_value("bios", bios_val - 1, OPTION_PRIORITY_CMDLINE);
}
+ else
+ {
+ const char *slot_option_name = dev->owner()->tag() + 1;
+ machine().options().slot_option(slot_option_name).set_bios(string_format("%d", bios_val - 1));
+ }
+ reset(reset_options::REMEMBER_REF);
}
+
+ // triggers an item reset for any change
+ return false;
}
@@ -157,22 +166,23 @@ menu_network_devices::~menu_network_devices()
network device menu
-------------------------------------------------*/
-void menu_network_devices::populate(float &customtop, float &custombottom)
+void menu_network_devices::populate()
{
/* cycle through all devices for this system */
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())
+ for (auto &entry : get_netdev_list())
{
- if(entry->id==curr) {
+ if (entry->id == curr)
+ {
title = entry->description;
break;
}
}
- item_append(network.device().tag(), (title) ? title : "------", FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&network);
+ item_append(network.device().tag(), title ? title : "------", FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&network);
}
item_append(menu_item_type::SEPARATOR);
@@ -182,24 +192,43 @@ void menu_network_devices::populate(float &customtop, float &custombottom)
menu_network_devices - menu that
-------------------------------------------------*/
-void menu_network_devices::handle(event const *ev)
+bool menu_network_devices::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ {
+ return false;
+ }
+ else if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
{
- if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
+ // FIXME: this conflates presumably arbitrary interface ID numbers with 0-based indices
+ device_network_interface *const network = (device_network_interface *)ev->itemref;
+ auto const &interfaces = get_netdev_list();
+ int curr = network->get_interface();
+ if (ev->iptkey == IPT_UI_LEFT)
+ curr--;
+ else
+ curr++;
+ if (curr == -2)
+ curr = interfaces.size() - 1;
+ network->set_interface(curr);
+
+ curr = network->get_interface();
+ const char *title = nullptr;
+ for (auto &entry : interfaces)
{
- device_network_interface *network = (device_network_interface *)ev->itemref;
- int curr = network->get_interface();
- if (ev->iptkey == IPT_UI_LEFT)
- curr--;
- else
- curr++;
- if (curr == -2)
- curr = netdev_count() - 1;
- network->set_interface(curr);
- reset(reset_options::REMEMBER_REF);
+ if (entry->id == curr)
+ {
+ title = entry->description;
+ break;
+ }
}
+
+ ev->item->set_subtext(title ? title : "------");
+ return true;
+ }
+ else
+ {
+ return false;
}
}
@@ -229,7 +258,7 @@ void menu_bookkeeping::populate_text(std::optional<text_layout> &layout, float &
if (!layout || (layout->width() != width))
{
rgb_t const color = ui().colors().text_color();
- layout.emplace(ui().create_layout(container(), width));
+ layout.emplace(create_layout(width));
// show total time first
prevtime = machine().time();
@@ -266,20 +295,24 @@ void menu_bookkeeping::populate_text(std::optional<text_layout> &layout, float &
width = layout->actual_width();
}
-void menu_bookkeeping::populate(float &customtop, float &custombottom)
+void menu_bookkeeping::populate()
{
}
-void menu_bookkeeping::handle(event const *ev)
+bool menu_bookkeeping::handle(event const *ev)
{
// if the time has rolled over another second, regenerate
// TODO: what about other bookkeeping events happening with the menu open?
attotime const curtime = machine().time();
if (curtime.seconds() != prevtime.seconds())
+ {
reset_layout();
-
- if (ev)
- handle_key(ev->iptkey);
+ return true;
+ }
+ else
+ {
+ return menu_textbox::handle(ev);
+ }
}
@@ -288,7 +321,7 @@ void menu_bookkeeping::handle(event const *ev)
menu
-------------------------------------------------*/
-void menu_crosshair::handle(event const *ev)
+bool menu_crosshair::handle(event const *ev)
{
// handle events
if (ev && ev->itemref)
@@ -381,6 +414,9 @@ void menu_crosshair::handle(event const *ev)
if (changed)
reset(reset_options::REMEMBER_REF); // rebuild the menu
}
+
+ // triggers an item reset for any changes
+ return false;
}
@@ -395,7 +431,7 @@ menu_crosshair::menu_crosshair(mame_ui_manager &mui, render_container &container
set_heading(_("menu-crosshair", "Crosshair Options"));
}
-void menu_crosshair::populate(float &customtop, float &custombottom)
+void menu_crosshair::populate()
{
if (m_data.empty())
{
@@ -445,10 +481,18 @@ void menu_crosshair::populate(float &customtop, float &custombottom)
if ((length > 4) && core_filename_ends_with(dir->name, ".png"))
m_pics.emplace_back(dir->name, length - 4);
}
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
std::stable_sort(
m_pics.begin(),
m_pics.end(),
- [] (std::string const &a, std::string const &b) { return 0 > core_stricmp(a.c_str(), b.c_str()); });
+ [&coll] (auto const &x, auto const &y)
+ {
+ 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());
+ }
+ );
}
// Make sure to keep these matched to the CROSSHAIR_VISIBILITY_xxx types
@@ -592,7 +636,7 @@ menu_export::~menu_export()
// handle the export menu
//-------------------------------------------------
-void menu_export::handle(event const *ev)
+bool menu_export::handle(event const *ev)
{
// process the menu
if (ev && ev->itemref)
@@ -631,7 +675,7 @@ void menu_export::handle(event const *ev)
auto iter = std::find_if(
driver_list.begin(),
driver_list.end(),
- [shortname](const game_driver *driver) { return !strcmp(shortname, driver->name); });
+ [shortname] (const game_driver *driver) { return !strcmp(shortname, driver->name); });
return iter != driver_list.end();
};
@@ -686,13 +730,15 @@ void menu_export::handle(event const *ev)
break;
}
}
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_export::populate(float &customtop, float &custombottom)
+void menu_export::populate()
{
// add options items
item_append(_("Export list in XML format (like -listxml)"), 0, (void *)(uintptr_t)1);
@@ -743,7 +789,7 @@ menu_machine_configure::~menu_machine_configure()
// handle the machine options menu
//-------------------------------------------------
-void menu_machine_configure::handle(event const *ev)
+bool menu_machine_configure::handle(event const *ev)
{
// process the menu
if (ev && ev->itemref)
@@ -796,13 +842,16 @@ void menu_machine_configure::handle(event const *ev)
reset(reset_options::REMEMBER_POSITION);
}
}
+
+ // triggers an item reset for any changes
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_machine_configure::populate(float &customtop, float &custombottom)
+void menu_machine_configure::populate()
{
// add options items
item_append(_("BIOS"), FLAG_DISABLE | FLAG_UI_HEADING, nullptr);
@@ -896,43 +945,43 @@ menu_plugins_configure::~menu_plugins_configure()
// handle the plugins menu
//-------------------------------------------------
-void menu_plugins_configure::handle(event const *ev)
+bool menu_plugins_configure::handle(event const *ev)
{
- // process the menu
- bool changed = false;
- plugin_options &plugins = mame_machine_manager::instance()->plugins();
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT || ev->iptkey == IPT_UI_SELECT)
{
- if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT || ev->iptkey == IPT_UI_SELECT)
+ plugin_options &plugins = mame_machine_manager::instance()->plugins();
+ plugin_options::plugin *p = plugins.find((const char*)ev->itemref);
+ if (p)
{
- plugin_options::plugin *p = plugins.find((const char*)ev->itemref);
- if (p)
- {
- p->m_start = !p->m_start;
- changed = true;
- }
+ p->m_start = !p->m_start;
+ ev->item->set_subtext(p->m_start ? _("On") : _("Off"));
+ ev->item->set_flags(p->m_start ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW);
+ return true;
}
}
- if (changed)
- reset(reset_options::REMEMBER_REF);
+
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_plugins_configure::populate(float &customtop, float &custombottom)
+void menu_plugins_configure::populate()
{
- plugin_options const &plugins = mame_machine_manager::instance()->plugins();
+ plugin_options const &plugin_opts = mame_machine_manager::instance()->plugins();
bool first(true);
- for (auto const &curentry : plugins.plugins())
+ for (const plugin_options::plugin &p : plugin_opts.plugins())
{
- if ("library" != curentry.m_type)
+ if (p.m_type != "library")
{
first = false;
- bool const enabled = curentry.m_start;
- item_append_on_off(curentry.m_description, enabled, 0, (void *)(uintptr_t)curentry.m_name.c_str());
+ bool const enabled = p.m_start;
+ item_append_on_off(p.m_description, enabled, 0, (void *)(uintptr_t)p.m_name.c_str());
}
}
if (first)
diff --git a/src/frontend/mame/ui/miscmenu.h b/src/frontend/mame/ui/miscmenu.h
index 22a2d1dc135..6d365ddba67 100644
--- a/src/frontend/mame/ui/miscmenu.h
+++ b/src/frontend/mame/ui/miscmenu.h
@@ -33,8 +33,8 @@ public:
virtual ~menu_network_devices();
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
class menu_bookkeeping : public menu_textbox
@@ -48,8 +48,8 @@ protected:
virtual void populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
attotime prevtime;
};
@@ -81,8 +81,8 @@ private:
std::string next_name;
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
std::vector<crosshair_item_data> m_data;
std::vector<std::string> m_pics;
@@ -96,8 +96,8 @@ public:
virtual ~menu_bios_selection();
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
@@ -112,8 +112,8 @@ public:
virtual ~menu_export();
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
std::vector<const game_driver*> m_list;
};
@@ -148,8 +148,8 @@ private:
LAST = ADVANCED
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
void setup_bios();
@@ -174,8 +174,8 @@ public:
virtual ~menu_plugins_configure();
protected:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/moptions.cpp b/src/frontend/mame/ui/moptions.cpp
index ff08ab3f9e6..d6eba821947 100644
--- a/src/frontend/mame/ui/moptions.cpp
+++ b/src/frontend/mame/ui/moptions.cpp
@@ -20,66 +20,74 @@
const options_entry ui_options::s_option_entries[] =
{
// search path options
- { nullptr, nullptr, core_options::option_type::HEADER, "UI SEARCH PATH OPTIONS" },
- { OPTION_HISTORY_PATH, "history;dats;.", core_options::option_type::STRING, "path to system/software info files" },
- { OPTION_CATEGORYINI_PATH, "folders", core_options::option_type::STRING, "path to category ini files" },
- { OPTION_CABINETS_PATH, "cabinets;cabdevs", core_options::option_type::STRING, "path to cabinets / devices image" },
- { OPTION_CPANELS_PATH, "cpanel", core_options::option_type::STRING, "path to control panel image" },
- { OPTION_PCBS_PATH, "pcb", core_options::option_type::STRING, "path to pcbs image" },
- { OPTION_FLYERS_PATH, "flyers", core_options::option_type::STRING, "path to flyers image" },
- { OPTION_TITLES_PATH, "titles", core_options::option_type::STRING, "path to titles image" },
- { OPTION_ENDS_PATH, "ends", core_options::option_type::STRING, "path to ends image" },
- { OPTION_MARQUEES_PATH, "marquees", core_options::option_type::STRING, "path to marquees image" },
- { OPTION_ARTPREV_PATH, "artwork preview;artpreview", core_options::option_type::STRING, "path to artwork preview image" },
- { OPTION_BOSSES_PATH, "bosses", core_options::option_type::STRING, "path to bosses image" },
- { OPTION_LOGOS_PATH, "logo", core_options::option_type::STRING, "path to logos image" },
- { OPTION_SCORES_PATH, "scores", core_options::option_type::STRING, "path to scores image" },
- { OPTION_VERSUS_PATH, "versus", core_options::option_type::STRING, "path to versus image" },
- { OPTION_GAMEOVER_PATH, "gameover", core_options::option_type::STRING, "path to gameover image" },
- { OPTION_HOWTO_PATH, "howto", core_options::option_type::STRING, "path to howto image" },
- { OPTION_SELECT_PATH, "select", core_options::option_type::STRING, "path to select image" },
- { OPTION_ICONS_PATH, "icons", core_options::option_type::STRING, "path to ICOns image" },
- { OPTION_COVER_PATH, "covers", core_options::option_type::STRING, "path to software cover image" },
- { OPTION_UI_PATH, "ui", core_options::option_type::STRING, "path to UI files" },
+ { nullptr, nullptr, option_type::HEADER, "UI SEARCH PATH OPTIONS" },
+ { OPTION_HISTORY_PATH, "history;dats;.", option_type::MULTIPATH, "path to system/software info files" },
+ { OPTION_CATEGORYINI_PATH, "folders", option_type::MULTIPATH, "path to category ini files" },
+ { OPTION_CABINETS_PATH, "cabinets;cabdevs", option_type::MULTIPATH, "path to cabinets / devices image" },
+ { OPTION_CPANELS_PATH, "cpanel", option_type::MULTIPATH, "path to control panel image" },
+ { OPTION_PCBS_PATH, "pcb", option_type::MULTIPATH, "path to pcbs image" },
+ { OPTION_FLYERS_PATH, "flyers", option_type::MULTIPATH, "path to flyers image" },
+ { OPTION_TITLES_PATH, "titles", option_type::MULTIPATH, "path to titles image" },
+ { OPTION_ENDS_PATH, "ends", option_type::MULTIPATH, "path to ends image" },
+ { OPTION_MARQUEES_PATH, "marquees", option_type::MULTIPATH, "path to marquees image" },
+ { OPTION_ARTPREV_PATH, "artwork preview;artpreview", option_type::MULTIPATH, "path to artwork preview image" },
+ { OPTION_BOSSES_PATH, "bosses", option_type::MULTIPATH, "path to bosses image" },
+ { OPTION_LOGOS_PATH, "logo", option_type::MULTIPATH, "path to logos image" },
+ { OPTION_SCORES_PATH, "scores", option_type::MULTIPATH, "path to scores image" },
+ { OPTION_VERSUS_PATH, "versus", option_type::MULTIPATH, "path to versus image" },
+ { OPTION_GAMEOVER_PATH, "gameover", option_type::MULTIPATH, "path to gameover image" },
+ { OPTION_HOWTO_PATH, "howto", option_type::MULTIPATH, "path to howto image" },
+ { OPTION_SELECT_PATH, "select", option_type::MULTIPATH, "path to select image" },
+ { OPTION_ICONS_PATH, "icons", option_type::MULTIPATH, "path to ICOns image" },
+ { OPTION_COVER_PATH, "covers", option_type::MULTIPATH, "path to software cover image" },
+ { OPTION_UI_PATH, "ui", option_type::MULTIPATH, "path to UI files" },
// misc options
- { nullptr, nullptr, core_options::option_type::HEADER, "UI MISC OPTIONS" },
- { OPTION_SYSTEM_NAMES, "", core_options::option_type::STRING, "translated system names file" },
- { OPTION_SKIP_WARNINGS, "0", core_options::option_type::BOOLEAN, "display fewer repeated warnings about imperfect emulation" },
- { OPTION_REMEMBER_LAST, "1", core_options::option_type::BOOLEAN, "initially select last used system in main menu" },
- { OPTION_ENLARGE_SNAPS, "1", core_options::option_type::BOOLEAN, "enlarge artwork (snapshot, title, etc.) in right panel (keeping aspect ratio)" },
- { OPTION_FORCED4X3, "1", core_options::option_type::BOOLEAN, "force the appearance of the snapshot in the list software to 4:3" },
- { OPTION_USE_BACKGROUND, "1", core_options::option_type::BOOLEAN, "enable background image in main view" },
- { OPTION_SKIP_BIOS_MENU, "0", core_options::option_type::BOOLEAN, "skip bios submenu, start with configured or default" },
- { OPTION_SKIP_PARTS_MENU, "0", core_options::option_type::BOOLEAN, "skip parts submenu, start with first part" },
- { OPTION_LAST_USED_FILTER, "", core_options::option_type::STRING, "latest used filter" },
- { OPTION_LAST_RIGHT_PANEL "(0-1)", "0", core_options::option_type::INTEGER, "latest right panel focus" },
- { OPTION_LAST_USED_MACHINE, "", core_options::option_type::STRING, "latest used machine" },
- { OPTION_INFO_AUTO_AUDIT, "0", core_options::option_type::BOOLEAN, "enable auto audit in the general info panel" },
- { OPTION_HIDE_ROMLESS, "1", core_options::option_type::BOOLEAN, "hide romless machine from available list" },
- { OPTION_UNTHROTTLE_MUTE ";utm", "0", core_options::option_type::BOOLEAN, "mute audio when running unthrottled" },
+ { nullptr, nullptr, option_type::HEADER, "UI MISC OPTIONS" },
+ { OPTION_SYSTEM_NAMES, "", option_type::MULTIPATH, "translated system names file" },
+ { OPTION_SKIP_WARNINGS, "0", option_type::BOOLEAN, "display fewer repeated warnings about imperfect emulation" },
+ { OPTION_UNTHROTTLE_MUTE ";utm", "0", option_type::BOOLEAN, "mute audio when running unthrottled" },
// UI options
- { nullptr, nullptr, core_options::option_type::HEADER, "UI OPTIONS" },
- { OPTION_INFOS_SIZE "(0.20-1.00)", "0.75", core_options::option_type::FLOAT, "UI right panel infos text size (0.20 - 1.00)" },
- { OPTION_FONT_ROWS "(25-40)", "30", core_options::option_type::INTEGER, "UI font lines per screen (25 - 40)" },
- { OPTION_HIDE_PANELS "(0-3)", "0", core_options::option_type::INTEGER, "UI hide left/right panel in main view (0 = Show all, 1 = hide left, 2 = hide right, 3 = hide both" },
- { OPTION_UI_BORDER_COLOR, "ffffffff", core_options::option_type::STRING, "UI border color (ARGB)" },
- { OPTION_UI_BACKGROUND_COLOR, "ef101030", core_options::option_type::STRING, "UI background color (ARGB)" },
- { OPTION_UI_CLONE_COLOR, "ff808080", core_options::option_type::STRING, "UI clone color (ARGB)" },
- { OPTION_UI_DIPSW_COLOR, "ffffff00", core_options::option_type::STRING, "UI dipswitch color (ARGB)" },
- { OPTION_UI_GFXVIEWER_BG_COLOR, "ef101030", core_options::option_type::STRING, "UI gfx viewer color (ARGB)" },
- { OPTION_UI_MOUSEDOWN_BG_COLOR, "b0606000", core_options::option_type::STRING, "UI mouse down bg color (ARGB)" },
- { OPTION_UI_MOUSEDOWN_COLOR, "ffffff80", core_options::option_type::STRING, "UI mouse down color (ARGB)" },
- { OPTION_UI_MOUSEOVER_BG_COLOR, "70404000", core_options::option_type::STRING, "UI mouse over bg color (ARGB)" },
- { OPTION_UI_MOUSEOVER_COLOR, "ffffff80", core_options::option_type::STRING, "UI mouse over color (ARGB)" },
- { OPTION_UI_SELECTED_BG_COLOR, "ef808000", core_options::option_type::STRING, "UI selected bg color (ARGB)" },
- { OPTION_UI_SELECTED_COLOR, "ffffff00", core_options::option_type::STRING, "UI selected color (ARGB)" },
- { OPTION_UI_SLIDER_COLOR, "ffffffff", core_options::option_type::STRING, "UI slider color (ARGB)" },
- { OPTION_UI_SUBITEM_COLOR, "ffffffff", core_options::option_type::STRING, "UI subitem color (ARGB)" },
- { OPTION_UI_TEXT_BG_COLOR, "ef000000", core_options::option_type::STRING, "UI text bg color (ARGB)" },
- { OPTION_UI_TEXT_COLOR, "ffffffff", core_options::option_type::STRING, "UI text color (ARGB)" },
- { OPTION_UI_UNAVAILABLE_COLOR, "ff404040", core_options::option_type::STRING, "UI unavailable color (ARGB)" },
+ { nullptr, nullptr, option_type::HEADER, "UI OPTIONS" },
+ { OPTION_INFOS_SIZE "(0.20-1.00)", "0.75", option_type::FLOAT, "UI right panel infos text size (0.20 - 1.00)" },
+ { OPTION_FONT_ROWS "(25-40)", "30", option_type::INTEGER, "UI font lines per screen (25 - 40)" },
+ { OPTION_UI_BORDER_COLOR, "ffffffff", option_type::STRING, "UI border color (ARGB)" },
+ { OPTION_UI_BACKGROUND_COLOR, "ef101030", option_type::STRING, "UI background color (ARGB)" },
+ { OPTION_UI_CLONE_COLOR, "ff808080", option_type::STRING, "UI clone color (ARGB)" },
+ { OPTION_UI_DIPSW_COLOR, "ffffff00", option_type::STRING, "UI dipswitch color (ARGB)" },
+ { OPTION_UI_GFXVIEWER_BG_COLOR, "ef101030", option_type::STRING, "UI gfx viewer color (ARGB)" },
+ { OPTION_UI_MOUSEDOWN_BG_COLOR, "b0606000", option_type::STRING, "UI mouse down bg color (ARGB)" },
+ { OPTION_UI_MOUSEDOWN_COLOR, "ffffff80", option_type::STRING, "UI mouse down color (ARGB)" },
+ { OPTION_UI_MOUSEOVER_BG_COLOR, "70404000", option_type::STRING, "UI mouse over bg color (ARGB)" },
+ { OPTION_UI_MOUSEOVER_COLOR, "ffffff80", option_type::STRING, "UI mouse over color (ARGB)" },
+ { OPTION_UI_SELECTED_BG_COLOR, "ef808000", option_type::STRING, "UI selected bg color (ARGB)" },
+ { OPTION_UI_SELECTED_COLOR, "ffffff00", option_type::STRING, "UI selected color (ARGB)" },
+ { OPTION_UI_SLIDER_COLOR, "ffffffff", option_type::STRING, "UI slider color (ARGB)" },
+ { OPTION_UI_SUBITEM_COLOR, "ffffffff", option_type::STRING, "UI subitem color (ARGB)" },
+ { OPTION_UI_TEXT_BG_COLOR, "ef000000", option_type::STRING, "UI text bg color (ARGB)" },
+ { OPTION_UI_TEXT_COLOR, "ffffffff", option_type::STRING, "UI text color (ARGB)" },
+ { OPTION_UI_UNAVAILABLE_COLOR, "ff404040", option_type::STRING, "UI unavailable color (ARGB)" },
+
+ // system/software selection menu options
+ { nullptr, nullptr, option_type::HEADER, "SYSTEM/SOFTWARE SELECTION MENU OPTIONS" },
+ { OPTION_HIDE_PANELS "(0-3)", "0", option_type::INTEGER, "UI hide left/right panel in main view (0 = Show all, 1 = hide left, 2 = hide right, 3 = hide both" },
+ { OPTION_USE_BACKGROUND, "1", option_type::BOOLEAN, "enable background image in main view" },
+ { OPTION_SKIP_BIOS_MENU, "0", option_type::BOOLEAN, "skip bios submenu, start with configured or default" },
+ { OPTION_SKIP_PARTS_MENU, "0", option_type::BOOLEAN, "skip parts submenu, start with first part" },
+ { OPTION_REMEMBER_LAST, "1", option_type::BOOLEAN, "initially select last used system in main menu" },
+ { OPTION_LAST_USED_MACHINE, "", option_type::STRING, "last selected system" },
+ { OPTION_LAST_USED_FILTER, "", option_type::STRING, "last used system filter" },
+ { OPTION_SYSTEM_RIGHT_PANEL, "image", option_type::STRING, "selected system right panel tab" },
+ { OPTION_SOFTWARE_RIGHT_PANEL, "image", option_type::STRING, "selected software right panel tab" },
+ { OPTION_SYSTEM_RIGHT_IMAGE, "snap", option_type::STRING, "selected system right panel image" },
+ { OPTION_SOFTWARE_RIGHT_IMAGE, "snap", option_type::STRING, "selected software right panel image" },
+ { OPTION_ENLARGE_SNAPS, "1", option_type::BOOLEAN, "enlarge images in right panel (keeping aspect ratio)" },
+ { OPTION_FORCED4X3, "1", option_type::BOOLEAN, "force 4:3 aspect ratio for snapshots in the software menu" },
+ { OPTION_INFO_AUTO_AUDIT, "0", option_type::BOOLEAN, "automatically audit media for the general info panel" },
+ { OPTION_HIDE_ROMLESS, "1", option_type::BOOLEAN, "hide systems that don't require ROMs in the available system filter" },
+
+ // sentinel
{ nullptr }
};
diff --git a/src/frontend/mame/ui/moptions.h b/src/frontend/mame/ui/moptions.h
index 4b228aad974..ab854870f32 100644
--- a/src/frontend/mame/ui/moptions.h
+++ b/src/frontend/mame/ui/moptions.h
@@ -40,25 +40,12 @@
// core misc options
#define OPTION_SYSTEM_NAMES "system_names"
#define OPTION_SKIP_WARNINGS "skip_warnings"
-#define OPTION_REMEMBER_LAST "remember_last"
-#define OPTION_ENLARGE_SNAPS "enlarge_snaps"
-#define OPTION_FORCED4X3 "forced4x3"
-#define OPTION_USE_BACKGROUND "use_background"
-#define OPTION_SKIP_BIOS_MENU "skip_biosmenu"
-#define OPTION_SKIP_PARTS_MENU "skip_partsmenu"
-#define OPTION_LAST_USED_FILTER "last_used_filter"
-#define OPTION_LAST_RIGHT_PANEL "last_right_panel"
-#define OPTION_LAST_USED_MACHINE "last_used_machine"
-#define OPTION_INFO_AUTO_AUDIT "info_audit_enabled"
-#define OPTION_HIDE_ROMLESS "hide_romless"
#define OPTION_UNTHROTTLE_MUTE "unthrottle_mute"
// core UI options
#define OPTION_INFOS_SIZE "infos_text_size"
#define OPTION_FONT_ROWS "font_rows"
-#define OPTION_HIDE_PANELS "hide_main_panel"
-
#define OPTION_UI_BORDER_COLOR "ui_border_color"
#define OPTION_UI_BACKGROUND_COLOR "ui_bg_color"
#define OPTION_UI_GFXVIEWER_BG_COLOR "ui_gfxviewer_color"
@@ -76,6 +63,24 @@
#define OPTION_UI_DIPSW_COLOR "ui_dipsw_color"
#define OPTION_UI_SLIDER_COLOR "ui_slider_color"
+// system/software selection menu options
+#define OPTION_HIDE_PANELS "hide_main_panel"
+#define OPTION_USE_BACKGROUND "use_background"
+#define OPTION_SKIP_BIOS_MENU "skip_biosmenu"
+#define OPTION_SKIP_PARTS_MENU "skip_partsmenu"
+#define OPTION_REMEMBER_LAST "remember_last"
+#define OPTION_LAST_USED_MACHINE "last_used_machine"
+#define OPTION_LAST_USED_FILTER "last_used_filter"
+#define OPTION_SYSTEM_RIGHT_PANEL "system_right_panel"
+#define OPTION_SOFTWARE_RIGHT_PANEL "software_right_panel"
+#define OPTION_SYSTEM_RIGHT_IMAGE "system_right_image"
+#define OPTION_SOFTWARE_RIGHT_IMAGE "software_right_image"
+#define OPTION_ENLARGE_SNAPS "enlarge_snaps"
+#define OPTION_FORCED4X3 "forced4x3"
+#define OPTION_INFO_AUTO_AUDIT "info_audit_enabled"
+#define OPTION_HIDE_ROMLESS "hide_romless"
+
+
class ui_options : public core_options
{
public:
@@ -107,23 +112,13 @@ public:
// Misc options
const char *system_names() const { return value(OPTION_SYSTEM_NAMES); }
bool skip_warnings() const { return bool_value(OPTION_SKIP_WARNINGS); }
- bool remember_last() const { return bool_value(OPTION_REMEMBER_LAST); }
bool enlarge_snaps() const { return bool_value(OPTION_ENLARGE_SNAPS); }
bool forced_4x3_snapshot() const { return bool_value(OPTION_FORCED4X3); }
- bool use_background_image() const { return bool_value(OPTION_USE_BACKGROUND); }
- bool skip_bios_menu() const { return bool_value(OPTION_SKIP_BIOS_MENU); }
- bool skip_parts_menu() const { return bool_value(OPTION_SKIP_PARTS_MENU); }
- const char *last_used_machine() const { return value(OPTION_LAST_USED_MACHINE); }
- const char *last_used_filter() const { return value(OPTION_LAST_USED_FILTER); }
- int last_right_panel() const { return int_value(OPTION_LAST_RIGHT_PANEL); }
- bool info_audit() const { return bool_value(OPTION_INFO_AUTO_AUDIT); }
- bool hide_romless() const { return bool_value(OPTION_HIDE_ROMLESS); }
bool unthrottle_mute() const { return bool_value(OPTION_UNTHROTTLE_MUTE); }
// UI options
float infos_size() const { return float_value(OPTION_INFOS_SIZE); }
int font_rows() const { return int_value(OPTION_FONT_ROWS); }
- int hide_panels() const { return int_value(OPTION_HIDE_PANELS); }
rgb_t border_color() const { return rgb_value(OPTION_UI_BORDER_COLOR); }
rgb_t background_color() const { return rgb_value(OPTION_UI_BACKGROUND_COLOR); }
@@ -142,6 +137,21 @@ public:
rgb_t dipsw_color() const { return rgb_value(OPTION_UI_DIPSW_COLOR); }
rgb_t slider_color() const { return rgb_value(OPTION_UI_SLIDER_COLOR); }
+ // system/software selection menu options
+ int hide_panels() const { return int_value(OPTION_HIDE_PANELS); }
+ bool use_background_image() const { return bool_value(OPTION_USE_BACKGROUND); }
+ bool skip_bios_menu() const { return bool_value(OPTION_SKIP_BIOS_MENU); }
+ bool skip_parts_menu() const { return bool_value(OPTION_SKIP_PARTS_MENU); }
+ bool remember_last() const { return bool_value(OPTION_REMEMBER_LAST); }
+ const char *last_used_machine() const { return value(OPTION_LAST_USED_MACHINE); }
+ const char *last_used_filter() const { return value(OPTION_LAST_USED_FILTER); }
+ char const *system_right_panel() const { return value(OPTION_SYSTEM_RIGHT_PANEL); }
+ char const *software_right_panel() const { return value(OPTION_SOFTWARE_RIGHT_PANEL); }
+ char const *system_right_image() const { return value(OPTION_SYSTEM_RIGHT_IMAGE); }
+ char const *software_right_image() const { return value(OPTION_SOFTWARE_RIGHT_IMAGE); }
+ bool info_audit() const { return bool_value(OPTION_INFO_AUTO_AUDIT); }
+ bool hide_romless() const { return bool_value(OPTION_HIDE_ROMLESS); }
+
rgb_t rgb_value(const char *option) const;
private:
diff --git a/src/frontend/mame/ui/optsmenu.cpp b/src/frontend/mame/ui/optsmenu.cpp
index 225e2ef3af4..07178b2cae4 100644
--- a/src/frontend/mame/ui/optsmenu.cpp
+++ b/src/frontend/mame/ui/optsmenu.cpp
@@ -60,18 +60,16 @@ menu_simple_game_options::~menu_simple_game_options()
// handle
//-------------------------------------------------
-void menu_simple_game_options::handle(event const *ev)
+bool menu_simple_game_options::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
- handle_item_event(*ev);
+ return ev && ev->itemref && handle_item_event(*ev);
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_simple_game_options::populate(float &customtop, float &custombottom)
+void menu_simple_game_options::populate()
{
item_append(_(submenu::video_options()[0].description), 0, (void *)(uintptr_t)DISPLAY_MENU);
item_append(_("Sound Options"), 0, (void *)(uintptr_t)SOUND_MENU);
@@ -85,15 +83,13 @@ void menu_simple_game_options::populate(float &customtop, float &custombottom)
item_append(_("Input Devices"), 0, (void *)(uintptr_t)INPUTDEV_MENU);
item_append(menu_item_type::SEPARATOR);
item_append(_("Save Settings"), 0, (void *)(uintptr_t)SAVE_CONFIG);
-
- custombottom = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
}
//-------------------------------------------------
// handle item
//-------------------------------------------------
-void menu_simple_game_options::handle_item_event(event const &menu_event)
+bool menu_simple_game_options::handle_item_event(event const &menu_event)
{
if (IPT_UI_SELECT == menu_event.iptkey)
{
@@ -132,6 +128,7 @@ void menu_simple_game_options::handle_item_event(event const &menu_event)
break;
}
}
+ return false;
}
@@ -163,18 +160,16 @@ menu_game_options::~menu_game_options()
// handle
//-------------------------------------------------
-void menu_game_options::handle(event const *ev)
+bool menu_game_options::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
- handle_item_event(*ev);
+ return ev && ev->itemref && handle_item_event(*ev);
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_game_options::populate(float &customtop, float &custombottom)
+void menu_game_options::populate()
{
// set filter arrow
std::string fbuff;
@@ -198,14 +193,14 @@ void menu_game_options::populate(float &customtop, float &custombottom)
item_append(_("Configure Folders"), 0, (void *)(uintptr_t)CONF_DIR);
// add the options that don't relate to the UI
- menu_simple_game_options::populate(customtop, custombottom);
+ menu_simple_game_options::populate();
}
//-------------------------------------------------
// handle item
//-------------------------------------------------
-void menu_game_options::handle_item_event(event const &menu_event)
+bool menu_game_options::handle_item_event(event const &menu_event)
{
bool changed = false;
@@ -270,12 +265,14 @@ void menu_game_options::handle_item_event(event const &menu_event)
menu::stack_push<menu_custom_ui>(ui(), container(), [this] () { reset(reset_options::REMEMBER_REF); });
break;
default:
- menu_simple_game_options::handle_item_event(menu_event);
- return;
+ return menu_simple_game_options::handle_item_event(menu_event);
}
if (changed)
reset(reset_options::REMEMBER_REF);
+
+ // triggers an item reset for any changes
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/optsmenu.h b/src/frontend/mame/ui/optsmenu.h
index b10d6601926..b300ed90cd5 100644
--- a/src/frontend/mame/ui/optsmenu.h
+++ b/src/frontend/mame/ui/optsmenu.h
@@ -28,10 +28,10 @@ public:
virtual ~menu_simple_game_options() override;
protected:
- virtual void handle(event const *ev) override;
- virtual void populate(float &customtop, float &custombottom) override;
+ virtual bool handle(event const *ev) override;
+ virtual void populate() override;
- void handle_item_event(event const &menu_event);
+ bool handle_item_event(event const &menu_event);
private:
enum
@@ -62,10 +62,10 @@ public:
virtual ~menu_game_options() override;
protected:
- virtual void handle(event const *ev) override;
- virtual void populate(float &customtop, float &custombottom) override;
+ virtual bool handle(event const *ev) override;
+ virtual void populate() override;
- void handle_item_event(event const &menu_event);
+ bool handle_item_event(event const &menu_event);
private:
enum
diff --git a/src/frontend/mame/ui/pluginopt.cpp b/src/frontend/mame/ui/pluginopt.cpp
index 08f7f84a752..809047357fc 100644
--- a/src/frontend/mame/ui/pluginopt.cpp
+++ b/src/frontend/mame/ui/pluginopt.cpp
@@ -19,13 +19,14 @@
namespace ui {
-void menu_plugin::handle(event const *ev)
+bool menu_plugin::handle(event const *ev)
{
if (ev && ev->itemref)
{
if (ev->iptkey == IPT_UI_SELECT)
- menu::stack_push<menu_plugin_opt>(ui(), container(), (char *)ev->itemref);
+ menu::stack_push<menu_plugin_opt>(ui(), container(), (char *)ev->itemref, false);
}
+ return false;
}
menu_plugin::menu_plugin(mame_ui_manager &mui, render_container &container) :
@@ -35,40 +36,35 @@ menu_plugin::menu_plugin(mame_ui_manager &mui, render_container &container) :
set_heading(_("Plugin Options"));
}
-void menu_plugin::populate(float &customtop, float &custombottom)
+void menu_plugin::populate()
{
for (auto &curplugin : m_plugins)
item_append(curplugin, 0, (void *)curplugin.c_str());
item_append(menu_item_type::SEPARATOR);
}
-void menu_plugin::show_menu(mame_ui_manager &mui, render_container &container, char *menu)
+void menu_plugin::show_menu(mame_ui_manager &mui, render_container &container, std::string_view menu)
{
- // reset the menu stack
- menu::stack_reset(mui);
-
// add the plugin menu entry
- menu::stack_push<menu_plugin_opt>(mui, container, menu);
+ menu::stack_push<menu_plugin_opt>(mui, container, menu, true);
// force the menus on
mui.show_menu();
-
- // make sure MAME is paused
- mui.machine().pause();
}
menu_plugin::~menu_plugin()
{
}
-menu_plugin_opt::menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu) :
+menu_plugin_opt::menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu, bool one_shot) :
ui::menu(mui, container),
m_menu(menu),
m_need_idle(false)
{
+ set_one_shot(one_shot);
}
-void menu_plugin_opt::handle(event const *ev)
+bool menu_plugin_opt::handle(event const *ev)
{
void *const itemref = ev ? ev->itemref : get_selection_ref();
std::string key;
@@ -103,6 +99,9 @@ void menu_plugin_opt::handle(event const *ev)
case IPT_UI_CLEAR:
key = "clear";
break;
+ case IPT_UI_BACK:
+ key = "back";
+ break;
case IPT_UI_CANCEL:
key = "cancel";
break;
@@ -113,19 +112,22 @@ void menu_plugin_opt::handle(event const *ev)
break;
}
}
- if (!key.empty() || m_need_idle)
- {
- auto const result = mame_machine_manager::instance()->lua()->menu_callback(m_menu, uintptr_t(itemref), key);
- if (result.second)
- set_selection(reinterpret_cast<void *>(uintptr_t(*result.second)));
- if (result.first)
- reset(reset_options::REMEMBER_REF);
- else if (ev && (ev->iptkey == IPT_UI_CANCEL))
- stack_pop();
- }
+
+ if (key.empty() && !m_need_idle)
+ return false;
+
+ auto const result = mame_machine_manager::instance()->lua()->menu_callback(m_menu, uintptr_t(itemref), key);
+ if (result.second)
+ set_selection(reinterpret_cast<void *>(uintptr_t(*result.second)));
+ if (result.first)
+ reset(reset_options::REMEMBER_REF);
+ else if (ev && (ev->iptkey == IPT_UI_BACK))
+ stack_pop();
+
+ return result.second && !result.first;
}
-void menu_plugin_opt::populate(float &customtop, float &custombottom)
+void menu_plugin_opt::populate()
{
std::vector<std::tuple<std::string, std::string, std::string>> menu_list;
std::string flags;
diff --git a/src/frontend/mame/ui/pluginopt.h b/src/frontend/mame/ui/pluginopt.h
index 15196c1ca2d..3ac87acd709 100644
--- a/src/frontend/mame/ui/pluginopt.h
+++ b/src/frontend/mame/ui/pluginopt.h
@@ -27,29 +27,30 @@ class menu_plugin : public menu
public:
menu_plugin(mame_ui_manager &mui, render_container &container);
- static void show_menu(mame_ui_manager &mui, render_container &container, char *menu);
+ static void show_menu(mame_ui_manager &mui, render_container &container, std::string_view menu);
virtual ~menu_plugin();
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
- std::vector<std::string> &m_plugins;
+ std::vector<std::string> const &m_plugins;
};
+
class menu_plugin_opt : public menu
{
public:
- menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu);
+ menu_plugin_opt(mame_ui_manager &mui, render_container &container, std::string_view menu, bool one_shot);
virtual ~menu_plugin_opt();
protected:
- virtual bool custom_ui_cancel() override { return true; }
+ virtual bool custom_ui_back() override { return true; }
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
std::string const m_menu;
bool m_need_idle;
diff --git a/src/frontend/mame/ui/prscntrl.cpp b/src/frontend/mame/ui/prscntrl.cpp
new file mode 100644
index 00000000000..aab071cfc0e
--- /dev/null
+++ b/src/frontend/mame/ui/prscntrl.cpp
@@ -0,0 +1,69 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ ui/prscntrl.cpp
+
+ MAME's clunky built-in preset selection in a fixed list
+
+***************************************************************************/
+
+#include "emu.h"
+
+#include "ui/prscntrl.h"
+#include "ui/ui.h"
+
+namespace ui {
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+menu_control_device_preset::menu_control_device_preset(mame_ui_manager &mui, render_container &container, device_image_interface &image)
+ : menu(mui, container)
+ , m_image(image)
+{
+}
+
+
+//-------------------------------------------------
+// dtor
+//-------------------------------------------------
+
+menu_control_device_preset::~menu_control_device_preset()
+{
+}
+
+
+//-------------------------------------------------
+// populate
+//-------------------------------------------------
+
+void menu_control_device_preset::populate()
+{
+ auto presets = m_image.preset_images_list();
+ for(int i = 0; i != int(presets.size()); i++)
+ item_append(presets[i], 0, reinterpret_cast<void *>(i));
+ set_selection(reinterpret_cast<void *>(m_image.current_preset_image_id()));
+}
+
+
+//-------------------------------------------------
+// handle
+//-------------------------------------------------
+
+bool menu_control_device_preset::handle(event const *ev)
+{
+ if (ev && (ev->iptkey == IPT_UI_SELECT)) {
+ int id = reinterpret_cast<uintptr_t>(ev->itemref);
+ m_image.switch_preset_image(id);
+ stack_pop();
+ }
+ return false;
+}
+
+} // namespace ui
diff --git a/src/frontend/mame/ui/prscntrl.h b/src/frontend/mame/ui/prscntrl.h
new file mode 100644
index 00000000000..a89d6712efc
--- /dev/null
+++ b/src/frontend/mame/ui/prscntrl.h
@@ -0,0 +1,45 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ ui/prscntrl.h
+
+ MAME's clunky built-in preset selection in a fixed list
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_PRESETCNTRL_H
+#define MAME_FRONTEND_UI_PRESETCNTRL_H
+
+#pragma once
+
+#include "ui/filesel.h"
+#include "ui/menu.h"
+#include "ui/swlist.h"
+
+
+namespace ui {
+
+// ======================> menu_control_device_preset
+
+class menu_control_device_preset : public menu
+{
+public:
+ menu_control_device_preset(mame_ui_manager &mui, render_container &container, device_image_interface &image);
+ virtual ~menu_control_device_preset() override;
+
+protected:
+ // methods
+ virtual bool handle(event const *ev) override;
+
+private:
+ // instance variables
+ device_image_interface & m_image;
+
+ // methods
+ virtual void populate() override;
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_PRESETCNTRL_H
diff --git a/src/frontend/mame/ui/quitmenu.cpp b/src/frontend/mame/ui/quitmenu.cpp
index bb26f7e6290..2177313a0cd 100644
--- a/src/frontend/mame/ui/quitmenu.cpp
+++ b/src/frontend/mame/ui/quitmenu.cpp
@@ -20,7 +20,8 @@ menu_confirm_quit::menu_confirm_quit(mame_ui_manager &mui, render_container &con
: autopause_menu<>(mui, container)
{
set_one_shot(true);
- set_process_flags(PROCESS_CUSTOM_ONLY | PROCESS_NOINPUT);
+ set_needs_prev_menu_item(false);
+ set_heading(_("menu-quit", "Are you sure you want to quit?"));
}
@@ -29,33 +30,24 @@ menu_confirm_quit::~menu_confirm_quit()
}
-void menu_confirm_quit::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu_confirm_quit::populate()
{
- ui().draw_text_box(
- container(),
- util::string_format(
- _("Are you sure you want to quit?\n\n"
- "Press %1$s to quit\n"
- "Press %2$s to return to emulation"),
- ui().get_general_input_setting(IPT_UI_SELECT),
- ui().get_general_input_setting(IPT_UI_CANCEL)),
- text_layout::text_justify::CENTER,
- 0.5f, 0.5f,
- UI_RED_COLOR);
+ item_append(_("menu-quit", "Quit"), 0, nullptr);
+ item_append(_("menu-quit", "Return to emulation"), 0, nullptr);
}
-void menu_confirm_quit::populate(float &customtop, float &custombottom)
+bool menu_confirm_quit::handle(event const *ev)
{
-}
-
-
-void menu_confirm_quit::handle(event const *ev)
-{
- if (machine().ui_input().pressed(IPT_UI_SELECT))
- machine().schedule_exit();
- else if (machine().ui_input().pressed(IPT_UI_CANCEL))
- stack_pop();
+ if (ev && (IPT_UI_SELECT == ev->iptkey))
+ {
+ if (0 == selected_index())
+ machine().schedule_exit();
+ else
+ stack_pop();
+ }
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/quitmenu.h b/src/frontend/mame/ui/quitmenu.h
index 81ca61e2892..9f22801fdba 100644
--- a/src/frontend/mame/ui/quitmenu.h
+++ b/src/frontend/mame/ui/quitmenu.h
@@ -23,12 +23,9 @@ public:
menu_confirm_quit(mame_ui_manager &mui, render_container &container);
virtual ~menu_confirm_quit();
-protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
-
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/selector.cpp b/src/frontend/mame/ui/selector.cpp
index f8791a37887..53f68006c04 100644
--- a/src/frontend/mame/ui/selector.cpp
+++ b/src/frontend/mame/ui/selector.cpp
@@ -49,48 +49,54 @@ menu_selector::~menu_selector()
// handle
//-------------------------------------------------
-void menu_selector::handle(event const *ev)
+bool menu_selector::handle(event const *ev)
{
- // process the menu
- if (ev)
+ if (!ev)
+ return false;
+
+ switch (ev->iptkey)
{
- switch (ev->iptkey)
+ case IPT_UI_SELECT:
+ if (ev->itemref)
{
- case IPT_UI_SELECT:
- if (ev->itemref)
- {
- int selection(-1);
- for (size_t idx = 0; (m_str_items.size() > idx) && (0 > selection); ++idx)
- if ((void*)&m_str_items[idx] == ev->itemref)
- selection = int(unsigned(idx));
+ int selection(-1);
+ for (size_t idx = 0; (m_str_items.size() > idx) && (0 > selection); ++idx)
+ if ((void*)&m_str_items[idx] == ev->itemref)
+ selection = int(unsigned(idx));
- m_handler(selection);
- stack_pop();
- }
- break;
+ m_handler(selection);
+ stack_pop();
+ }
+ break;
- case IPT_SPECIAL:
- if (input_character(m_search, ev->unichar, uchar_is_printable))
- reset(reset_options::SELECT_FIRST);
- break;
+ case IPT_UI_PASTE:
+ if (paste_text(m_search, uchar_is_printable))
+ reset(reset_options::SELECT_FIRST);
+ break;
- case IPT_UI_CANCEL:
- if (!m_search.empty())
- {
- // escape pressed with non-empty search text clears the search text
- m_search.clear();
- reset(reset_options::SELECT_FIRST);
- }
- break;
+ case IPT_SPECIAL:
+ if (input_character(m_search, ev->unichar, uchar_is_printable))
+ reset(reset_options::SELECT_FIRST);
+ break;
+
+ case IPT_UI_CANCEL:
+ if (!m_search.empty())
+ {
+ // escape pressed with non-empty search text clears the search text
+ m_search.clear();
+ reset(reset_options::SELECT_FIRST);
}
+ break;
}
+
+ return false; // any changes will trigger an item reset
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_selector::populate(float &customtop, float &custombottom)
+void menu_selector::populate()
{
set_heading(util::string_format(_("menu-selector", "%1$s - Search: %2$s_"), m_title, m_search));
@@ -119,23 +125,33 @@ void menu_selector::populate(float &customtop, float &custombottom)
}
item_append(menu_item_type::SEPARATOR);
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
m_initial = -1;
}
//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_selector::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(0.0F, line_height() + 3.0F * tb_border());
+}
+
+//-------------------------------------------------
// 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)) };
draw_text_box(
std::begin(tempbuf), std::end(tempbuf),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + 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(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
}
//-------------------------------------------------
diff --git a/src/frontend/mame/ui/selector.h b/src/frontend/mame/ui/selector.h
index 17fdb972343..58b93c5185a 100644
--- a/src/frontend/mame/ui/selector.h
+++ b/src/frontend/mame/ui/selector.h
@@ -38,14 +38,15 @@ public:
virtual ~menu_selector() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool custom_ui_cancel() override { return !m_search.empty(); }
+ 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 bool custom_ui_back() override { return !m_search.empty(); }
private:
enum { VISIBLE_SEARCH_ITEMS = 200 };
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
void find_matches(const char *str);
diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp
index 07b8d03d043..e9cc85e9a43 100644
--- a/src/frontend/mame/ui/selgame.cpp
+++ b/src/frontend/mame/ui/selgame.cpp
@@ -78,7 +78,6 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta
{
//s_first_start = false; TODO: why wasn't it ever clearing the first start flag?
reselect_last::set_driver(moptions.last_used_machine());
- ui_globals::rpanel = std::min<int>(std::max<int>(moptions.last_right_panel(), RP_FIRST), RP_LAST);
std::string tmp(moptions.last_used_filter());
std::size_t const found = tmp.find_first_of(',');
@@ -108,8 +107,11 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta
mui.machine().options().set_value(OPTION_SNAPNAME, "%g/%i", OPTION_PRIORITY_CMDLINE);
+ // restore last right panel settings
+ set_right_panel(moptions.system_right_panel());
+ set_right_image(moptions.system_right_image());
+
ui_globals::curdats_view = 0;
- ui_globals::panels_status = moptions.hide_panels();
ui_globals::curdats_total = 1;
}
@@ -119,31 +121,34 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta
menu_select_game::~menu_select_game()
{
- std::string error_string, last_driver;
- ui_system_info const *system;
- ui_software_info const *swinfo;
- get_selection(swinfo, system);
- if (swinfo)
- last_driver = swinfo->shortname;
- else if (system)
- last_driver = system->driver->name;
+ // TODO: reconsider when to do this
+ ui().save_ui_options();
+}
- std::string const filter(m_persistent_data.filter_data().get_config_string());
- ui_options &mopt = ui().options();
- mopt.set_value(OPTION_LAST_RIGHT_PANEL, ui_globals::rpanel, OPTION_PRIORITY_CMDLINE);
- mopt.set_value(OPTION_LAST_USED_FILTER, filter, OPTION_PRIORITY_CMDLINE);
- mopt.set_value(OPTION_LAST_USED_MACHINE, last_driver, OPTION_PRIORITY_CMDLINE);
- mopt.set_value(OPTION_HIDE_PANELS, ui_globals::panels_status, OPTION_PRIORITY_CMDLINE);
- ui().save_ui_options();
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_select_game::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu_select_launch::recompute_metrics(width, height, aspect);
+
+ m_icons.clear();
+
+ // configure the custom rendering
+ set_custom_space(3.0F * line_height() + 5.0F * tb_border(), 4.0F * line_height() + 3.0F * tb_border());
}
+
//-------------------------------------------------
// menu_activated
//-------------------------------------------------
void menu_select_game::menu_activated()
{
+ menu_select_launch::menu_activated();
+
// if I have to load datfile, perform a hard reset
if (ui_globals::reset)
{
@@ -154,15 +159,42 @@ void menu_select_game::menu_activated()
ui_globals::reset = false;
machine().schedule_hard_reset();
stack_reset();
- return;
}
}
//-------------------------------------------------
+// menu_deactivated
+//-------------------------------------------------
+
+void menu_select_game::menu_deactivated()
+{
+ menu_select_launch::menu_deactivated();
+
+ // get the "last selected system" string
+ ui_system_info const *system;
+ ui_software_info const *swinfo;
+ get_selection(swinfo, system);
+ std::string last_driver;
+ if (swinfo)
+ last_driver = swinfo->shortname;
+ else if (system)
+ last_driver = system->driver->name;
+
+ // serialise the selected filter settings
+ std::string const filter(m_persistent_data.filter_data().get_config_string());
+
+ ui_options &mopt = ui().options();
+ mopt.set_value(OPTION_LAST_USED_MACHINE, last_driver, OPTION_PRIORITY_CMDLINE);
+ mopt.set_value(OPTION_LAST_USED_FILTER, filter, OPTION_PRIORITY_CMDLINE);
+ mopt.set_value(OPTION_SYSTEM_RIGHT_PANEL, right_panel_config_string(), OPTION_PRIORITY_CMDLINE);
+ mopt.set_value(OPTION_SYSTEM_RIGHT_IMAGE, right_image_config_string(), OPTION_PRIORITY_CMDLINE);
+}
+
+//-------------------------------------------------
// handle
//-------------------------------------------------
-void menu_select_game::handle(event const *ev)
+bool menu_select_game::handle(event const *ev)
{
if (!m_prev_selected && item_count() > 0)
m_prev_selected = item(0).ref();
@@ -175,40 +207,22 @@ void menu_select_game::handle(event const *ev)
const ui_software_info *software;
get_selection(software, system);
menu::stack_push<menu_select_software>(ui(), container(), *system);
- return;
+ return false;
}
// FIXME: everything above here used to run before events were processed
// process the menu
+ bool changed = false;
if (ev)
{
if (dismiss_error())
{
// reset the error on any subsequent menu event
+ changed = true;
}
else switch (ev->iptkey)
{
- case IPT_UI_UP:
- if ((get_focus() == focused_menu::LEFT) && (machine_filter::FIRST < m_filter_highlight))
- --m_filter_highlight;
- break;
-
- case IPT_UI_DOWN:
- if ((get_focus() == focused_menu::LEFT) && (machine_filter::LAST > m_filter_highlight))
- m_filter_highlight++;
- break;
-
- case IPT_UI_HOME:
- if (get_focus() == focused_menu::LEFT)
- m_filter_highlight = machine_filter::FIRST;
- break;
-
- case IPT_UI_END:
- if (get_focus() == focused_menu::LEFT)
- m_filter_highlight = machine_filter::LAST;
- break;
-
case IPT_UI_EXPORT:
inkey_export();
break;
@@ -226,66 +240,14 @@ void menu_select_game::handle(event const *ev)
if (get_focus() == focused_menu::MAIN)
{
if (m_populated_favorites)
- inkey_select_favorite(ev);
+ changed = inkey_select_favorite(ev);
else
- inkey_select(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 (ui_globals::rpanel == RP_IMAGES)
- {
- // Images
- previous_image_view();
- }
- else if (ui_globals::rpanel == RP_INFOS)
- {
- // Infos
- change_info_pane(-1);
- }
- break;
-
- case IPT_UI_RIGHT:
- if (ui_globals::rpanel == RP_IMAGES)
- {
- // Images
- next_image_view();
- }
- else if (ui_globals::rpanel == RP_INFOS)
- {
- // Infos
- change_info_pane(1);
+ changed = inkey_select(ev);
}
break;
case IPT_UI_FAVORITES:
- if (uintptr_t(ev->itemref) > skip_main_items)
+ if (uintptr_t(ev->itemref) > m_skip_main_items)
{
favorite_manager &mfav(mame_machine_manager::instance()->favorite());
if (!m_populated_favorites)
@@ -302,6 +264,7 @@ void menu_select_game::handle(event const *ev)
mfav.remove_favorite_system(driver);
machine().popmessage(_("%s\n removed from favorites list."), info.description);
}
+ changed = true;
}
else
{
@@ -321,15 +284,14 @@ void menu_select_game::handle(event const *ev)
}
}
- // if we're in an error state, overlay an error message
- draw_error_text();
+ return changed;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_select_game::populate(float &customtop, float &custombottom)
+void menu_select_game::populate()
{
for (auto &icon : m_icons) // TODO: why is this here? maybe better on resize or setting change?
icon.second.texture.reset();
@@ -422,7 +384,8 @@ void menu_select_game::populate(float &customtop, float &custombottom)
cloneof = false;
}
- item_append(info.longname, cloneof ? FLAG_INVERT : 0, (void *)&info);
+ ui_system_info const &sysinfo = m_persistent_data.systems()[driver_list::find(info.driver->name)];
+ item_append(sysinfo.description, cloneof ? FLAG_INVERT : 0, (void *)&info);
}
else
{
@@ -440,28 +403,21 @@ void menu_select_game::populate(float &customtop, float &custombottom)
item_append(menu_item_type::SEPARATOR, 0);
item_append(_("General Settings"), 0, (void *)(uintptr_t)CONF_OPTS);
item_append(_("System Settings"), 0, (void *)(uintptr_t)CONF_MACHINE);
- skip_main_items = 3;
+ m_skip_main_items = 3;
- if (m_prev_selected && !have_prev_selected)
+ if (m_prev_selected && !have_prev_selected && item_count() > 0)
m_prev_selected = item(0).ref();
}
else
{
- skip_main_items = 0;
+ m_skip_main_items = 0;
}
- // configure the custom rendering
- customtop = 3.0f * ui().get_line_height() + 5.0f * ui().box_tb_border();
- custombottom = 4.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
-
// reselect prior game launched, if any
if (old_item_selected != -1)
{
set_selected_index(old_item_selected);
- if (ui_globals::visible_main_lines == 0)
- top_line = (selected_index() != 0) ? selected_index() - 1 : 0;
- else
- top_line = selected_index() - (ui_globals::visible_main_lines / 2);
+ centre_selection();
if (reselect_last::software().empty())
reselect_last::reset();
@@ -502,7 +458,7 @@ void menu_select_game::build_available_list()
// now check and include NONE_NEEDED
if (!ui().options().hide_romless())
{
- // FIXME: can't use the convenience macros tiny ROM entries
+ // FIXME: can't use the convenience macros with tiny ROM entries
auto const is_required_rom =
[] (tiny_rom_entry const &rom) { return ROMENTRY_ISFILE(rom) && !ROM_ISOPTIONAL(rom) && !std::strchr(rom.hashdata, '!'); };
for (std::size_t x = 0; total > x; ++x)
@@ -592,7 +548,7 @@ void menu_select_game::force_game_select(mame_ui_manager &mui, render_container
// handle select key event
//-------------------------------------------------
-void menu_select_game::inkey_select(const event *menu_event)
+bool menu_select_game::inkey_select(const event *menu_event)
{
auto const system = reinterpret_cast<ui_system_info const *>(menu_event->itemref);
@@ -604,13 +560,14 @@ void menu_select_game::inkey_select(const event *menu_event)
container(),
m_persistent_data.filter_data(),
[this] () { reset(reset_options::SELECT_FIRST); });
+ return false;
}
else if (uintptr_t(system) == CONF_MACHINE)
{
// special case for configure machine
if (m_prev_selected)
menu::stack_push<menu_machine_configure>(ui(), container(), *reinterpret_cast<const ui_system_info *>(m_prev_selected));
- return;
+ return false;
}
else
{
@@ -624,7 +581,7 @@ void menu_select_game::inkey_select(const event *menu_event)
if (!swlistdev.get_info().empty())
{
menu::stack_push<menu_select_software>(ui(), container(), *system);
- return;
+ return false;
}
}
@@ -637,11 +594,13 @@ void menu_select_game::inkey_select(const event *menu_event)
{
if (!select_bios(*system->driver, false))
launch_system(*system->driver);
+ return false;
}
else
{
// otherwise, display an error
set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, summary));
+ return true;
}
}
}
@@ -650,7 +609,7 @@ void menu_select_game::inkey_select(const event *menu_event)
// handle select key event for favorites menu
//-------------------------------------------------
-void menu_select_game::inkey_select_favorite(const event *menu_event)
+bool menu_select_game::inkey_select_favorite(const event *menu_event)
{
ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref;
@@ -662,6 +621,7 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
container(),
m_persistent_data.filter_data(),
[this] () { reset(reset_options::SELECT_FIRST); });
+ return false;
}
else if ((uintptr_t)ui_swinfo == CONF_MACHINE)
{
@@ -680,9 +640,9 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
reset(empty ? reset_options::SELECT_FIRST : reset_options::REMEMBER_REF);
});
}
- return;
+ return false;
}
- else if (ui_swinfo->startempty == 1)
+ else if (ui_swinfo->startempty)
{
driver_enumerator enumerator(machine().options(), *ui_swinfo->driver);
enumerator.next();
@@ -694,7 +654,7 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
{
ui_system_info const &system(m_persistent_data.systems()[driver_list::find(ui_swinfo->driver->name)]);
menu::stack_push<menu_select_software>(ui(), container(), system);
- return;
+ return false;
}
}
@@ -710,11 +670,13 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
reselect_last::reselect(true);
launch_system(*ui_swinfo->driver);
}
+ return false;
}
else
{
// otherwise, display an error
set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, summary));
+ return true;
}
}
else
@@ -727,6 +689,7 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
if (!audit_passed(sysaudit))
{
set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, sysaudit));
+ return true;
}
else
{
@@ -741,11 +704,13 @@ void menu_select_game::inkey_select_favorite(const event *menu_event)
reselect_last::reselect(true);
if (!select_bios(*ui_swinfo, false) && !select_part(*swinfo, *ui_swinfo))
launch_system(drv.driver(), *ui_swinfo, ui_swinfo->part);
+ return false;
}
else
{
// otherwise, display an error
set_error(reset_options::REMEMBER_REF, make_software_audit_fail_text(auditor, swaudit));
+ return true;
}
}
}
@@ -762,41 +727,6 @@ bool menu_select_game::isfavorite() const
//-------------------------------------------------
-// change what's displayed in the info box
-//-------------------------------------------------
-
-void menu_select_game::change_info_pane(int delta)
-{
- auto const cap_delta = [this, &delta] (uint8_t &current, uint8_t &total)
- {
- 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;
- }
- };
- ui_system_info const *sys;
- ui_software_info const *soft;
- get_selection(soft, sys);
- if (!m_populated_favorites)
- {
- if (uintptr_t(sys) > skip_main_items)
- cap_delta(ui_globals::curdats_view, ui_globals::curdats_total);
- }
- else if (uintptr_t(soft) > skip_main_items)
- {
- if (soft->startempty)
- cap_delta(ui_globals::curdats_view, ui_globals::curdats_total);
- else
- cap_delta(ui_globals::cur_sw_dats_view, ui_globals::cur_sw_dats_total);
- }
-}
-
-//-------------------------------------------------
// populate search list
//-------------------------------------------------
@@ -929,6 +859,10 @@ render_texture *menu_select_game::get_icon_texture(int linenum, void *selectedre
}
+//-------------------------------------------------
+// export displayed list
+//-------------------------------------------------
+
void menu_select_game::inkey_export()
{
std::vector<game_driver const *> list;
@@ -1028,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());
}
@@ -1053,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) )"),
@@ -1087,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 4099e248f54..8a9037a8864 100644
--- a/src/frontend/mame/ui/selgame.h
+++ b/src/frontend/mame/ui/selgame.h
@@ -33,7 +33,10 @@ public:
static void force_game_select(mame_ui_manager &mui, render_container &container);
protected:
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
+
void menu_activated() override;
+ void menu_deactivated() override;
private:
enum
@@ -55,15 +58,16 @@ private:
static bool s_first_start;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ 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
@@ -71,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
- void change_info_pane(int delta);
-
void build_available_list();
bool isfavorite() const;
@@ -87,8 +88,8 @@ private:
void load_custom_filters();
// handlers
- void inkey_select(const event *menu_event);
- void inkey_select_favorite(const event *menu_event);
+ bool inkey_select(const event *menu_event);
+ bool inkey_select_favorite(const event *menu_event);
};
} // namespace ui
diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp
index bc43ee4b4b8..5655c226d3f 100644
--- a/src/frontend/mame/ui/selmenu.cpp
+++ b/src/frontend/mame/ui/selmenu.cpp
@@ -15,12 +15,9 @@
#include "ui/info.h"
#include "ui/inifile.h"
-// these hold static bitmap images
-#include "ui/defimg.ipp"
-#include "ui/toolbar.ipp"
-
#include "audit.h"
#include "cheat.h"
+#include "infoxml.h"
#include "mame.h"
#include "mameopts.h"
@@ -42,13 +39,21 @@
#include <algorithm>
#include <cmath>
#include <cstring>
-#include <utility>
+
+
+// these hold static bitmap images
+#include "ui/defimg.ipp"
+#include "ui/toolbar.ipp"
namespace ui {
namespace {
+std::pair<char const *, char const *> RIGHT_PANEL_NAMES[RP_LAST + 1] = {
+ { "image", N_("Images") },
+ { "info", N_("Info") } };
+
enum
{
FIRST_VIEW = 0,
@@ -72,25 +77,25 @@ enum
LAST_VIEW = COVERS_VIEW
};
-std::pair<char const *, char const *> const arts_info[] =
-{
- { N_p("selmenu-artwork", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY },
- { N_p("selmenu-artwork", "Cabinet"), OPTION_CABINETS_PATH },
- { N_p("selmenu-artwork", "Control Panel"), OPTION_CPANELS_PATH },
- { N_p("selmenu-artwork", "PCB"), OPTION_PCBS_PATH },
- { N_p("selmenu-artwork", "Flyer"), OPTION_FLYERS_PATH },
- { N_p("selmenu-artwork", "Title Screen"), OPTION_TITLES_PATH },
- { N_p("selmenu-artwork", "Ending"), OPTION_ENDS_PATH },
- { N_p("selmenu-artwork", "Artwork Preview"), OPTION_ARTPREV_PATH },
- { N_p("selmenu-artwork", "Bosses"), OPTION_BOSSES_PATH },
- { N_p("selmenu-artwork", "Logo"), OPTION_LOGOS_PATH },
- { N_p("selmenu-artwork", "Versus"), OPTION_VERSUS_PATH },
- { N_p("selmenu-artwork", "Game Over"), OPTION_GAMEOVER_PATH },
- { N_p("selmenu-artwork", "HowTo"), OPTION_HOWTO_PATH },
- { N_p("selmenu-artwork", "Scores"), OPTION_SCORES_PATH },
- { N_p("selmenu-artwork", "Select"), OPTION_SELECT_PATH },
- { N_p("selmenu-artwork", "Marquee"), OPTION_MARQUEES_PATH },
- { N_p("selmenu-artwork", "Covers"), OPTION_COVER_PATH },
+std::tuple<char const *, char const *, char const *> const ARTS_INFO[] =
+{
+ { "snap", N_p("selmenu-artwork", "Snapshots"), OPTION_SNAPSHOT_DIRECTORY },
+ { "cabinet", N_p("selmenu-artwork", "Cabinet"), OPTION_CABINETS_PATH },
+ { "cpanel", N_p("selmenu-artwork", "Control Panel"), OPTION_CPANELS_PATH },
+ { "pcb", N_p("selmenu-artwork", "PCB"), OPTION_PCBS_PATH },
+ { "flyer", N_p("selmenu-artwork", "Flyer"), OPTION_FLYERS_PATH },
+ { "title", N_p("selmenu-artwork", "Title Screen"), OPTION_TITLES_PATH },
+ { "ending", N_p("selmenu-artwork", "Ending"), OPTION_ENDS_PATH },
+ { "artpreview", N_p("selmenu-artwork", "Artwork Preview"), OPTION_ARTPREV_PATH },
+ { "boss", N_p("selmenu-artwork", "Bosses"), OPTION_BOSSES_PATH },
+ { "logo", N_p("selmenu-artwork", "Logo"), OPTION_LOGOS_PATH },
+ { "versus", N_p("selmenu-artwork", "Versus"), OPTION_VERSUS_PATH },
+ { "gameover", N_p("selmenu-artwork", "Game Over"), OPTION_GAMEOVER_PATH },
+ { "howto", N_p("selmenu-artwork", "HowTo"), OPTION_HOWTO_PATH },
+ { "scores", N_p("selmenu-artwork", "Scores"), OPTION_SCORES_PATH },
+ { "select", N_p("selmenu-artwork", "Select"), OPTION_SELECT_PATH },
+ { "marquee", N_p("selmenu-artwork", "Marquee"), OPTION_MARQUEES_PATH },
+ { "cover", N_p("selmenu-artwork", "Covers"), OPTION_COVER_PATH },
};
char const *const hover_msg[] = {
@@ -100,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)
{
@@ -158,8 +175,6 @@ void load_driver_image(bitmap_argb32 &bitmap, emu_file &file, game_driver const
} // anonymous namespace
-constexpr std::size_t menu_select_launch::MAX_VISIBLE_SEARCH; // stupid non-inline semantics
-
class menu_select_launch::software_parts : public menu
{
@@ -168,8 +183,8 @@ public:
virtual ~software_parts() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
ui_software_info const &m_uiinfo;
s_parts const m_parts;
@@ -185,8 +200,8 @@ public:
private:
bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, void const *driver, bool software, bool inlist);
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
void const *m_driver;
bool m_software, m_inlist;
@@ -201,8 +216,8 @@ bool menu_select_launch::reselect_last::s_reselect = false;
// instantiate possible variants of these so derived classes don't get link errors
template bool menu_select_launch::select_bios(game_driver const &, bool);
template bool menu_select_launch::select_bios(ui_software_info const &, bool);
-template float menu_select_launch::draw_left_panel<machine_filter>(machine_filter::type current, std::map<machine_filter::type, machine_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
-template float menu_select_launch::draw_left_panel<software_filter>(software_filter::type current, std::map<software_filter::type, software_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
+template void menu_select_launch::draw_left_panel<machine_filter>(u32 flags, machine_filter::type current, std::map<machine_filter::type, machine_filter::ptr> const &filters);
+template void menu_select_launch::draw_left_panel<software_filter>(u32 flags, software_filter::type current, std::map<software_filter::type, software_filter::ptr> const &filters);
menu_select_launch::system_flags::system_flags(machine_static_info const &info)
@@ -272,13 +287,13 @@ menu_select_launch::software_parts::~software_parts()
// populate
//-------------------------------------------------
-void menu_select_launch::software_parts::populate(float &customtop, float &custombottom)
+void menu_select_launch::software_parts::populate()
{
std::vector<s_parts::const_iterator> parts;
parts.reserve(m_parts.size());
for (s_parts::const_iterator it = m_parts.begin(); m_parts.end() != it; ++it)
parts.push_back(it);
- std::sort(parts.begin(), parts.end(), [] (auto const &left, auto const &right) { return 0 > core_stricmp(left->first.c_str(), right->first.c_str()); });
+ std::sort(parts.begin(), parts.end(), [] (auto const &left, auto const &right) { return 0 > core_stricmp(left->first, right->first); });
for (auto const &elem : parts)
item_append(elem->first, elem->second, 0, (void *)&*elem);
@@ -289,9 +304,8 @@ void menu_select_launch::software_parts::populate(float &customtop, float &custo
// handle
//-------------------------------------------------
-void menu_select_launch::software_parts::handle(event const *ev)
+bool menu_select_launch::software_parts::handle(event const *ev)
{
- // process the menu
if (ev && (ev->iptkey == IPT_UI_SELECT) && ev->itemref)
{
for (auto const &elem : m_parts)
@@ -303,6 +317,8 @@ void menu_select_launch::software_parts::handle(event const *ev)
}
}
}
+
+ return false;
}
@@ -342,7 +358,7 @@ menu_select_launch::bios_selection::~bios_selection()
// populate
//-------------------------------------------------
-void menu_select_launch::bios_selection::populate(float &customtop, float &custombottom)
+void menu_select_launch::bios_selection::populate()
{
for (auto &elem : m_bios)
item_append(elem.first, 0, (void *)&elem.first);
@@ -354,9 +370,8 @@ void menu_select_launch::bios_selection::populate(float &customtop, float &custo
// handle
//-------------------------------------------------
-void menu_select_launch::bios_selection::handle(event const *ev)
+bool menu_select_launch::bios_selection::handle(event const *ev)
{
- // process the menu
if (ev && (ev->iptkey == IPT_UI_SELECT) && ev->itemref)
{
for (auto & elem : m_bios)
@@ -394,6 +409,8 @@ void menu_select_launch::bios_selection::handle(event const *ev)
}
}
}
+
+ return false;
}
@@ -411,10 +428,10 @@ menu_select_launch::cache::cache(running_machine &machine)
// create a texture for snapshot
m_snapx_texture.reset(render.texture_alloc(render_texture::hq_scale));
- std::memcpy(&m_no_avail_bitmap.pix(0), no_avail_bmp, 256 * 256 * sizeof(uint32_t));
+ 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);
}
@@ -428,7 +445,7 @@ void menu_select_launch::cache::cache_toolbar(running_machine &machine, float wi
// not bothering to transform for non-square pixels greatly simplifies this
render_manager &render(machine.render());
render_target const &target(render.ui_target());
- int32_t const pix_size(std::ceil(std::max(width * target.width(), height * target.height())));
+ s32 const pix_size(std::ceil(std::max(width * target.width(), height * target.height())));
if (m_toolbar_textures.empty() || (m_toolbar_bitmaps[0].width() != pix_size) || (m_toolbar_bitmaps[0].height() != pix_size))
{
m_toolbar_textures.clear();
@@ -444,7 +461,7 @@ void menu_select_launch::cache::cache_toolbar(running_machine &machine, float wi
float const drawscale((std::max)(xscale, yscale));
// rasterise the SVG and clear it out of memory
- bitmap_argb32 &bitmap(m_toolbar_bitmaps[i]);
+ bitmap_argb32 &bitmap(m_toolbar_bitmaps[2 * i]);
bitmap.resize(pix_size, pix_size);
nsvgRasterize(
rasterizer.get(),
@@ -456,20 +473,28 @@ void menu_select_launch::cache::cache_toolbar(running_machine &machine, float wi
svg.reset();
// correct colour format
- for (int32_t y = 0; bitmap.height() > y; ++y)
+ 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)
{
- uint32_t *dst(&bitmap.pix(y));
- for (int32_t 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);
}
}
}
@@ -482,6 +507,7 @@ menu_select_launch::~menu_select_launch()
menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &container, bool is_swlist)
: menu(mui, container)
+ , m_skip_main_items(0)
, m_prev_selected(nullptr)
, m_total_lines(0)
, m_topline_datsview(0)
@@ -493,15 +519,47 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c
, m_items_list()
, m_info_buffer()
, m_info_layout()
+ , m_icon_width(0)
+ , m_icon_height(0)
+ , m_divider_width(0.0F)
+ , m_divider_arrow_width(0.0F)
+ , m_divider_arrow_height(0.0F)
+ , m_info_line_height(0.0F)
, 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)
, m_switch_image(false)
- , m_default_image(true)
, m_image_view(FIRST_VIEW)
, m_flags(256)
{
@@ -510,25 +568,109 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c
}
-void 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();
- m_default_image = false;
+ return std::make_pair(true, (LAST_VIEW > m_image_view));
+ }
+ else
+ {
+ return std::make_pair(false, false);
}
}
-void 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();
- m_default_image = false;
+ return std::make_pair(true, (FIRST_VIEW < m_image_view));
+ }
+ else
+ {
+ 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);
}
@@ -607,24 +749,69 @@ void menu_select_launch::launch_system(mame_ui_manager &mui, game_driver const &
//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_select_launch::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ // calculate icon size in pixels
+ render_target const &target(machine().render().ui_target());
+ bool const rotated((target.orientation() & ORIENTATION_SWAP_XY) != 0);
+ m_icon_width = int((rotated ? height : width) * line_height() * aspect);
+ m_icon_height = int((rotated ? width : height) * line_height());
+
+ // force info text to be laid out again
+ m_info_layout = std::nullopt;
+
+ // calculate size of dividers between panes
+ 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);
+}
+
+
+//-------------------------------------------------
// perform our special rendering
//-------------------------------------------------
-void menu_select_launch::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::custom_render(u32 flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
std::string tempbuf[4];
// determine the text for the header
make_topbox_text(tempbuf[0], tempbuf[1], tempbuf[2]);
- float const y1 = origy1 - 3.0f * ui().box_tb_border() - ui().get_line_height();
+ float const y1 = origy1 - 3.0f * tb_border() - line_height();
draw_text_box(
tempbuf, tempbuf + 3,
origx1, origx2, origy1 - top, y1,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, true,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
// draw toolbar
- draw_toolbar(origx1, y1, origx2, origy1 - ui().box_tb_border());
+ draw_toolbar(flags, origx1, y1, origx2, origy1 - tb_border());
// determine the text to render below
ui_software_info const *swinfo;
@@ -714,26 +901,53 @@ 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
draw_text_box(
std::begin(tempbuf), std::end(tempbuf),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, true,
- ui().colors().text_color(), color, 1.0f);
+ ui().colors().text_color(), color);
// is favorite? draw the star
if (isstar)
- draw_star(origx1 + ui().box_lr_border() * machine().render().ui_aspect(&container()), origy2 + (2.0f * ui().box_tb_border()));
+ draw_star(origx1 + lr_border(), origy2 + (2.0f * tb_border()));
+}
+
+
+void menu_select_launch::menu_activated()
+{
+ m_panels_status = ui().options().hide_panels();
+ m_wheel_target = focused_menu::MAIN;
+ m_wheel_movement = 0;
+}
+
+
+void menu_select_launch::menu_deactivated()
+{
+ ui().options().set_value(OPTION_HIDE_PANELS, m_panels_status, OPTION_PRIORITY_CMDLINE);
}
@@ -742,27 +956,39 @@ void menu_select_launch::rotate_focus(int dir)
switch (get_focus())
{
case focused_menu::MAIN:
- if (selected_index() <= m_available_items)
+ if (selected_index() >= m_available_items)
+ {
+ if ((0 > dir) || (m_panels_status == HIDE_BOTH))
+ select_prev();
+ else if (m_panels_status == HIDE_LEFT_PANEL)
+ set_focus(focused_menu::RIGHTTOP);
+ else
+ set_focus(focused_menu::LEFT);
+ }
+ else if (m_skip_main_items || (m_panels_status != HIDE_BOTH))
{
- if (skip_main_items || (ui_globals::panels_status != HIDE_BOTH))
+ m_prev_selected = get_selection_ref();
+ if (0 < dir)
{
- m_prev_selected = get_selection_ref();
- if ((0 < dir) || (ui_globals::panels_status == HIDE_BOTH))
+ if (m_skip_main_items)
set_selected_index(m_available_items + 1);
- else if (ui_globals::panels_status == HIDE_RIGHT_PANEL)
- set_focus(focused_menu::LEFT);
+ else if (m_panels_status == HIDE_LEFT_PANEL)
+ set_focus(focused_menu::RIGHTTOP);
else
- set_focus(focused_menu::RIGHTBOTTOM);
+ set_focus(focused_menu::LEFT);
}
- }
- else
- {
- if ((0 > dir) || (ui_globals::panels_status == HIDE_BOTH))
- select_prev();
- else if (ui_globals::panels_status == HIDE_LEFT_PANEL)
- set_focus(focused_menu::RIGHTTOP);
- else
+ else if (m_panels_status == HIDE_RIGHT_PANEL)
+ {
set_focus(focused_menu::LEFT);
+ }
+ else if (m_panels_status != HIDE_BOTH)
+ {
+ set_focus(focused_menu::RIGHTBOTTOM);
+ }
+ else
+ {
+ set_selected_index(m_available_items + 1);
+ }
}
break;
@@ -770,12 +996,12 @@ void menu_select_launch::rotate_focus(int dir)
if (0 > dir)
{
set_focus(focused_menu::MAIN);
- if (skip_main_items)
+ if (m_skip_main_items)
set_selected_index(m_available_items + 1);
else
select_prev();
}
- else if (ui_globals::panels_status != HIDE_RIGHT_PANEL)
+ else if (m_panels_status != HIDE_RIGHT_PANEL)
{
set_focus(focused_menu::RIGHTTOP);
}
@@ -791,14 +1017,17 @@ void menu_select_launch::rotate_focus(int dir)
{
set_focus(focused_menu::RIGHTBOTTOM);
}
- else if (ui_globals::panels_status != HIDE_LEFT_PANEL)
+ else if (m_panels_status != HIDE_LEFT_PANEL)
{
set_focus(focused_menu::LEFT);
}
else
{
set_focus(focused_menu::MAIN);
- set_selected_index(m_available_items + 1);
+ if (m_skip_main_items)
+ set_selected_index(m_available_items + 1);
+ else
+ select_prev();
}
break;
@@ -830,206 +1059,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)
{
- float const aspect = machine().render().ui_aspect(&container());
- float const line_height = ui().get_line_height();
- float const lr_arrow_width = 0.4f * line_height * aspect;
- float const gutter_width = 0.5f * line_height * aspect;
-
- // set left-right arrows dimension
- float const ar_x0 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width - lr_arrow_width;
- float const ar_y0 = origy1 + 0.1f * line_height;
- float const ar_x1 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width;
- float const ar_y1 = origy1 + 0.9f * line_height;
-
- float const al_x0 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width;
- float const al_y0 = origy1 + 0.1f * line_height;
- float const al_x1 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width + lr_arrow_width;
- float const al_y1 = origy1 + 0.9f * line_height;
+ float const linetop(m_right_content_vbounds.first + (float(line) * m_info_line_height));
+ float const linebottom(m_right_content_vbounds.first + (float(line + 1) * m_info_line_height));
+ bool const hovered(pointer_in_rect(m_right_content_hbounds.first, linetop, m_right_content_hbounds.second, linebottom));
+ bool const clicked((pointer_action::RIGHT_TRACK_LINE == m_pointer_action) && (line == m_clicked_line));
- rgb_t fgcolor_right, fgcolor_left;
- fgcolor_right = fgcolor_left = ui().colors().text_color();
-
- // set hover
- if (mouse_in_rect(ar_x0, ar_y0, ar_x1, ar_y1) && current != dmax)
+ rgb_t bgcolor = ui().colors().text_bg_color();
+ rgb_t fgcolor = ui().colors().text_color();
+ if (clicked && hovered)
{
- ui().draw_textured_box(container(), ar_x0 + 0.01f, ar_y0, ar_x1 - 0.01f, ar_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43),
- hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
- set_hover(HOVER_UI_RIGHT);
- fgcolor_right = ui().colors().mouseover_color();
+ // draw selected highlight for tracked item
+ bgcolor = ui().colors().selected_bg_color();
+ fgcolor = ui().colors().selected_color();
+ highlight(m_right_content_hbounds.first, linetop, m_right_content_hbounds.second, linebottom, bgcolor);
}
- else if (mouse_in_rect(al_x0, al_y0, al_x1, al_y1) && current != dmin)
+ else if (clicked || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered && pointer_idle()))
{
- ui().draw_textured_box(container(), al_x0 + 0.01f, al_y0, al_x1 - 0.01f, al_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43),
- hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
- set_hover(HOVER_UI_LEFT);
- fgcolor_left = ui().colors().mouseover_color();
+ // draw hover highlight when hovered over or dragged off
+ bgcolor = ui().colors().mouseover_bg_color();
+ fgcolor = ui().colors().mouseover_color();
+ highlight(m_right_content_hbounds.first, linetop, m_right_content_hbounds.second, linebottom, bgcolor);
}
- // apply arrow
- if (dmax == dmin)
- return;
- else if (current == dmin)
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90);
- else if (current == dmax)
- draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X);
- else
- {
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90);
- draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X);
- }
+ draw_arrow(
+ 0.5F * (m_right_content_hbounds.first + m_right_content_hbounds.second - (x_aspect() * m_info_line_height)),
+ linetop + (0.25F * m_info_line_height),
+ 0.5F * (m_right_content_hbounds.first + m_right_content_hbounds.second + (x_aspect() * m_info_line_height)),
+ linetop + (0.75F * m_info_line_height),
+ fgcolor,
+ line ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
}
//-------------------------------------------------
-// draw info arrow
+// draw vertical divider
//-------------------------------------------------
-void menu_select_launch::draw_info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float text_size, 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();
- uint32_t orientation = (!ub) ? ROT0 : ROT0 ^ ORIENTATION_FLIP_Y;
-
- if (mouse_in_rect(origx1, oy1, origx2, oy1 + (line_height * text_size)))
+ 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 * text_size), 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();
}
- draw_arrow(0.5f * (origx1 + origx2) - 0.5f * (ud_arrow_width * text_size), oy1 + 0.25f * (line_height * text_size),
- 0.5f * (origx1 + origx2) + 0.5f * (ud_arrow_width * text_size), oy1 + 0.75f * (line_height * text_size), 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 ((ui_globals::panels_status != SHOW_PANELS) && (ui_globals::panels_status != HIDE_RIGHT_PANEL))
- return draw_collapsed_left_panel(x1, y1, x2, y2);
-
- // calculate line height
- float const line_height(ui().get_line_height());
- float const text_size(ui().options().infos_size());
- float const sc(y2 - y1 - (2.0f * ui().box_tb_border()));
- float line_height_max(line_height * text_size);
- if ((Filter::COUNT * line_height_max) > sc)
+ if (!show_left_panel())
{
- 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, text_size);
+ // 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), text_size) + text_sign, left_width);
+ // 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);
- float const aspect(machine().render().ui_aspect(&container()));
- float const lr_border(ui().box_lr_border() * aspect);
- 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 += ui().box_tb_border();
- y2 -= ui().box_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 - x1,
- text_layout::text_justify::LEFT, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NORMAL, fgcolor, bgcolor,
- nullptr, nullptr, text_size);
- y1 += line_height_max;
- }
-
- x1 = x2 + lr_border;
- x2 = x1 + 2.0f * lr_border;
- y1 = origy1;
- y2 = origy2;
- float const space = x2 - x1;
- float const lr_arrow_width = 0.4f * space * aspect;
-
- // set left-right arrows dimension
- float const ar_x0 = 0.5f * (x2 + x1) - 0.5f * lr_arrow_width;
- float const ar_y0 = 0.5f * (y2 + y1) + 0.1f * space;
- float const ar_x1 = ar_x0 + lr_arrow_width;
- float const ar_y1 = 0.5f * (y2 + y1) + 0.9f * space;
-
- 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);
}
@@ -1127,24 +1361,14 @@ bool menu_select_launch::scale_icon(bitmap_argb32 &&src, texture_and_bitmap &dst
assert(dst.texture);
if (src.valid())
{
- // calculate available space for the icon in pixels
- float const height(ui().get_line_height());
- float const width(height * container().manager().ui_aspect(&container()));
- render_target const &target(machine().render().ui_target());
- uint32_t const dst_height(target.height());
- uint32_t const dst_width(target.width());
- bool const rotated((target.orientation() & ORIENTATION_SWAP_XY) != 0);
- int const max_height(int((rotated ? dst_width : dst_height) * height));
- int const max_width(int((rotated ? dst_height : dst_width) * width));
-
// reduce the source bitmap if it's too big
bitmap_argb32 tmp;
- float const ratio((std::min)({ float(max_height) / src.height(), float(max_width) / src.width(), 1.0F }));
+ 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 pix_height(std::ceil(src.height() * ratio));
float const pix_width(std::ceil(src.width() * ratio));
- tmp.allocate(int32_t(pix_width), int32_t(pix_height));
+ tmp.allocate(s32(pix_width), s32(pix_height));
render_resample_argb_bitmap_hq(tmp, src, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true);
}
else
@@ -1153,7 +1377,7 @@ bool menu_select_launch::scale_icon(bitmap_argb32 &&src, texture_and_bitmap &dst
}
// copy into the destination
- dst.bitmap.allocate(max_width, max_height);
+ dst.bitmap.allocate(m_icon_width, m_icon_height);
for (int y = 0; tmp.height() > y; ++y)
for (int x = 0; tmp.width() > x; ++x)
dst.bitmap.pix(y, x) = tmp.pix(y, x);
@@ -1209,78 +1433,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()));
- float const lr_border(ui().box_lr_border() * aspect);
- x1 += lr_border;
- x2 -= lr_border;
- y1 += ui().box_tb_border();
- y2 -= ui().box_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 + ui().get_line_height() + 2.0f * ui().box_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 + ui().get_line_height() + 2.0f * ui().box_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));
}
}
@@ -1294,8 +1526,8 @@ void menu_select_launch::draw_star(float x0, float y0)
{
if (TOOLBAR_BITMAP_FAVORITE < m_cache.toolbar_textures().size())
{
- float const y1 = y0 + ui().get_line_height();
- float const x1 = x0 + ui().get_line_height() * container().manager().ui_aspect(&container());
+ float const y1 = y0 + line_height();
+ float const x1 = x0 + line_height() * container().manager().ui_aspect(&container());
container().add_quad(
x0, y0, x1, y1,
rgb_t::white(),
@@ -1305,13 +1537,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
//-------------------------------------------------
@@ -1321,9 +1546,8 @@ void menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, flo
render_texture *const icon(get_icon_texture(linenum, selectedref));
if (icon)
{
- float const ud_arrow_width = ui().get_line_height() * container().manager().ui_aspect(&container());
- float const x1 = x0 + ud_arrow_width;
- float const y1 = y0 + ui().get_line_height();
+ float const x1 = x0 + ud_arrow_width();
+ float const y1 = y0 + line_height();
container().add_quad(x0, y0, x1, y1, rgb_t::white(), icon, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
};
}
@@ -1333,24 +1557,23 @@ 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", arts_info[m_image_view].first));
+ std::string searchstr;
// get search path
std::string addpath;
if (m_image_view == SNAPSHOT_VIEW)
{
emu_options moptions;
- searchstr = machine().options().value(arts_info[m_image_view].second);
- addpath = moptions.value(arts_info[m_image_view].second);
+ searchstr = machine().options().value(std::get<2>(ARTS_INFO[m_image_view]));
+ addpath = moptions.value(std::get<2>(ARTS_INFO[m_image_view]));
}
else
{
ui_options moptions;
- searchstr = ui().options().value(arts_info[m_image_view].second);
- addpath = moptions.value(arts_info[m_image_view].second);
+ searchstr = ui().options().value(std::get<2>(ARTS_INFO[m_image_view]));
+ addpath = moptions.value(std::get<2>(ARTS_INFO[m_image_view]));
}
std::string tmp(searchstr);
@@ -1365,6 +1588,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 (!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;
}
@@ -1372,11 +1881,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(uint32_t 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))
@@ -1387,39 +1896,46 @@ void menu_select_launch::handle_keys(uint32_t 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_CANCEL, 0))
+ if (exclusive_input_pressed(iptkey, IPT_UI_BACK, 0))
{
if (m_ui_error)
{
// dismiss error
+ return false;
}
- else if (!m_search.empty())
+ else if (!is_special_main_menu() && m_search.empty())
+ {
+ // pop the stack if this isn't the root session menu
+ stack_pop();
+ return false;
+ }
+ }
+
+ if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
+ {
+ if (!m_search.empty())
{
// escape pressed with non-empty search text clears it
m_search.clear();
reset(reset_options::REMEMBER_REF);
}
- else
+ else if (is_special_main_menu())
{
- // otherwise pop the stack
+ // this is the root session menu, exit
stack_pop();
- if (is_special_main_menu())
- machine().schedule_exit();
+ machine().schedule_exit();
}
- return;
+ return false;
}
// validate the current selection
validate_selection(1);
-
- // swallow left/right keys if they are not appropriate
- bool const leftclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_LEFT_PANEL);
- bool const rightclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL);
+ 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))
@@ -1427,85 +1943,151 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
if (m_focus == focused_menu::RIGHTTOP)
{
// Swap the right panel and swallow it
- ui_globals::rpanel = 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)
{
// Swap the right panel and swallow it
- ui_globals::rpanel = 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 (!leftclose && 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 (!rightclose && ((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 (!leftclose && 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 (!rightclose && ((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 (!rightclose && ((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));
@@ -1516,14 +2098,25 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
// page down advances by m_visible_items
if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_DOWN, 6))
{
- // Infos
- if (!rightclose && ((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));
@@ -1534,54 +2127,92 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
// home goes to the start
if (exclusive_input_pressed(iptkey, IPT_UI_HOME, 0))
{
- if (!leftclose && 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 (!rightclose && ((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 (!leftclose && 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 (!rightclose && ((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))
+ {
+ if (!m_ui_error && accept_search())
+ {
+ if (paste_text(m_search, uchar_is_printable))
+ reset(reset_options::SELECT_FIRST);
+ }
+ }
// see if any other UI keys are pressed
if (iptkey == IPT_INVALID)
@@ -1603,322 +2234,967 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
break;
}
}
+ return updated;
}
//-------------------------------------------------
-// handle input events for main menu
+// handle pointer input for main menu
//-------------------------------------------------
-void menu_select_launch::handle_events(uint32_t 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();
- int32_t 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;
}
-
- if (ui_globals::panels_status == HIDE_LEFT_PANEL)
- ui_globals::panels_status = SHOW_PANELS;
- else if (ui_globals::panels_status == HIDE_BOTH)
- ui_globals::panels_status = HIDE_RIGHT_PANEL;
- else if (ui_globals::panels_status == SHOW_PANELS)
- ui_globals::panels_status = HIDE_LEFT_PANEL;
- else if (ui_globals::panels_status == HIDE_RIGHT_PANEL)
- ui_globals::panels_status = HIDE_BOTH;
+ 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);
+ }
+ }
- if (ui_globals::panels_status == HIDE_RIGHT_PANEL)
- ui_globals::panels_status = SHOW_PANELS;
- else if (ui_globals::panels_status == HIDE_BOTH)
- ui_globals::panels_status = HIDE_LEFT_PANEL;
- else if (ui_globals::panels_status == SHOW_PANELS)
- ui_globals::panels_status = HIDE_RIGHT_PANEL;
- else if (ui_globals::panels_status == HIDE_LEFT_PANEL)
- ui_globals::panels_status = HIDE_BOTH;
+ // 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_CANCEL;
- 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)
{
- ui_globals::rpanel = (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() - 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;
+ }
+
+ // 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);
+}
- case ui_event::type::MOUSE_RDOWN:
- if (hover() >= 0 && hover() < item_count() - skip_main_items)
+
+//-------------------------------------------------
+// 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);
}
//-------------------------------------------------
-// draw main menu
+// move selection to visible range
//-------------------------------------------------
-void menu_select_launch::draw(uint32_t flags)
+bool menu_select_launch::main_force_visible_selection()
{
- bool noinput = (flags & PROCESS_NOINPUT);
- float const aspect = machine().render().ui_aspect(&container());
- float const lr_border = ui().box_lr_border() * aspect;
- float line_height = ui().get_line_height();
- float const ud_arrow_width = line_height * aspect;
- float const gutter_width = 0.52f * ud_arrow_width;
- float const icon_offset = m_has_icons ? (1.5f * ud_arrow_width) : 0.0f;
- float right_panel_size = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL) ? 2.0f * lr_border : 0.3f;
- float visible_width = 1.0f - 4.0f * lr_border;
- float primary_left = (1.0f - visible_width) * 0.5f;
- float primary_width = visible_width;
+ int const first(main_at_top() ? 0 : (top_line + 1));
+ int const last(top_line + m_primary_lines - (main_at_bottom() ? 1 : 2));
+ if (selected_index() < m_available_items)
+ {
+ int const restricted(std::clamp(selected_index(), first, last));
+ if (selected_index() != restricted)
+ {
+ set_selected_index(restricted);
+ return true;
+ }
+ }
+ else
+ {
+ int selection(0);
+ while ((m_available_items > selection) && (item(selection).ref() != m_prev_selected))
+ ++selection;
+ auto const ref(item(std::clamp(selection, first, last)).ref());
+ if (ref != m_prev_selected)
+ {
+ m_prev_selected = ref;
+ return true;
+ }
+ }
+ return false;
+}
- draw_background();
- clear_hover();
- m_available_items = item_count() - skip_main_items;
- float extra_height = skip_main_items * line_height;
- float visible_extra_menu_height = get_customtop() + get_custombottom() + extra_height;
+//-------------------------------------------------
+// draw main menu
+//-------------------------------------------------
- // locate mouse
- if (noinput)
- ignore_mouse();
- else
- map_mouse();
+void menu_select_launch::draw(u32 flags)
+{
- // account for extra space at the top and bottom
- float visible_main_menu_height = 1.0f - 2.0f * ui().box_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;
+ // 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;
+ }
- if (!m_is_swlist)
- ui_globals::visible_main_lines = m_visible_lines;
- else
- ui_globals::visible_sw_lines = m_visible_lines;
-
- // 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 - ui().box_tb_border();
- float x2 = x1 + 2.0f * lr_border;
- float y2 = visible_top + visible_main_menu_height + ui().box_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;
+ // ensure the selection is visible
+ m_available_items = item_count() - m_skip_main_items;
+ m_visible_lines = (std::min)(m_primary_lines, m_available_items);
int selection;
if (selected_index() < m_available_items)
{
@@ -1943,34 +3219,41 @@ void menu_select_launch::draw(uint32_t 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);
- 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 = visible_top + (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);
@@ -1978,175 +3261,177 @@ void menu_select_launch::draw(uint32_t 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 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 ((!linenum && top_line) || ((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);
- }
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
- ui().draw_text_full(
- container(),
- subitem_text,
- effective_left + icon_offset, line_y, ui().get_string_width(pitem.subtext()),
- text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NONE, item_invert ? fgcolor3 : fgcolor, bgcolor,
- &subitem_width, nullptr);
- 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);
-
- // 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);
+ draw_icon(linenum, pitem.ref(), item_text_left, linetop);
+ if (pitem.subtext().empty())
+ {
+ // draw the item left-aligned
+ ui().draw_text_full(
+ container(),
+ itemtext,
+ item_text_left + icon_offset, linetop, item_text_width - icon_offset,
+ text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
+ nullptr, nullptr,
+ line_height());
+ }
+ else
+ {
+ // compute right space for subitem
+ std::string_view const subitem_text(pitem.subtext());
+ float const subitem_width(get_string_width(subitem_text) + gutter_width());
+
+ // draw the item left-aligned
+ float item_width;
+ ui().draw_text_full(
+ container(),
+ itemtext,
+ item_text_left + icon_offset, linetop, item_text_width - icon_offset - subitem_width,
+ text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
+ &item_width, nullptr,
+ line_height());
+
+ // draw the subitem right-aligned
+ ui().draw_text_full(
+ container(),
+ subitem_text,
+ item_text_left + icon_offset + item_width, linetop, item_text_width - icon_offset - item_width,
+ text_layout::text_justify::RIGHT, text_layout::word_wrapping::NEVER,
+ mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
+ nullptr, nullptr,
+ line_height());
+ }
}
}
- for (size_t count = m_available_items; count < item_count(); count++)
+ // draw extra fixed items
+ for (size_t linenum = 0; linenum < m_skip_main_items; linenum++)
{
- const menu_item &pitem = item(count);
- const std::string_view itemtext = pitem.text();
- float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
- float line_y0 = line;
- float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
- float line_y1 = line + line_height;
+ int const itemnum(m_available_items + linenum);
+ menu_item const &pitem(item(itemnum));
+ std::string_view const itemtext(pitem.text());
+ float const linetop(m_primary_items_top + (float(m_primary_lines + linenum) * line_height()));
+ float const linebottom(linetop + line_height());
+
+ // work out colours
rgb_t fgcolor = ui().colors().text_color();
rgb_t bgcolor = ui().colors().text_bg_color();
-
- if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
- set_hover(count);
-
- // if we're selected, draw with a different background
- if (is_selected(count) && m_focus == focused_menu::MAIN)
+ bool const hovered(is_selectable(pitem) && pointer_in_rect(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom));
+ bool const pointerline((pointer_action::MAIN_TRACK_LINE == m_pointer_action) && ((m_primary_lines + linenum) == m_clicked_line));
+ if (is_selected(itemnum) && (get_focus() == focused_menu::MAIN))
{
+ // if we're selected, draw with a different background
fgcolor = rgb_t(0xff, 0xff, 0x00);
bgcolor = rgb_t(0xff, 0xff, 0xff);
- ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43),
+ ui().draw_textured_box(
+ container(),
+ m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom,
+ bgcolor, rgb_t(43, 43, 43),
hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
- // else if the mouse is over this item, draw with a different background
- else if (count == hover())
+ else if (pointerline && hovered)
+ {
+ // 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()))
{
- fgcolor = ui().options().mouseover_color();
+ // draw hover highlight when hovered over or dragged off
+ fgcolor = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
- highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
}
if (pitem.type() == menu_item_type::SEPARATOR)
{
+ // if we're just a divider, draw a line
container().add_line(
- visible_left, line + 0.5f * line_height,
- visible_left + visible_width, line + 0.5f * line_height,
- UI_LINE_WIDTH,
- ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ m_primary_items_hbounds.first, linetop + (0.5F * line_height()),
+ m_primary_items_hbounds.second, linetop + (0.5F * line_height()),
+ UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
else
{
+ // draw the item centred
ui().draw_text_full(
container(),
itemtext,
- effective_left, line, effective_width,
+ item_text_left, linetop, item_text_width,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
mame_ui_manager::NORMAL, fgcolor, bgcolor,
- nullptr, nullptr);
+ nullptr, nullptr,
+ line_height());
}
- line += line_height;
}
- x1 = x2;
- x2 += right_panel_size;
-
- draw_right_panel(x1, y1, x2, y2);
-
- x1 = primary_left - lr_border;
- x2 = primary_left + primary_width + lr_border;
-
// if there is something special to add, do it by calling the virtual method
- custom_render(get_selection_ref(), get_customtop(), get_custombottom(), x1, y1, x2, y2);
+ custom_render(
+ flags,
+ get_selection_ref(),
+ get_customtop(), get_custombottom(),
+ lr_border(), m_primary_vbounds.first, 1.0F - lr_border(), m_primary_vbounds.second);
- // return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
- m_visible_items = m_visible_lines - (top_line != 0) - (top_line + m_visible_lines != m_available_items);
-
- // noinput
- if (noinput)
+ // show error text if necessary
+ if (m_ui_error)
{
- int alpha = (1.0f - machine().options().pause_brightness()) * 255.0f;
- if (alpha > 255)
- alpha = 255;
- if (alpha >= 0)
- container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(alpha, 0x00, 0x00, 0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_rect(0.0F, 0.0F, 1.0F, 1.0F, rgb_t(114, 0, 0, 0), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ ui().draw_text_box(container(), m_error_text, text_layout::text_justify::CENTER, 0.5f, 0.5f, UI_RED_COLOR);
}
+
+ // return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
+ m_visible_items = m_visible_lines - (top_line != 0) - (top_line + m_visible_lines != m_available_items);
}
@@ -2154,121 +3439,197 @@ void menu_select_launch::draw(uint32_t 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 aspect(machine().render().ui_aspect(&container()));
- bool const hide((ui_globals::panels_status == HIDE_RIGHT_PANEL) || (ui_globals::panels_status == HIDE_BOTH));
- float const x2(hide ? origx2 : (origx1 + 2.0f * ui().box_lr_border() * aspect));
- float const space(x2 - origx1);
- float const lr_arrow_width(0.4f * space * aspect);
+ if (show_right_panel())
+ {
+ m_right_panel_width = 0.3F - m_divider_width;
- // set left-right arrows dimension
- float const ar_x0(0.5f * (x2 + origx1) - 0.5f * lr_arrow_width);
- float const ar_y0(0.5f * (origy2 + origy1) + 0.1f * space);
- float const ar_x1(ar_x0 + lr_arrow_width);
- float const ar_y1(0.5f * (origy2 + origy1) + 0.9f * space);
+ 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);
- ui().draw_outlined_box(container(), origx1, origy1, origx2, origy2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
+ 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());
+ }
- 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 (m_right_panel == RP_IMAGES)
+ arts_render(flags);
+ else
+ infos_render(flags);
}
-
- if (hide)
+ else
{
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
- return;
+ m_right_panel_width = 0.0F;
}
- draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90);
- origy1 = draw_right_box_title(x2, origy1, origx2, origy2);
-
- if (ui_globals::rpanel == RP_IMAGES)
- arts_render(x2, origy1, origx2, origy2);
- else
- infos_render(x2, origy1, origx2, origy2);
+ 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)
{
- auto line_height = ui().get_line_height();
- float const midl = (x2 - x1) * 0.5f;
-
- // add outlined box for options
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ m_right_tabs_bottom = m_primary_vbounds.first + line_height();
- // 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));
+ float const x1(1.0F - lr_border() - m_right_panel_width);
+ float const x2(1.0F - lr_border());
+ float const tabwidth = right_tab_width();
- std::string buffer[RP_LAST + 1];
- buffer[RP_IMAGES] = _("Images");
- buffer[RP_INFOS] = _("Infos");
+ 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 = ui().get_string_width(elem) + 0.01f;
- float tmp_size = (textlen > midl) ? (midl / textlen) : 1.0f;
+ auto textlen = get_string_width(elem) + 0.01f;
+ 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 (ui_globals::rpanel != cells)
- {
- bgcolor = ui().colors().mouseover_bg_color();
- fgcolor = ui().options().mouseover_color();
- set_hover(HOVER_RP_FIRST + cells);
- }
- }
-
- if (ui_globals::rpanel != cells)
- {
- container().add_line(x1, y1 + line_height, x1 + midl, y1 + line_height, UI_LINE_WIDTH,
- ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- if (fgcolor != ui().colors().mouseover_color())
- fgcolor = ui().colors().clone_color();
- }
+ float const tableft(x1 + (float(cells - RP_FIRST) * tabwidth));
- if (m_focus == focused_menu::RIGHTTOP && ui_globals::rpanel == 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, text_size);
- x1 += midl;
+ mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr,
+ line_height() * text_size);
+
+ // 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
+//-------------------------------------------------
- return (y1 + line_height + UI_LINE_WIDTH);
+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);
+ }
+
+ 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);
+ }
}
@@ -2276,8 +3637,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);
@@ -2286,16 +3650,10 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f
{
m_cache.set_snapx_driver(nullptr);
- if (m_default_image)
- m_image_view = (software->startempty == 0) ? SNAPSHOT_VIEW : CABINETS_VIEW;
-
- // 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)
@@ -2315,90 +3673,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);
- if (m_default_image)
- m_image_view = ((system->driver->flags & machine_flags::MASK_TYPE) != machine_flags::TYPE_ARCADE) ? CABINETS_VIEW : SNAPSHOT_VIEW;
-
- 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)
-{
- float const line_height = ui().get_line_height();
- float const gutter_width = 0.4f * line_height * machine().render().ui_aspect(&container()) * 1.3f;
-
- 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 text_length;
- ui().draw_text_full(container(),
- _("selmenu-artwork", arts_info[x].first), origx1, origy1, origx2 - origx1,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
- &text_length, nullptr);
- 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 + ui().box_tb_border(),
- origx1 + ((middle + title_size) * 0.5f), origy1 + ui().box_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 + ui().box_tb_border(), origx2 - origx1,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor,
- nullptr, nullptr, tmp_size);
-
- draw_common_arrow(origx1, origy1 + ui().box_tb_border(), origx2, origy2, m_image_view, FIRST_VIEW, LAST_VIEW, title_size);
-
- return searchstr;
}
@@ -2406,13 +3705,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;
- float line_height = ui().get_line_height();
-
// 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());
@@ -2421,76 +3718,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 * ui().box_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);
}
@@ -2498,22 +3793,66 @@ 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 line_height = ui().get_line_height();
- float const x1 = origx1 + 0.01f;
- float const x2 = origx2 - 0.01f;
- float const y1 = origy1 + (2.0f * ui().box_tb_border()) + line_height;
- float const y2 = origy2 - ui().box_tb_border() - line_height;
+ 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));
+ }
+}
+
+
+char const *menu_select_launch::right_panel_config_string() const
+{
+ assert(std::size(RIGHT_PANEL_NAMES) > m_right_panel);
+ return RIGHT_PANEL_NAMES[m_right_panel].first;
+}
+
+char const *menu_select_launch::right_image_config_string() const
+{
+ assert(std::size(ARTS_INFO) > m_image_view);
+ return std::get<0>(ARTS_INFO[m_image_view]);
+}
+
+void menu_select_launch::set_right_panel(u8 index)
+{
+ assert(std::size(RIGHT_PANEL_NAMES) > index);
+ m_right_panel = index;
+}
- // apply texture
- container().add_quad(x1, y1, x2, y2, rgb_t::white(), m_cache.snapx_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+void menu_select_launch::set_right_image(u8 index)
+{
+ assert(std::size(ARTS_INFO) > index);
+ if (index != m_image_view)
+ {
+ m_image_view = index;
+ set_switch_image();
}
}
+void menu_select_launch::set_right_panel(std::string_view value)
+{
+ auto const found = std::find_if(
+ std::begin(RIGHT_PANEL_NAMES),
+ std::end(RIGHT_PANEL_NAMES),
+ [&value] (auto const &that) { return value == that.first; });
+ if (std::end(RIGHT_PANEL_NAMES) != found)
+ m_right_panel = found - std::begin(RIGHT_PANEL_NAMES);
+}
+
+void menu_select_launch::set_right_image(std::string_view value)
+{
+ auto const found = std::find_if(
+ std::begin(ARTS_INFO),
+ std::end(ARTS_INFO),
+ [&value] (auto const &that) { return value == std::get<0>(that); });
+ if (std::end(ARTS_INFO) != found)
+ m_image_view = found - std::begin(ARTS_INFO);
+}
+
std::string menu_select_launch::make_system_audit_fail_text(media_auditor const &auditor, media_auditor::summary summary)
{
@@ -2608,7 +3947,7 @@ bool menu_select_launch::has_multiple_bios(game_driver const &driver, s_bios &bi
for (romload::system_bios const &bios : romload::entries(driver.rom).get_system_bioses())
{
std::string name(bios.get_description());
- uint32_t const bios_flags(bios.get_value());
+ u32 const bios_flags(bios.get_value());
if (default_name && !std::strcmp(bios.get_name(), default_name))
{
@@ -2625,44 +3964,11 @@ 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)
-{
- float const aspect = machine().render().ui_aspect(&container());
- float const space = x2 - x1;
- float const lr_arrow_width = 0.4f * space * aspect;
-
- // set left-right arrows dimension
- float const ar_x0 = 0.5f * (x2 + x1) - (0.5f * lr_arrow_width);
- float const ar_y0 = 0.5f * (y2 + y1) + (0.1f * space);
- float const ar_x1 = ar_x0 + lr_arrow_width;
- float const ar_y1 = 0.5f * (y2 + y1) + (0.9f * space);
-
- 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 + ui().box_lr_border() * aspect;
-}
-
-
-//-------------------------------------------------
// draw infos
//-------------------------------------------------
-void menu_select_launch::infos_render(float origx1, float origy1, float origx2, float origy2)
+void menu_select_launch::infos_render(u32 flags)
{
- float const line_height = ui().get_line_height();
- float text_size = ui().options().infos_size();
std::string_view first;
ui_software_info const *software;
ui_system_info const *system;
@@ -2747,76 +4053,23 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2,
return;
}
- origy1 += ui().box_tb_border();
- float const aspect(machine().render().ui_aspect(&container()));
- float const gutter_width = 0.4f * line_height * aspect * 1.3f;
- float const ud_arrow_width = line_height * aspect;
- 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(0.0f);
- ui().draw_text_full(
- container(), name,
- origx1, origy1, origx2 - origx1,
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER,
- mame_ui_manager::NONE, ui().colors().text_color(), ui().colors().text_bg_color(),
- &txt_length, nullptr);
- 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, 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().create_layout(
- container(),
- 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(), text_size);
+ *ui().get_font(),
+ 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) / (line_height * text_size));
+ 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)
@@ -2825,22 +4078,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, line_height, text_size, ud_arrow_width);
+ draw_info_arrow(flags, 0);
if (down_arrow)
- draw_info_arrow(1, origx1, origx2, oy1 + (float(m_right_visible_lines - 1) * line_height * text_size), line_height, text_size, ud_arrow_width);
+ 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 ? (line_height * text_size) : 0.0f));
+ right_panel_left() + gutter_width(), m_right_content_vbounds.first + (m_topline_datsview ? m_info_line_height : 0.0f));
}
@@ -2997,7 +4247,6 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
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"));
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"));
@@ -3041,6 +4290,8 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
str << _("Media Audit\tDisabled\nSamples Audit\tDisabled\n");
}
+ util::stream_format(str, _("Source File\t%1$s\n"), info_xml_creator::format_sourcefile(driver.type.source()));
+
buffer = std::move(str).str();
}
diff --git a/src/frontend/mame/ui/selmenu.h b/src/frontend/mame/ui/selmenu.h
index 6568ca14a44..d03bae9de8f 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
@@ -39,8 +52,8 @@ public:
virtual ~menu_select_launch() override;
protected:
- static constexpr std::size_t MAX_ICONS_RENDER = 128;
- static constexpr std::size_t MAX_VISIBLE_SEARCH = 200;
+ static inline constexpr std::size_t MAX_ICONS_RENDER = 128;
+ static inline constexpr std::size_t MAX_VISIBLE_SEARCH = 200;
// tab navigation
enum class focused_menu
@@ -113,9 +126,6 @@ protected:
menu_select_launch(mame_ui_manager &mui, render_container &container, bool is_swlist);
focused_menu get_focus() const { return m_focus; }
- void set_focus(focused_menu focus) { m_focus = focus; }
- void next_image_view();
- void previous_image_view();
bool dismiss_error();
void set_error(reset_options ropt, std::string &&message);
@@ -126,7 +136,10 @@ protected:
void launch_system(game_driver const &driver, ui_software_info const &swinfo) { launch_system(ui(), driver, &swinfo, nullptr, nullptr); }
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 custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
+ virtual void custom_render(u32 flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ virtual void menu_activated() override;
+ virtual void menu_deactivated() override;
// handlers
virtual void inkey_export() = 0;
@@ -134,15 +147,10 @@ protected:
// draw arrow
void draw_common_arrow(float origx1, float origy1, float origx2, float origy2, int current, int dmin, int dmax, float title);
- void draw_info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float text_size, 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);
@@ -158,9 +166,21 @@ protected:
void *get_selection_ptr() const
{
void *const selected_ref(get_selection_ref());
- return (uintptr_t(selected_ref) > skip_main_items) ? selected_ref : m_prev_selected;
+ 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);
+ void set_right_image(u8 index);
+ void set_right_panel(std::string_view value);
+ void set_right_image(std::string_view value);
+
static std::string make_system_audit_fail_text(media_auditor const &auditor, media_auditor::summary summary);
static std::string make_software_audit_fail_text(media_auditor const &auditor, media_auditor::summary summary);
static constexpr bool audit_passed(media_auditor::summary summary)
@@ -169,7 +189,7 @@ protected:
}
int m_available_items;
- int skip_main_items;
+ int m_skip_main_items;
void *m_prev_selected;
int m_total_lines;
int m_topline_datsview;
@@ -177,11 +197,28 @@ protected:
std::string m_search;
private:
+ enum class pointer_action
+ {
+ NONE,
+ MAIN_TRACK_LINE,
+ MAIN_TRACK_RBUTTON,
+ MAIN_DRAG,
+ LEFT_TRACK_LINE,
+ LEFT_DRAG,
+ RIGHT_TRACK_TAB,
+ RIGHT_TRACK_ARROW,
+ RIGHT_TRACK_LINE,
+ RIGHT_SWITCH,
+ RIGHT_DRAG,
+ TOOLBAR_TRACK,
+ DIVIDER_TRACK
+ };
+
using bitmap_vector = std::vector<bitmap_argb32>;
using texture_ptr_vector = std::vector<texture_ptr>;
using s_parts = std::unordered_map<std::string, std::string>;
- using s_bios = std::vector<std::pair<std::string, int>>;
+ using s_bios = std::vector<std::pair<std::string, int> >;
class software_parts;
class bios_selection;
@@ -230,22 +267,48 @@ private:
using flags_cache = util::lru_cache_map<game_driver const *, system_flags>;
- void reset_pressed() { m_pressed = false; m_repeat = 0; }
- bool mouse_pressed() const { return (osd_ticks() >= m_repeat); }
- void set_pressed();
+ // various helpers for common calculations
+ bool main_at_top() const noexcept { return !top_line; }
+ bool main_at_bottom() const noexcept { return (top_line + m_primary_lines) >= m_available_items; }
+ bool is_main_up_arrow(int index) const noexcept { return !index && !main_at_top(); }
+ bool is_main_down_arrow(int index) const noexcept { return ((m_primary_lines - 1) == index) && !main_at_bottom(); }
+ bool left_at_top() const noexcept { return !m_left_visible_top; }
+ bool left_at_bottom() const noexcept { return (m_left_visible_top + m_left_visible_lines) >= m_left_item_count; }
+ bool is_left_up_arrow(int index) const noexcept { return !index && !left_at_top(); }
+ bool is_left_down_arrow(int index) const noexcept { return ((m_left_visible_lines - 1) == index) && !left_at_bottom(); }
+ bool info_at_top() const noexcept { return !m_topline_datsview; }
+ bool info_at_bottom() const noexcept { return (m_topline_datsview + m_right_visible_lines) >= m_total_lines; }
+
+ // getting precalculated geometry
+ float left_panel_left() const noexcept { return lr_border(); }
+ float left_panel_right() const noexcept { return lr_border() + m_left_panel_width; }
+ float right_panel_left() const noexcept { return 1.0F - lr_border() - m_right_panel_width; }
+ float right_panel_right() const noexcept { return 1.0F - lr_border(); }
+ float right_tab_width() const noexcept { return m_right_panel_width / float(RP_LAST - RP_FIRST + 1); }
+ float right_arrows_top() const noexcept { return m_right_heading_top + (0.1F * line_height()); }
+ float right_arrows_bottom() const noexcept { return m_right_heading_top + (0.9F * line_height()); }
+ float left_divider_left() const noexcept { return lr_border() + m_left_panel_width; }
+ float left_divider_right() const noexcept { return lr_border() + m_left_panel_width + m_divider_width; }
+ float right_divider_left() const noexcept { return 1.0F - lr_border() - m_right_panel_width - m_divider_width; }
+ float right_divider_right() const noexcept { return 1.0F - lr_border() - m_right_panel_width; }
bool snapx_valid() const { return m_cache.snapx_bitmap().valid(); }
+ void draw_divider(u32 flags, float x1, bool right);
+
// draw left panel
- virtual float draw_left_panel(float x1, float y1, float x2, float y2) = 0;
- float draw_collapsed_left_panel(float x1, float y1, float x2, float y2);
+ virtual void draw_left_panel(u32 flags) = 0;
// draw infos
- void infos_render(float x1, float y1, float x2, float y2);
+ void infos_render(u32 flags);
void general_info(ui_system_info const *system, game_driver const &driver, std::string &buffer);
// get selected software and/or driver
virtual void get_selection(ui_software_info const *&software, ui_system_info const *&system) const = 0;
+
+ // show configuration menu
+ virtual void show_config_menu(int index) = 0;
+
virtual bool accept_search() const { return true; }
void select_prev()
{
@@ -265,38 +328,65 @@ private:
}
}
}
+ void set_focus(focused_menu focus) { m_focus = focus; }
void rotate_focus(int dir);
-
- void draw_toolbar(float x1, float y1, float x2, float y2);
+ std::pair<bool, bool> next_right_panel_view();
+ std::pair<bool, bool> previous_right_panel_view();
+ std::pair<bool, bool> next_image_view();
+ std::pair<bool, bool> previous_image_view();
+ std::pair<bool, bool> next_info_view();
+ std::pair<bool, bool> previous_info_view();
+
+ void draw_toolbar(u32 flags, float x1, float y1, float x2, float y2);
void draw_star(float x0, float y0);
void draw_icon(int linenum, void *selectedref, float x1, float y1);
virtual render_texture *get_icon_texture(int linenum, void *selectedref) = 0;
- void get_title_search(std::string &title, std::string &search);
+ std::string get_arts_searchpath();
// event handling
- virtual void handle_keys(uint32_t flags, int &iptkey) override;
- virtual void handle_events(uint32_t 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(uint32_t flags) override;
+ 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);
@@ -304,6 +394,21 @@ 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 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;
@@ -314,19 +419,56 @@ private:
std::string m_info_buffer;
std::optional<text_layout> m_info_layout;
- cache &m_cache;
- bool m_is_swlist;
- focused_menu m_focus;
- bool m_pressed; // mouse button held down
- osd_ticks_t m_repeat;
-
- int m_right_visible_lines; // right box lines
-
- bool m_has_icons;
- bool m_switch_image;
- bool m_default_image;
- uint8_t m_image_view;
- flags_cache m_flags;
+ int m_icon_width;
+ int m_icon_height;
+ float m_divider_width;
+ float m_divider_arrow_width;
+ float m_divider_arrow_height;
+ float m_info_line_height;
+
+ cache &m_cache;
+ bool m_is_swlist;
+ focused_menu m_focus;
+
+ 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;
+ bool m_switch_image;
+ u8 m_image_view;
+ flags_cache m_flags;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp
index da4ad722f2f..5a0a4f1eb87 100644
--- a/src/frontend/mame/ui/selsoft.cpp
+++ b/src/frontend/mame/ui/selsoft.cpp
@@ -14,9 +14,9 @@
#include "ui/ui.h"
#include "ui/icorender.h"
#include "ui/inifile.h"
+#include "ui/miscmenu.h"
#include "ui/selector.h"
-#include "corestr.h"
#include "drivenum.h"
#include "emuopts.h"
#include "fileio.h"
@@ -25,6 +25,9 @@
#include "softlist_dev.h"
#include "uiinput.h"
#include "luaengine.h"
+
+#include "corestr.h"
+#include "path.h"
#include "unicode.h"
#include <algorithm>
@@ -85,6 +88,8 @@ public:
, m_filter_type(software_filter::ALL)
, m_swinfo()
, m_searchlist()
+ , m_right_panel(menu.right_panel())
+ , m_right_image(menu.right_image())
{
// add start empty item
m_swinfo.emplace_back(*menu.m_system.driver);
@@ -219,7 +224,8 @@ public:
}
// sort array
- std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(std::locale());
+ 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
{
@@ -345,6 +351,11 @@ public:
return m_searchlist;
}
+ u8 right_panel() const { return m_right_panel; }
+ u8 right_image() const { return m_right_image; }
+ void set_right_panel(u8 index) { m_right_panel = index; }
+ void set_right_image(u8 index) { m_right_image = index; }
+
private:
icon_cache m_icons;
bool m_has_empty_start;
@@ -354,6 +365,9 @@ private:
std::vector<ui_software_info> m_swinfo;
std::vector<search_item> m_searchlist;
+ u8 m_right_panel;
+ u8 m_right_image;
+
std::unique_ptr<std::thread> m_search_thread;
};
@@ -372,8 +386,21 @@ menu_select_software::menu_select_software(mame_ui_manager &mui, render_containe
using machine_data_cache = util::lru_cache_map<game_driver const *, std::shared_ptr<machine_data> >;
auto &cached(mui.get_session_data<menu_select_software, machine_data_cache>(8)[system.driver]);
- if (!cached)
+ if (cached)
+ {
+ // restore last right panel settings for this machine
+ set_right_panel(cached->right_panel());
+ set_right_image(cached->right_image());
+ }
+ else
+ {
+ // restore last right panel settings from UI options
+ ui_options &moptions = ui().options();
+ set_right_panel(moptions.software_right_panel());
+ set_right_image(moptions.software_right_image());
+
cached = std::make_shared<machine_data>(*this);
+ }
m_data = cached;
m_filter_highlight = m_data->filter_type();
@@ -395,73 +422,27 @@ menu_select_software::~menu_select_software()
// handle
//-------------------------------------------------
-void menu_select_software::handle(event const *ev)
+bool menu_select_software::handle(event const *ev)
{
- if (m_prev_selected == nullptr)
+ if (m_prev_selected == nullptr && item_count() > 0)
m_prev_selected = item(0).ref();
// FIXME: everything above here used run before events were processed
// process the menu
+ bool changed = false;
if (ev)
{
if (dismiss_error())
{
// reset the error on any subsequent menu event
+ changed = true;
}
else switch (ev->iptkey)
{
case IPT_UI_SELECT:
if ((get_focus() == focused_menu::MAIN) && ev->itemref)
- inkey_select(ev);
- break;
-
- case IPT_UI_LEFT:
- if (ui_globals::rpanel == RP_IMAGES)
- {
- // Images
- previous_image_view();
- }
- else if (ui_globals::rpanel == RP_INFOS && ui_globals::cur_sw_dats_view > 0)
- {
- // Infos
- ui_globals::cur_sw_dats_view--;
- m_topline_datsview = 0;
- }
- break;
-
- case IPT_UI_RIGHT:
- if (ui_globals::rpanel == RP_IMAGES)
- {
- // Images
- next_image_view();
- }
- else if (ui_globals::rpanel == 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;
- }
- break;
-
- case IPT_UI_UP:
- if ((get_focus() == focused_menu::LEFT) && (software_filter::FIRST < m_filter_highlight))
- --m_filter_highlight;
- break;
-
- case IPT_UI_DOWN:
- if ((get_focus() == focused_menu::LEFT) && (software_filter::LAST > m_filter_highlight))
- ++m_filter_highlight;
- break;
-
- case IPT_UI_HOME:
- if (get_focus() == focused_menu::LEFT)
- m_filter_highlight = software_filter::FIRST;
- break;
-
- case IPT_UI_END:
- if (get_focus() == focused_menu::LEFT)
- m_filter_highlight = software_filter::LAST;
+ changed = inkey_select(ev);
break;
case IPT_UI_DATS:
@@ -484,27 +465,54 @@ void menu_select_software::handle(event const *ev)
mfav.add_favorite_software(*swinfo);
machine().popmessage(_("%s\n added to favorites list."), swinfo->longname);
}
-
else
{
machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname);
mfav.remove_favorite_software(*swinfo);
}
+ changed = true;
}
}
}
}
}
- // if we're in an error state, overlay an error message
- draw_error_text();
+ return changed;
+}
+
+//-------------------------------------------------
+// recompute_metrics
+//-------------------------------------------------
+
+void menu_select_software::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ 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() + 3.0F * tb_border());
+}
+
+//-------------------------------------------------
+// menu_deactivated
+//-------------------------------------------------
+
+void menu_select_software::menu_deactivated()
+{
+ menu_select_launch::menu_deactivated();
+
+ // save last right panel settings
+ m_data->set_right_panel(right_panel());
+ m_data->set_right_image(right_image());
+ ui_options &mopt = ui().options();
+ mopt.set_value(OPTION_SOFTWARE_RIGHT_PANEL, right_panel_config_string(), OPTION_PRIORITY_CMDLINE);
+ mopt.set_value(OPTION_SOFTWARE_RIGHT_IMAGE, right_image_config_string(), OPTION_PRIORITY_CMDLINE);
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_select_software::populate(float &customtop, float &custombottom)
+void menu_select_software::populate()
{
for (auto &icon : m_data->icons()) // TODO: why is this here? maybe better on resize or setting change?
icon.second.texture.reset();
@@ -564,15 +572,12 @@ void menu_select_software::populate(float &customtop, float &custombottom)
m_displaylist[curitem].get().parentname.empty() ? 0 : FLAG_INVERT, (void *)&m_displaylist[curitem].get());
}
- // configure the custom rendering
- skip_main_items = 0;
- customtop = 4.0f * ui().get_line_height() + 5.0f * ui().box_tb_border();
- custombottom = 4.0f * ui().get_line_height() + 4.0f * ui().box_tb_border();
+ m_skip_main_items = 0;
if (old_software != -1)
{
set_selected_index(old_software);
- top_line = selected_index() - (ui_globals::visible_sw_lines / 2);
+ centre_selection();
}
reselect_last::reset();
@@ -583,7 +588,7 @@ void menu_select_software::populate(float &customtop, float &custombottom)
// handle select key event
//-------------------------------------------------
-void menu_select_software::inkey_select(const event *menu_event)
+bool menu_select_software::inkey_select(const event *menu_event)
{
ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref;
driver_enumerator drivlist(machine().options(), *ui_swinfo->driver);
@@ -595,6 +600,7 @@ void menu_select_software::inkey_select(const event *menu_event)
if (!audit_passed(sysaudit))
{
set_error(reset_options::REMEMBER_REF, make_system_audit_fail_text(auditor, sysaudit));
+ return true;
}
else if (ui_swinfo->startempty == 1)
{
@@ -603,6 +609,7 @@ void menu_select_software::inkey_select(const event *menu_event)
reselect_last::reselect(true);
launch_system(*ui_swinfo->driver, *ui_swinfo);
}
+ return false;
}
else
{
@@ -618,11 +625,13 @@ void menu_select_software::inkey_select(const event *menu_event)
reselect_last::reselect(true);
launch_system(drivlist.driver(), *ui_swinfo);
}
+ return false;
}
else
{
// otherwise, display an error
set_error(reset_options::REMEMBER_REF, make_software_audit_fail_text(auditor, swaudit));
+ return true;
}
}
}
@@ -632,9 +641,9 @@ void 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());
}
@@ -699,12 +708,18 @@ 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
int vis_item = !m_search.empty() ? m_available_items : (m_available_items - 1);
line0 = string_format(_("%1$s %2$s ( %3$d / %4$d software packages )"), emulator_info::get_appname(), bare_build_version, vis_item, m_data->swinfo().size() - 1);
- line1 = string_format(_("System: \"%1$s\" software list "), m_system.description);
+ line1 = string_format(_("%1$s - select software"), m_system.description);
software_filter const *const it(m_data->current_filter());
char const *const filter(it ? it->filter_text() : nullptr);
@@ -722,29 +737,28 @@ std::string menu_select_software::make_software_description(ui_software_info con
}
-void menu_select_software::filter_selected()
+void menu_select_software::filter_selected(int index)
{
- if ((software_filter::FIRST <= m_filter_highlight) && (software_filter::LAST >= m_filter_highlight))
- {
- m_data->get_filter(software_filter::type(m_filter_highlight)).show_ui(
- ui(),
- container(),
- [this] (software_filter &filter)
+ assert((software_filter::FIRST <= index) && (software_filter::LAST >= index));
+
+ m_data->get_filter(software_filter::type(index)).show_ui(
+ ui(),
+ container(),
+ [this] (software_filter &filter)
+ {
+ software_filter::type const new_type(filter.get_type());
+ if (software_filter::CUSTOM == new_type)
{
- software_filter::type const new_type(filter.get_type());
- if (software_filter::CUSTOM == new_type)
+ emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ if (!file.open(util::string_format("custom_%s_filter.ini", m_system.driver->name)))
{
- emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- if (!file.open(util::string_format("custom_%s_filter.ini", m_system.driver->name)))
- {
- filter.save_ini(file, 0);
- file.close();
- }
+ filter.save_ini(file, 0);
+ file.close();
}
- m_data->set_filter_type(new_type);
- reset(reset_options::REMEMBER_REF);
- });
- }
+ }
+ m_data->set_filter_type(new_type);
+ reset(reset_options::REMEMBER_REF);
+ });
}
} // namespace ui
diff --git a/src/frontend/mame/ui/selsoft.h b/src/frontend/mame/ui/selsoft.h
index c91495d568d..e250a608c08 100644
--- a/src/frontend/mame/ui/selsoft.h
+++ b/src/frontend/mame/ui/selsoft.h
@@ -32,6 +32,11 @@ public:
menu_select_software(mame_ui_manager &mui, render_container &container, ui_system_info const &system);
virtual ~menu_select_software() override;
+protected:
+ virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override;
+
+ virtual void menu_deactivated() override;
+
private:
using filter_map = std::map<software_filter::type, software_filter::ptr>;
using icon_cache = texture_lru<ui_software_info const *>;
@@ -39,28 +44,29 @@ private:
struct search_item;
class machine_data;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ 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; }
// handlers
- void inkey_select(const event *menu_event);
+ bool inkey_select(const event *menu_event);
std::map<std::string, std::string> m_icon_paths;
ui_system_info const &m_system;
diff --git a/src/frontend/mame/ui/simpleselgame.cpp b/src/frontend/mame/ui/simpleselgame.cpp
index 9cd8dd26fbe..354a8ad2c87 100644
--- a/src/frontend/mame/ui/simpleselgame.cpp
+++ b/src/frontend/mame/ui/simpleselgame.cpp
@@ -17,6 +17,8 @@
#include "ui/ui.h"
#include "ui/utils.h"
+#include "infoxml.h"
+
#include "audit.h"
#include "drivenum.h"
#include "emuopts.h"
@@ -25,6 +27,7 @@
#include "uiinput.h"
#include <cctype>
+#include <string_view>
namespace ui {
@@ -110,44 +113,38 @@ void simple_menu_select_game::build_driver_list()
// handle - handle the game select menu
//-------------------------------------------------
-void simple_menu_select_game::handle(event const *ev)
+bool simple_menu_select_game::handle(event const *ev)
{
- // process the menu
- if (ev)
+ if (!ev)
+ return false;
+
+ if (m_error)
{
- if (m_error)
- {
- // reset the error on any subsequent menu event
- m_error = false;
- machine().ui_input().reset();
- }
- else
- {
- // handle selections
- switch(ev->iptkey)
- {
- case IPT_UI_SELECT:
- inkey_select(*ev);
- break;
- case IPT_UI_CANCEL:
- inkey_cancel();
- break;
- case IPT_SPECIAL:
- inkey_special(*ev);
- break;
- }
- }
+ // reset the error on any subsequent menu event
+ m_error = false;
+ machine().ui_input().reset();
+ return true;
}
- // if we're in an error state, overlay an error message
- if (m_error)
+ // handle selections
+ bool changed = false;
+ switch (ev->iptkey)
{
- 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);
+ case IPT_UI_SELECT:
+ changed = inkey_select(*ev);
+ break;
+ case IPT_UI_CANCEL:
+ inkey_cancel();
+ break;
+ case IPT_UI_PASTE:
+ if (paste_text(m_search, uchar_is_printable))
+ reset(reset_options::SELECT_FIRST);
+ break;
+ case IPT_SPECIAL:
+ inkey_special(*ev);
+ break;
}
+ return changed;
}
@@ -155,7 +152,7 @@ void simple_menu_select_game::handle(event const *ev)
// inkey_select
//-------------------------------------------------
-void simple_menu_select_game::inkey_select(const event &menu_event)
+bool simple_menu_select_game::inkey_select(const event &menu_event)
{
const game_driver *driver = (const game_driver *)menu_event.itemref;
@@ -165,10 +162,12 @@ void simple_menu_select_game::inkey_select(const event &menu_event)
ui(),
container(),
[this] () { reset(reset_options::SELECT_FIRST); });
+ return false;
}
else if (!driver) // special case for previous menu
{
stack_pop();
+ return false;
}
else // anything else is a driver
{
@@ -184,12 +183,14 @@ void simple_menu_select_game::inkey_select(const event &menu_event)
mame_machine_manager::instance()->schedule_new_driver(*driver);
machine().schedule_hard_reset();
stack_reset();
+ return false;
}
else
{
// otherwise, display an error
reset(reset_options::REMEMBER_REF);
m_error = true;
+ return true;
}
}
}
@@ -218,7 +219,7 @@ void simple_menu_select_game::inkey_cancel()
void simple_menu_select_game::inkey_special(const event &menu_event)
{
// typed characters append to the buffer
- size_t old_size = m_search.size();
+ size_t const old_size = m_search.size();
if (input_character(m_search, menu_event.unichar, uchar_is_printable))
{
if (m_search.size() < old_size)
@@ -232,7 +233,7 @@ void simple_menu_select_game::inkey_special(const event &menu_event)
// populate - populate the game select menu
//-------------------------------------------------
-void simple_menu_select_game::populate(float &customtop, float &custombottom)
+void simple_menu_select_game::populate()
{
int matchcount;
int curitem;
@@ -278,10 +279,19 @@ void simple_menu_select_game::populate(float &customtop, float &custombottom)
{
item_append(_("Return to Previous Menu"), 0, nullptr);
}
+}
+
+
+//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void simple_menu_select_game::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
// configure the custom rendering
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
- custombottom = 4.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
+ set_custom_space(line_height() + 3.0f * tb_border(), 5.0f * line_height() + 3.0f * tb_border());
}
@@ -289,11 +299,11 @@ void simple_menu_select_game::populate(float &customtop, float &custombottom)
// 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(
@@ -303,114 +313,137 @@ void simple_menu_select_game::custom_render(void *selectedref, float top, float
emulator_info::get_configname(),
emulator_info::get_appname()),
text_layout::text_justify::CENTER,
- 0.5f, origy2 + ui().box_tb_border() + (0.5f * (bottom - ui().box_tb_border())),
+ 0.5f, origy2 + tb_border() + (0.5f * (bottom - tb_border())),
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 - ui().box_tb_border(),
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
-
- // 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
- std::string_view src(driver->type.source());
- auto prefix(src.find("src/mame/"));
- if (std::string_view::npos == prefix)
- prefix = src.find("src\\mame\\");
- if (std::string_view::npos != prefix)
- src.remove_prefix(prefix + 9);
- tempbuf[2] = string_format(_("Driver: %1$s"), src);
-
- // update cached values if selection changed
- if (driver != m_cached_driver)
- {
- emu_options clean_options;
- machine_static_info const info(ui().options(), machine_config(*driver, clean_options));
- m_cached_driver = driver;
- m_cached_flags = info.machine_flags();
- m_cached_unemulated = info.unemulated_features();
- m_cached_imperfect = info.imperfect_features();
- m_cached_color = info.status_color();
- }
+ std::string tempbuf[5];
- // 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");
+ // display the current typeahead
+ if (!m_search.empty())
+ tempbuf[0] = string_format(_("Type name or select: %1$s_"), m_search);
else
- tempbuf[3] = _("Overall: Working");
+ 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)
+ {
+ // first line is game name
+ tempbuf[0] = string_format(_("%1$-.100s"), driver->type.fullname());
- // 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 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()));
- // first line is version string
- tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version);
+ // update cached values if selection changed
+ if (driver != m_cached_driver)
+ {
+ emu_options clean_options;
+ machine_static_info const info(ui().options(), machine_config(*driver, clean_options));
+ m_cached_driver = driver;
+ m_cached_flags = info.machine_flags();
+ m_cached_unemulated = info.unemulated_features();
+ m_cached_imperfect = info.imperfect_features();
+ m_cached_color = info.status_color();
+ }
+
+ // next line is overall driver status
+ if (m_cached_flags & machine_flags::NOT_WORKING)
+ tempbuf[3] = _("Overall: NOT WORKING");
+ else if ((m_cached_unemulated | m_cached_imperfect) & device_t::feature::PROTECTION)
+ tempbuf[3] = _("Overall: Unemulated Protection");
+ else
+ tempbuf[3] = _("Overall: Working");
- // output message
- while (line < std::size(tempbuf))
+ // next line is graphics, sound status
+ if (m_cached_unemulated & device_t::feature::GRAPHICS)
+ tempbuf[4] = _("Graphics: Unimplemented, ");
+ else if ((m_cached_unemulated | m_cached_imperfect) & (device_t::feature::GRAPHICS | device_t::feature::PALETTE))
+ tempbuf[4] = _("Graphics: Imperfect, ");
+ else
+ tempbuf[4] = _("Graphics: OK, ");
+
+ if (m_cached_flags & machine_flags::NO_SOUND_HW)
+ tempbuf[4].append(_("Sound: None"));
+ else if (m_cached_unemulated & device_t::feature::SOUND)
+ tempbuf[4].append(_("Sound: Unimplemented"));
+ else if (m_cached_imperfect & device_t::feature::SOUND)
+ tempbuf[4].append(_("Sound: Imperfect"));
+ else
+ tempbuf[4].append(_("Sound: OK"));
+ }
+ else
{
- if (!(*s == 0 || *s == '\n'))
- tempbuf[line].push_back(*s);
+ std::string_view s = emulator_info::get_copyright();
+ unsigned line = 0;
- if (*s == '\n')
+ // first line is version string
+ tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version);
+
+ // 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());
}
- // draw the bottom box
- draw_text_box(
- tempbuf, tempbuf + 4,
- origx1, origx2, origy2 + ui().box_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(), 1.0f);
+ // 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);
+ }
+}
+
+
+//-------------------------------------------------
+// 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 e5ea310c7ec..fcfe94e6109 100644
--- a/src/frontend/mame/ui/simpleselgame.h
+++ b/src/frontend/mame/ui/simpleselgame.h
@@ -15,8 +15,10 @@
#include "menu.h"
+
class driver_enumerator;
+
namespace ui {
class simple_menu_select_game : public menu
@@ -29,18 +31,20 @@ public:
static void force_game_select(mame_ui_manager &mui, render_container &container);
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- virtual bool custom_ui_cancel() override { return !m_search.empty(); }
+ 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 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 };
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
// internal methods
void build_driver_list();
- void inkey_select(const event &menu_event);
+ bool inkey_select(const event &menu_event);
void inkey_cancel();
void inkey_special(const event &menu_event);
diff --git a/src/frontend/mame/ui/sliders.cpp b/src/frontend/mame/ui/sliders.cpp
index f4b5017397a..1cbec4e3528 100644
--- a/src/frontend/mame/ui/sliders.cpp
+++ b/src/frontend/mame/ui/sliders.cpp
@@ -39,119 +39,134 @@ menu_sliders::~menu_sliders()
// menu_sliders - handle the sliders menu
//-------------------------------------------------
-void menu_sliders::handle(event const *ev)
+bool menu_sliders::handle(event const *ev)
{
- // process the menu
- if (ev)
+ if (!ev)
+ return false;
+
+ if (ev->iptkey == IPT_UI_ON_SCREEN_DISPLAY)
{
- if (ev->iptkey == IPT_UI_ON_SCREEN_DISPLAY)
+ // toggle visibility
+ if (m_menuless_mode)
+ {
+ stack_pop();
+ return false;
+ }
+ else
+ {
+ m_hidden = !m_hidden;
+ set_process_flags(PROCESS_LR_REPEAT | (m_hidden ? PROCESS_CUSTOM_ONLY : 0));
+ return true;
+ }
+ }
+
+ // handle keys if there is a valid item selected
+ if (ev->itemref && (ev->item->type() == menu_item_type::SLIDER))
+ {
+ const slider_state *slider = (const slider_state *)ev->itemref;
+ int32_t curvalue = slider->update(nullptr, SLIDER_NOCHANGE);
+ int32_t increment = 0;
+ 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)
+ {
+ // decrease value
+ case IPT_UI_LEFT:
+ if (alt_pressed && shift_pressed)
+ increment = -1;
+ else if (alt_pressed)
+ increment = -(curvalue - slider->minval);
+ else if (shift_pressed)
+ increment = (slider->incval > 10) ? -(slider->incval / 10) : -1;
+ else if (ctrl_pressed)
+ increment = -slider->incval * 10;
+ else
+ increment = -slider->incval;
+ break;
+
+ // increase value
+ case IPT_UI_RIGHT:
+ if (alt_pressed && shift_pressed)
+ increment = 1;
+ else if (alt_pressed)
+ increment = slider->maxval - curvalue;
+ else if (shift_pressed)
+ increment = (slider->incval > 10) ? (slider->incval / 10) : 1;
+ else if (ctrl_pressed)
+ increment = slider->incval * 10;
+ else
+ increment = slider->incval;
+ break;
+
+ // restore default
+ case IPT_UI_CLEAR:
+ increment = slider->defval - curvalue;
+ break;
+ }
+
+ // handle any changes
+ if (increment != 0)
{
- // toggle visibility
+ int32_t newvalue = curvalue + increment;
+
+ // clamp within bounds
+ if (newvalue < slider->minval)
+ newvalue = slider->minval;
+ if (newvalue > slider->maxval)
+ newvalue = slider->maxval;
+
+ // update the slider and recompute the menu
+ slider->update(nullptr, newvalue);
if (m_menuless_mode)
+ ui().get_session_data<menu_sliders, void *>(nullptr) = ev->itemref;
+ reset(reset_options::REMEMBER_REF);
+ }
+
+ // slider changes trigger an item reset as they can change the available sliders
+ return false;
+ }
+
+ // when highlighting an item that isn't a slider with the menu is hidden, skip to the next one
+ if (m_hidden)
+ {
+ if (ev->iptkey == IPT_UI_UP || ev->iptkey == IPT_UI_PAGE_UP)
+ {
+ // if we got here via up or page up, select the previous item
+ if (is_first_selected())
{
- stack_pop();
+ select_last_item();
}
else
{
- m_hidden = !m_hidden;
- set_process_flags(PROCESS_LR_REPEAT | (m_hidden ? PROCESS_CUSTOM_ONLY : 0));
+ set_selected_index(selected_index() - 1);
+ validate_selection(-1);
}
-
+ return true;
}
- else if (ev->itemref && (ev->item->type() == menu_item_type::SLIDER))
+ else if (ev->iptkey == IPT_UI_DOWN || ev->iptkey == IPT_UI_PAGE_DOWN)
{
- // handle keys if there is a valid item selected
- const slider_state *slider = (const slider_state *)ev->itemref;
- int32_t curvalue = slider->update(nullptr, SLIDER_NOCHANGE);
- int32_t increment = 0;
- 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)
+ // otherwise select the next item
+ if (is_last_selected())
{
- // decrease value
- case IPT_UI_LEFT:
- if (alt_pressed && shift_pressed)
- increment = -1;
- else if (alt_pressed)
- increment = -(curvalue - slider->minval);
- else if (shift_pressed)
- increment = (slider->incval > 10) ? -(slider->incval / 10) : -1;
- else if (ctrl_pressed)
- increment = -slider->incval * 10;
- else
- increment = -slider->incval;
- break;
-
- // increase value
- case IPT_UI_RIGHT:
- if (alt_pressed && shift_pressed)
- increment = 1;
- else if (alt_pressed)
- increment = slider->maxval - curvalue;
- else if (shift_pressed)
- increment = (slider->incval > 10) ? (slider->incval / 10) : 1;
- else if (ctrl_pressed)
- increment = slider->incval * 10;
- else
- increment = slider->incval;
- break;
-
- // restore default
- case IPT_UI_SELECT:
- case IPT_UI_CLEAR:
- increment = slider->defval - curvalue;
- break;
+ select_first_item();
}
-
- // handle any changes
- if (increment != 0)
+ else
{
- int32_t newvalue = curvalue + increment;
-
- // clamp within bounds
- if (newvalue < slider->minval)
- newvalue = slider->minval;
- if (newvalue > slider->maxval)
- newvalue = slider->maxval;
-
- // update the slider and recompute the menu
- slider->update(nullptr, newvalue);
- if (m_menuless_mode)
- ui().get_session_data<menu_sliders, void *>(nullptr) = ev->itemref;
- reset(reset_options::REMEMBER_REF);
+ set_selected_index(selected_index() + 1);
+ validate_selection(1);
}
+ return true;
}
- else if (m_hidden)
+ else
{
- // if we are selecting an invalid item and we are hidden, skip to the next one
- if (ev->iptkey == IPT_UI_UP || ev->iptkey == IPT_UI_PAGE_UP)
- {
- // if we got here via up or page up, select the previous item
- if (is_first_selected())
- {
- select_last_item();
- }
- else
- {
- set_selected_index(selected_index() - 1);
- validate_selection(-1);
- }
- }
- else if (ev->iptkey == IPT_UI_DOWN || ev->iptkey == IPT_UI_PAGE_DOWN)
- {
- // otherwise select the next item
- if (is_last_selected())
- select_first_item();
- else
- {
- set_selected_index(selected_index() + 1);
- validate_selection(1);
- }
- }
+ return false;
}
}
+
+ // didn't do anything
+ return false;
}
@@ -160,7 +175,7 @@ void menu_sliders::handle(event const *ev)
// menu
//-------------------------------------------------
-void menu_sliders::populate(float &customtop, float &custombottom)
+void menu_sliders::populate()
{
std::string tempstring;
@@ -224,23 +239,31 @@ void menu_sliders::populate(float &customtop, float &custombottom)
if (ref)
set_selection(ref);
}
+}
+
+
+//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void menu_sliders::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
- custombottom = 2.0f * ui().get_line_height() + 2.0f * ui().box_tb_border();
+ set_custom_space(0.0f, 2.0f * line_height() + 2.0f * tb_border());
}
//-------------------------------------------------
-// menu_sliders_custom_render - perform our special
-// rendering
+// custom_render - perform our special rendering
//-------------------------------------------------
-void menu_sliders::custom_render(void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+void menu_sliders::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
{
const slider_state *curslider = (const slider_state *)selectedref;
if (curslider != nullptr)
{
float bar_left, bar_area_top, bar_width, bar_area_height, bar_top, bar_bottom, default_x, current_x;
- float line_height = ui().get_line_height();
float percentage, default_percentage;
std::string tempstring;
float text_height;
@@ -257,29 +280,30 @@ 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
- const float lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container());
- y2 = 1.0f - ui().box_tb_border();
+ y2 = 1.0f - tb_border();
y1 = y2 - bottom;
- x1 = lr_border;
- x2 = 1.0f - lr_border;
+ x1 = lr_border();
+ x2 = 1.0f - lr_border();
// draw extra menu area
ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
- y1 += ui().box_tb_border();
+ y1 += tb_border();
// determine the text height
ui().draw_text_full(
container(),
tempstring,
- 0, 0, x2 - x1 - 2.0f * lr_border,
+ 0, 0, x2 - x1 - 2.0f * lr_border(),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
- mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), nullptr, &text_height);
+ mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
+ nullptr, &text_height,
+ line_height());
// draw the thermometer
- bar_left = x1 + lr_border;
+ bar_left = x1 + lr_border();
bar_area_top = y1;
- bar_width = x2 - x1 - 2.0f * lr_border;
- bar_area_height = line_height;
+ bar_width = x2 - x1 - 2.0f * lr_border();
+ bar_area_height = line_height();
// compute positions
bar_top = bar_area_top + 0.125f * bar_area_height;
@@ -299,12 +323,11 @@ void menu_sliders::custom_render(void *selectedref, float top, float bottom, flo
container().add_line(default_x, bar_bottom, default_x, bar_area_top + bar_area_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
// draw the actual text
- ui().draw_text_full(
- container(),
+ draw_text_normal(
tempstring,
- x1 + lr_border, y1 + line_height, x2 - x1 - 2.0f * lr_border,
+ x1 + lr_border(), y1 + line_height(), x2 - x1 - 2.0f * lr_border(),
text_layout::text_justify::CENTER, text_layout::word_wrapping::WORD,
- mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(), nullptr, &text_height);
+ ui().colors().text_color());
}
}
diff --git a/src/frontend/mame/ui/sliders.h b/src/frontend/mame/ui/sliders.h
index 99a5c6e9cfb..b3c8ae3aca7 100644
--- a/src/frontend/mame/ui/sliders.h
+++ b/src/frontend/mame/ui/sliders.h
@@ -25,13 +25,14 @@ public:
virtual ~menu_sliders() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
bool const m_menuless_mode;
bool m_hidden;
diff --git a/src/frontend/mame/ui/slotopt.cpp b/src/frontend/mame/ui/slotopt.cpp
index 985b0fe5eb4..10fbeed03c6 100644
--- a/src/frontend/mame/ui/slotopt.cpp
+++ b/src/frontend/mame/ui/slotopt.cpp
@@ -171,7 +171,7 @@ bool menu_slot_devices::try_refresh_current_options()
// populate
//-------------------------------------------------
-void menu_slot_devices::populate(float &customtop, float &custombottom)
+void menu_slot_devices::populate()
{
// we need to keep our own copy of the machine_config because we
// can change this out from under the caller
@@ -202,9 +202,19 @@ void menu_slot_devices::populate(float &customtop, float &custombottom)
}
item_append(menu_item_type::SEPARATOR);
item_append(_("Reset System"), 0, ITEMREF_RESET);
+}
+
+
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_slot_devices::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
// leave space for the name of the current option at the bottom
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
+ set_custom_space(0.0F, line_height() + 3.0F * tb_border());
}
@@ -212,7 +222,7 @@ void menu_slot_devices::populate(float &customtop, float &custombottom)
// 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))
{
@@ -221,9 +231,9 @@ void menu_slot_devices::custom_render(void *selectedref, float top, float bottom
char const *const text[] = { option ? option->devtype().fullname() : _("[empty slot]") };
draw_text_box(
std::begin(text), std::end(text),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
}
}
@@ -232,29 +242,30 @@ void menu_slot_devices::custom_render(void *selectedref, float top, float bottom
// handle - process an input event
//-------------------------------------------------
-void menu_slot_devices::handle(event const *ev)
+bool menu_slot_devices::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref != nullptr)
+ if (!ev || !ev->itemref)
+ return false;
+
+ if (ev->itemref == ITEMREF_RESET)
{
- if (ev->itemref == ITEMREF_RESET)
- {
- if (ev->iptkey == IPT_UI_SELECT)
- machine().schedule_hard_reset();
- }
- else if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
- {
- device_slot_interface *slot = (device_slot_interface *)ev->itemref;
- rotate_slot_device(*slot, ev->iptkey == IPT_UI_LEFT ? step_t::PREVIOUS : step_t::NEXT);
- }
- else if (ev->iptkey == IPT_UI_SELECT)
- {
- device_slot_interface *slot = (device_slot_interface *)ev->itemref;
- device_slot_interface::slot_option const *const option = get_current_option(*slot);
- if (option)
- menu::stack_push<menu_device_config>(ui(), container(), slot, option);
- }
+ if (ev->iptkey == IPT_UI_SELECT)
+ machine().schedule_hard_reset();
+ }
+ else if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
+ {
+ device_slot_interface *slot = (device_slot_interface *)ev->itemref;
+ rotate_slot_device(*slot, ev->iptkey == IPT_UI_LEFT ? step_t::PREVIOUS : step_t::NEXT);
}
+ else if (ev->iptkey == IPT_UI_SELECT)
+ {
+ device_slot_interface *slot = (device_slot_interface *)ev->itemref;
+ device_slot_interface::slot_option const *const option = get_current_option(*slot);
+ if (option)
+ menu::stack_push<menu_device_config>(ui(), container(), slot, option);
+ }
+
+ return false; // any changes require the menu to be rebuilt
}
diff --git a/src/frontend/mame/ui/slotopt.h b/src/frontend/mame/ui/slotopt.h
index 449d22627aa..91c78de7d1e 100644
--- a/src/frontend/mame/ui/slotopt.h
+++ b/src/frontend/mame/ui/slotopt.h
@@ -25,6 +25,10 @@ public:
menu_slot_devices(mame_ui_manager &mui, render_container &container);
virtual ~menu_slot_devices() 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 origx1, float origy1, float origx2, float origy2) override;
+
private:
enum class step_t
{
@@ -32,9 +36,8 @@ private:
PREVIOUS
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
device_slot_interface::slot_option const *get_current_option(device_slot_interface &slot) const;
void set_slot_device(device_slot_interface &slot, std::string_view val);
diff --git a/src/frontend/mame/ui/sndmenu.cpp b/src/frontend/mame/ui/sndmenu.cpp
index 136cbcf98f4..32a590b9730 100644
--- a/src/frontend/mame/ui/sndmenu.cpp
+++ b/src/frontend/mame/ui/sndmenu.cpp
@@ -65,14 +65,13 @@ void menu_sound_options::menu_dismissed()
if (moptions.bool_value(OPTION_SAMPLES) != m_samples)
moptions.set_value(OPTION_SAMPLES, m_samples, OPTION_PRIORITY_CMDLINE);
-
}
//-------------------------------------------------
// handle
//-------------------------------------------------
-void menu_sound_options::handle(event const *ev)
+bool menu_sound_options::handle(event const *ev)
{
bool changed = false;
@@ -130,8 +129,9 @@ void menu_sound_options::handle(event const *ev)
}
}
- if (changed)
+ if (changed) // FIXME: most changes only require the item sub text to be updated
reset(reset_options::REMEMBER_REF);
+ return false;
}
@@ -139,7 +139,7 @@ void menu_sound_options::handle(event const *ev)
// populate
//-------------------------------------------------
-void menu_sound_options::populate(float &customtop, float &custombottom)
+void menu_sound_options::populate()
{
uint32_t arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(std::size(m_sound_rate) - 1), m_cur_rates);
m_sample_rate = m_sound_rate[m_cur_rates];
diff --git a/src/frontend/mame/ui/sndmenu.h b/src/frontend/mame/ui/sndmenu.h
index 48d85b7d1a5..01d69f6d8ff 100644
--- a/src/frontend/mame/ui/sndmenu.h
+++ b/src/frontend/mame/ui/sndmenu.h
@@ -38,8 +38,8 @@ private:
ENABLE_SAMPLES
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
uint16_t m_cur_rates;
static const int m_sound_rate[];
diff --git a/src/frontend/mame/ui/state.cpp b/src/frontend/mame/ui/state.cpp
index 9f4e6a40ab2..efbc9e67e98 100644
--- a/src/frontend/mame/ui/state.cpp
+++ b/src/frontend/mame/ui/state.cpp
@@ -13,6 +13,9 @@
#include "emuopts.h"
#include "inputdev.h"
+#include "uiinput.h"
+
+#include "path.h"
namespace ui {
@@ -95,6 +98,7 @@ menu_load_save_state_base::menu_load_save_state_base(
, m_confirm_delete(nullptr)
, m_must_exist(must_exist)
, m_keys_released(false)
+ , m_slot_selected(INPUT_CODE_INVALID)
{
set_one_shot(one_shot);
set_needs_prev_menu_item(!one_shot);
@@ -115,7 +119,7 @@ menu_load_save_state_base::~menu_load_save_state_base()
// populate
//-------------------------------------------------
-void menu_load_save_state_base::populate(float &customtop, float &custombottom)
+void menu_load_save_state_base::populate()
{
// build the "filename to code" map, if we have not already (if it were not for the
// possibility that the system keyboard can be changed at runtime, I would put this
@@ -221,9 +225,6 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
if (is_one_shot())
item_append(_("Cancel"), 0, nullptr);
- // set up custom render proc
- custombottom = (2.0f * ui().get_line_height()) + (3.0f * ui().box_tb_border());
-
// get ready to poll inputs
m_switch_poller.reset();
m_keys_released = false;
@@ -234,10 +235,16 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_load_save_state_base::handle(event const *ev)
+bool menu_load_save_state_base::handle(event const *ev)
{
// process the event
- if (ev && (ev->iptkey == IPT_UI_SELECT))
+ if (INPUT_CODE_INVALID != m_slot_selected)
+ {
+ if (!machine().input().code_pressed(m_slot_selected))
+ stack_pop();
+ return false;
+ }
+ else if (ev && (ev->iptkey == IPT_UI_SELECT))
{
if (ev->itemref)
{
@@ -245,10 +252,8 @@ void menu_load_save_state_base::handle(event const *ev)
file_entry const &entry = file_entry_from_itemref(ev->itemref);
slot_selected(std::string(entry.file_name()));
}
- else
- {
- stack_pop();
- }
+ stack_pop();
+ return false;
}
else if (ev && (ev->iptkey == IPT_UI_CLEAR))
{
@@ -260,15 +265,29 @@ void menu_load_save_state_base::handle(event const *ev)
_("Delete saved state %1$s?\nPress %2$s to delete\nPress %3$s to cancel"),
m_confirm_delete->visible_name(),
ui().get_general_input_setting(IPT_UI_SELECT),
- ui().get_general_input_setting(IPT_UI_CANCEL));
+ ui().get_general_input_setting(IPT_UI_BACK));
+ return true;
+ }
+ else
+ {
+ return false;
}
}
else if (!m_confirm_delete)
{
// poll inputs
- std::string name = poll_inputs();
- if (!name.empty())
- try_select_slot(std::move(name));
+ input_code code;
+ std::string name = poll_inputs(code);
+ if (!name.empty() && try_select_slot(std::move(name)))
+ {
+ m_switch_poller.reset();
+ m_slot_selected = code;
+ }
+ return false;
+ }
+ else
+ {
+ return false;
}
}
@@ -292,25 +311,32 @@ std::string menu_load_save_state_base::get_visible_name(const std::string &file_
// poll_inputs
//-------------------------------------------------
-std::string menu_load_save_state_base::poll_inputs()
+std::string menu_load_save_state_base::poll_inputs(input_code &code)
{
- input_code const code = m_switch_poller.poll();
- if (INPUT_CODE_INVALID == code)
+ input_code const result = m_switch_poller.poll();
+ if (INPUT_CODE_INVALID == result)
{
m_keys_released = true;
}
else if (m_keys_released)
{
- input_item_id const id = code.item_id();
+ input_item_id const id = result.item_id();
// keyboard A-Z and 0-9
if (((ITEM_ID_A <= id) && (ITEM_ID_Z >= id)) || ((ITEM_ID_0 <= id) && (ITEM_ID_9 >= id)))
+ {
+ code = result;
return keyboard_input_item_name(id);
+ }
// joystick buttons
- if ((DEVICE_CLASS_JOYSTICK == code.device_class()) && (ITEM_CLASS_SWITCH == code.item_class()) && (ITEM_MODIFIER_NONE == code.item_modifier()) && (ITEM_ID_BUTTON1 <= id) && (ITEM_ID_BUTTON32 >= id))
- return util::string_format("joy%i-%i", code.device_index(), id - ITEM_ID_BUTTON1 + 1);
+ if ((DEVICE_CLASS_JOYSTICK == result.device_class()) && (ITEM_CLASS_SWITCH == result.item_class()) && (ITEM_MODIFIER_NONE == result.item_modifier()) && (ITEM_ID_BUTTON1 <= id) && (ITEM_ID_BUTTON32 >= id))
+ {
+ code = result;
+ return util::string_format("joy%i-%i", result.device_index(), id - ITEM_ID_BUTTON1 + 1);
+ }
}
+ code = INPUT_CODE_INVALID;
return "";
}
@@ -319,10 +345,17 @@ std::string menu_load_save_state_base::poll_inputs()
// try_select_slot
//-------------------------------------------------
-void menu_load_save_state_base::try_select_slot(std::string &&name)
+bool menu_load_save_state_base::try_select_slot(std::string &&name)
{
if (!m_must_exist || is_present(name))
+ {
slot_selected(std::move(name));
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
@@ -337,9 +370,6 @@ void menu_load_save_state_base::slot_selected(std::string &&name)
// record the last slot touched
s_last_file_selected = std::move(name);
-
- // no matter what, pop out
- menu::stack_pop();
}
@@ -347,19 +377,18 @@ 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
- std::string const filename(util::string_format(
- "%2$s%1$s%3$s%1$s%4$s.sta",
- PATH_SEPARATOR,
+ std::string const filename(util::path_concat(
machine().options().state_directory(),
machine().get_statename(machine().options().state_name()),
- m_confirm_delete->file_name()));
+ m_confirm_delete->file_name() + ".sta"));
std::error_condition const err(osd_file::remove(filename));
if (err)
{
@@ -381,28 +410,64 @@ void menu_load_save_state_base::handle_keys(uint32_t flags, int &iptkey)
m_keys_released = false;
reset(reset_options::REMEMBER_POSITION);
}
- else if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
+ else if (exclusive_input_pressed(iptkey, IPT_UI_BACK, 0))
{
// don't delete it - dismiss the prompt
m_switch_poller.reset();
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
+//-------------------------------------------------
+
+void menu_load_save_state_base::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ autopause_menu<>::recompute_metrics(width, height, aspect);
+
+ // set up custom render proc
+ set_custom_space(0.0F, (2.0F * line_height()) + (3.0F * tb_border()));
+}
+
+
+//-------------------------------------------------
// 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);
@@ -415,7 +480,7 @@ void menu_load_save_state_base::custom_render(void *selectedref, float top, floa
if (selected_item().ref())
{
if (m_delete_prompt.empty())
- m_delete_prompt = util::string_format(_("Press %1$s to delete"), machine().input().seq_name(machine().ioport().type_seq(IPT_UI_CLEAR)));
+ m_delete_prompt = util::string_format(_("Press %1$s to delete"), ui().get_general_input_setting(IPT_UI_CLEAR));
text[count++] = m_delete_prompt;
}
@@ -424,14 +489,14 @@ void menu_load_save_state_base::custom_render(void *selectedref, float top, floa
{
draw_text_box(
std::begin(text), std::next(std::begin(text), count),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + (count * ui().get_line_height()) + (3.0f * ui().box_tb_border()),
+ origx1, origx2, origy2 + tb_border(), origy2 + (count * line_height()) + (3.0F * tb_border()),
text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
}
// draw the confirmation prompt if necessary
if (!m_confirm_prompt.empty())
- ui().draw_text_box(container(), m_confirm_prompt, text_layout::text_justify::CENTER, 0.5f, 0.5f, ui().colors().background_color());
+ ui().draw_text_box(container(), m_confirm_prompt, text_layout::text_justify::CENTER, 0.5F, 0.5F, ui().colors().background_color());
}
@@ -461,11 +526,9 @@ const menu_load_save_state_base::file_entry &menu_load_save_state_base::file_ent
std::string menu_load_save_state_base::state_directory() const
{
- const char *stateopt = machine().options().state_name();
- return util::string_format("%s%s%s",
+ return util::path_concat(
machine().options().state_directory(),
- PATH_SEPARATOR,
- machine().get_statename(stateopt));
+ machine().get_statename(machine().options().state_name()));
}
diff --git a/src/frontend/mame/ui/state.h b/src/frontend/mame/ui/state.h
index a4dc107b1fc..1bbdd1396ae 100644
--- a/src/frontend/mame/ui/state.h
+++ b/src/frontend/mame/ui/state.h
@@ -17,6 +17,7 @@
#include "iptseqpoll.h"
#include <chrono>
+#include <tuple>
#include <unordered_map>
@@ -36,10 +37,12 @@ protected:
bool must_exist,
bool one_shot);
- 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 populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ 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 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;
virtual void process_file(std::string &&file_name) = 0;
@@ -73,15 +76,16 @@ private:
file_entry const * m_confirm_delete;
bool const m_must_exist;
bool m_keys_released;
+ input_code m_slot_selected;
static void *itemref_from_file_entry(const file_entry &entry);
static const file_entry &file_entry_from_itemref(void *itemref);
- void try_select_slot(std::string &&name);
+ bool try_select_slot(std::string &&name);
void slot_selected(std::string &&name);
std::string state_directory() const;
bool is_present(const std::string &name) const;
- std::string poll_inputs();
+ std::string poll_inputs(input_code &code);
std::string get_visible_name(const std::string &file_name);
};
diff --git a/src/frontend/mame/ui/submenu.cpp b/src/frontend/mame/ui/submenu.cpp
index 4e470251b7a..cce1f28eeb2 100644
--- a/src/frontend/mame/ui/submenu.cpp
+++ b/src/frontend/mame/ui/submenu.cpp
@@ -91,6 +91,7 @@ std::vector<submenu::option> submenu::advanced_options()
{ option_type::EMU, N_("Off-screen reload"), OPTION_OFFSCREEN_RELOAD },
{ option_type::EMU, N_("Joystick deadzone"), OPTION_JOYSTICK_DEADZONE },
{ option_type::EMU, N_("Joystick saturation"), OPTION_JOYSTICK_SATURATION },
+ { option_type::EMU, N_("Joystick threshold"), OPTION_JOYSTICK_THRESHOLD },
{ option_type::EMU, N_("Natural keyboard"), OPTION_NATURAL_KEYBOARD },
{ option_type::EMU, N_("Allow contradictory joystick inputs"), OPTION_JOYSTICK_CONTRADICTORY },
{ option_type::EMU, N_("Coin impulse"), OPTION_COIN_IMPULSE } };
@@ -173,7 +174,7 @@ submenu::submenu(mame_ui_manager &mui, render_container &container, std::vector<
case option_type::EMU:
sm_option.entry = opts->get_entry(sm_option.name);
sm_option.options = opts;
- if (sm_option.entry->type() == core_options::option_type::STRING)
+ if ((sm_option.entry->type() == core_options::option_type::STRING) || (sm_option.entry->type() == core_options::option_type::PATH) || (sm_option.entry->type() == core_options::option_type::MULTIPATH))
{
sm_option.value.clear();
std::string namestr(sm_option.entry->description());
@@ -197,7 +198,7 @@ submenu::submenu(mame_ui_manager &mui, render_container &container, std::vector<
case option_type::OSD:
sm_option.entry = opts->get_entry(sm_option.name);
sm_option.options = opts;
- if (sm_option.entry->type() == core_options::option_type::STRING)
+ if ((sm_option.entry->type() == core_options::option_type::STRING) || (sm_option.entry->type() == core_options::option_type::PATH) || (sm_option.entry->type() == core_options::option_type::MULTIPATH))
{
sm_option.value.clear();
std::string descr(machine().options().get_entry(sm_option.name)->description()), delim(", ");
@@ -244,7 +245,7 @@ submenu::~submenu()
// handle the options menu
//-------------------------------------------------
-void submenu::handle(event const *ev)
+bool submenu::handle(event const *ev)
{
bool changed = false;
std::string error_string, tmptxt;
@@ -285,7 +286,7 @@ void submenu::handle(event const *ev)
const char *minimum = sm_option.entry->minimum();
const char *maximum = sm_option.entry->maximum();
f_step = atof(minimum);
- if (f_step <= 0.0f) {
+ if (f_step <= 0.0F) {
int pmin = getprecisionchr(minimum);
int pmax = getprecisionchr(maximum);
tmptxt = '1' + std::string((pmin > pmax) ? pmin : pmax, '0');
@@ -307,6 +308,8 @@ void submenu::handle(event const *ev)
}
break;
case core_options::option_type::STRING:
+ case core_options::option_type::PATH:
+ case core_options::option_type::MULTIPATH:
if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
{
changed = true;
@@ -329,15 +332,16 @@ void submenu::handle(event const *ev)
}
}
- if (changed)
+ if (changed) // FIXME: most changes should only require updating the item's subtext
reset(reset_options::REMEMBER_REF);
+ return false;
}
//-------------------------------------------------
// populate
//-------------------------------------------------
-void submenu::populate(float &customtop, float &custombottom)
+void submenu::populate()
{
// add options
for (auto sm_option = m_options.begin(); sm_option < m_options.end(); ++sm_option)
@@ -403,7 +407,7 @@ void submenu::populate(float &customtop, float &custombottom)
}
else
{
- f_min = 0.0f;
+ f_min = 0.0F;
f_max = std::numeric_limits<float>::max();
}
arrow_flags = get_arrow_flags(f_min, f_max, f_cur);
@@ -444,14 +448,24 @@ void submenu::populate(float &customtop, float &custombottom)
}
item_append(menu_item_type::SEPARATOR);
- custombottom = ui().get_line_height() + (3.0f * ui().box_tb_border());
+}
+
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void submenu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space(0.0F, line_height() + (3.0F * tb_border()));
}
//-------------------------------------------------
// 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)
{
@@ -461,9 +475,9 @@ void submenu::custom_render(void *selectedref, float top, float bottom, float or
char const *const bottomtext[] = { selected_sm_option.entry->description() };
draw_text_box(
std::begin(bottomtext), std::end(bottomtext),
- origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
+ origx1, origx2, origy2 + tb_border(), origy2 + bottom,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f);
+ ui().colors().text_color(), ui().colors().background_color());
}
}
}
diff --git a/src/frontend/mame/ui/submenu.h b/src/frontend/mame/ui/submenu.h
index fdcbfa2c7c2..80c836e67e8 100644
--- a/src/frontend/mame/ui/submenu.h
+++ b/src/frontend/mame/ui/submenu.h
@@ -60,11 +60,12 @@ public:
//static std::vector<option> export_options();
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
+ 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;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
std::vector<option> m_options;
game_driver const *const m_driver;
diff --git a/src/frontend/mame/ui/swlist.cpp b/src/frontend/mame/ui/swlist.cpp
index 70c9471da56..989147f3bb4 100644
--- a/src/frontend/mame/ui/swlist.cpp
+++ b/src/frontend/mame/ui/swlist.cpp
@@ -54,8 +54,8 @@ static bool is_valid_softlist_part_char(char32_t ch)
//-------------------------------------------------
menu_software_parts::menu_software_parts(mame_ui_manager &mui, render_container &container, const software_info *info, const char *interface, const software_part **part, bool other_opt, result &result)
- : menu(mui, container),
- m_result(result)
+ : menu(mui, container)
+ , m_result(result)
{
m_info = info;
m_interface = interface;
@@ -77,7 +77,7 @@ menu_software_parts::~menu_software_parts()
// populate
//-------------------------------------------------
-void menu_software_parts::populate(float &customtop, float &custombottom)
+void menu_software_parts::populate()
{
m_entries.clear();
@@ -124,9 +124,8 @@ void menu_software_parts::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_software_parts::handle(event const *ev)
+bool menu_software_parts::handle(event const *ev)
{
- // process the menu
if (ev && (ev->iptkey == IPT_UI_SELECT) && ev->itemref)
{
software_part_menu_entry *entry = (software_part_menu_entry *)ev->itemref;
@@ -134,6 +133,8 @@ void menu_software_parts::handle(event const *ev)
*m_selected_part = entry->part;
stack_pop();
}
+
+ return false;
}
@@ -148,6 +149,8 @@ void 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;
@@ -192,24 +195,86 @@ void menu_software_list::append_software_entry(const software_info &swinfo)
//-------------------------------------------------
+// update_search - update meunu for new search text
+//-------------------------------------------------
+
+void menu_software_list::update_search(void *selectedref)
+{
+ // display the popup
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_search);
+
+ // identify the selected entry
+ entry_info const *const cur_selected = (uintptr_t(selectedref) != 1)
+ ? reinterpret_cast<entry_info const *>(get_selection_ref())
+ : nullptr;
+
+ // if it's a perfect match for the current selection, don't move it
+ if (!cur_selected || core_strnicmp((m_ordered_by_shortname ? cur_selected->short_name : cur_selected->long_name).c_str(), m_search.c_str(), m_search.size()))
+ {
+ std::string::size_type bestmatch(0);
+ entry_info const *selected_entry(cur_selected);
+ for (auto &entry : m_entrylist)
+ {
+ // TODO: more efficient "common prefix" code
+ auto const &compare_name = m_ordered_by_shortname ? entry.short_name : entry.long_name;
+ std::string::size_type match(0);
+ for (std::string::size_type i = 1; m_search.size() >= i; ++i)
+ {
+ if (!core_strnicmp(compare_name.c_str(), m_search.c_str(), i))
+ match = i;
+ else
+ break;
+ }
+
+ if (match > bestmatch)
+ {
+ bestmatch = match;
+ selected_entry = &entry;
+ }
+ }
+
+ if (selected_entry && (selected_entry != cur_selected))
+ {
+ set_selection((void *)selected_entry);
+ centre_selection();
+ }
+ }
+}
+
+
+//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_software_list::populate(float &customtop, float &custombottom)
+void menu_software_list::populate()
{
// build up the list of entries for the menu
if (m_entrylist.empty())
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
{
- std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t>>(std::locale());
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
m_entrylist.sort(
[&coll] (entry_info const &e1, entry_info const &e2) -> bool
{
@@ -221,14 +286,11 @@ void menu_software_list::populate(float &customtop, float &custombottom)
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);
}
@@ -238,90 +300,79 @@ void menu_software_list::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_software_list::handle(event const *ev)
+bool menu_software_list::handle(event const *ev)
{
- // process the menu
- if (ev)
+ if (!ev)
{
- if (ev->iptkey == IPT_UI_SELECT)
+ return false;
+ }
+ else if (ev->iptkey == IPT_UI_SELECT)
+ {
+ if (ev->itemref == ITEMREF_SWITCH_ITEM_ORDERING)
{
- if (ev->itemref == ITEMREF_SWITCH_ITEM_ORDERING)
- {
- m_ordered_by_shortname = !m_ordered_by_shortname;
+ m_ordered_by_shortname = !m_ordered_by_shortname;
- // reset the char buffer if we change ordering criterion
- m_search.clear();
+ // reset the char buffer if we change ordering criterion
+ m_search.clear();
- // reload the menu with the new order
- reset(reset_options::REMEMBER_REF);
- machine().popmessage(
- m_ordered_by_shortname
- ? _("Switched Order: entries now ordered by shortname")
- : _("Switched Order: entries now ordered by description"));
- }
- else if (ev->itemref)
- {
- // handle selections
- entry_info *info = (entry_info *)ev->itemref;
- m_result = info->short_name;
- stack_pop();
- }
+ // reload the menu with the new order
+ reset(reset_options::REMEMBER_REF);
+ machine().popmessage(
+ m_ordered_by_shortname
+ ? _("Switched Order: entries now ordered by shortname")
+ : _("Switched Order: entries now ordered by description"));
}
- else if (ev->iptkey == IPT_SPECIAL)
+ else if (ev->itemref)
{
- if (input_character(m_search, ev->unichar, m_ordered_by_shortname ? is_valid_softlist_part_char : [] (char32_t ch) { return true; }))
- {
- // display the popup
- ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_search);
-
- // identify the selected entry
- entry_info const *const cur_selected = (uintptr_t(ev->itemref) != 1)
- ? reinterpret_cast<entry_info const *>(get_selection_ref())
- : nullptr;
-
- // if it's a perfect match for the current selection, don't move it
- if (!cur_selected || core_strnicmp((m_ordered_by_shortname ? cur_selected->short_name : cur_selected->long_name).c_str(), m_search.c_str(), m_search.size()))
- {
- std::string::size_type bestmatch(0);
- entry_info const *selected_entry(cur_selected);
- for (auto &entry : m_entrylist)
- {
- // TODO: more efficient "common prefix" code
- auto const &compare_name = m_ordered_by_shortname ? entry.short_name : entry.long_name;
- std::string::size_type match(0);
- for (std::string::size_type i = 1; m_search.size() >= i; ++i)
- {
- if (!core_strnicmp(compare_name.c_str(), m_search.c_str(), i))
- match = i;
- else
- break;
- }
-
- if (match > bestmatch)
- {
- bestmatch = match;
- selected_entry = &entry;
- }
- }
-
- if (selected_entry && (selected_entry != cur_selected))
- {
- set_selection((void *)selected_entry);
- centre_selection();
- }
- }
- }
+ // handle selections
+ entry_info *info = (entry_info *)ev->itemref;
+ m_result = info->short_name;
+ stack_pop();
}
- else if (ev->iptkey == IPT_UI_CANCEL)
+ return false;
+ }
+ else if (ev->iptkey == IPT_UI_PASTE)
+ {
+ if (paste_text(m_search, m_ordered_by_shortname ? is_valid_softlist_part_char : uchar_is_printable))
{
- // reset the char buffer also in this case
- if (!m_search.empty())
- {
- m_search.clear();
- ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_search);
- }
+ update_search(ev->itemref);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (ev->iptkey == IPT_SPECIAL)
+ {
+ if (input_character(m_search, ev->unichar, m_ordered_by_shortname ? is_valid_softlist_part_char : uchar_is_printable))
+ {
+ update_search(ev->itemref);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (ev->iptkey == IPT_UI_CANCEL)
+ {
+ // reset the char buffer also in this case
+ if (!m_search.empty())
+ {
+ m_search.clear();
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_search);
+ return true;
+ }
+ else
+ {
+ return false;
}
}
+ else
+ {
+ return false;
+ }
}
@@ -337,6 +388,8 @@ void 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;
}
@@ -355,7 +408,7 @@ menu_software::~menu_software()
// populate
//-------------------------------------------------
-void menu_software::populate(float &customtop, float &custombottom)
+void menu_software::populate()
{
bool have_compatible = false;
@@ -393,7 +446,7 @@ void menu_software::populate(float &customtop, float &custombottom)
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;
@@ -407,15 +460,16 @@ void menu_software::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_software::handle(event const *ev)
+bool menu_software::handle(event const *ev)
{
- // process the menu
if (ev && (ev->iptkey == IPT_UI_SELECT))
{
//menu::stack_push<menu_software_list>(ui(), container(), (software_list_config *)ev->itemref, image);
*m_result = reinterpret_cast<software_list_device *>(ev->itemref);
stack_pop();
}
+
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/swlist.h b/src/frontend/mame/ui/swlist.h
index 36e5d3c6eaf..891d81e054b 100644
--- a/src/frontend/mame/ui/swlist.h
+++ b/src/frontend/mame/ui/swlist.h
@@ -43,8 +43,8 @@ private:
};
using entry_list = std::list<software_part_menu_entry>;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
// variables
entry_list m_entries;
@@ -65,7 +65,7 @@ public:
virtual ~menu_software_list() override;
protected:
- virtual bool custom_ui_cancel() override { return !m_search.empty(); }
+ virtual bool custom_ui_back() override { return !m_search.empty(); }
private:
struct entry_info
@@ -88,11 +88,12 @@ private:
std::string m_search;
bool m_ordered_by_shortname;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
// functions
void append_software_entry(const software_info &swinfo);
+ void update_search(void *selectedref);
};
@@ -103,8 +104,8 @@ class menu_software : public menu
public:
menu_software(mame_ui_manager &mui, render_container &container, const char *interface, software_list_device **result);
virtual ~menu_software() override;
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
private:
const char * m_interface;
diff --git a/src/frontend/mame/ui/systemlist.cpp b/src/frontend/mame/ui/systemlist.cpp
index 5038c7e9783..d7450aac162 100644
--- a/src/frontend/mame/ui/systemlist.cpp
+++ b/src/frontend/mame/ui/systemlist.cpp
@@ -139,7 +139,8 @@ void system_list::do_cache_data(std::string const &datpath, std::string const &t
}
// sort drivers and notify
- std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(std::locale());
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
auto const compare_names =
[&coll] (std::wstring const &wx, std::wstring const &wy) -> bool
{
@@ -300,6 +301,7 @@ void system_list::populate_list(bool copydesc)
m_filter_data.add_manufacturer(driver.manufacturer);
m_filter_data.add_year(driver.year);
+ m_filter_data.add_source_file(driver.type.source());
}
}
}
diff --git a/src/frontend/mame/ui/tapectrl.cpp b/src/frontend/mame/ui/tapectrl.cpp
index 5eb505712a4..c302577cc63 100644
--- a/src/frontend/mame/ui/tapectrl.cpp
+++ b/src/frontend/mame/ui/tapectrl.cpp
@@ -56,7 +56,7 @@ menu_tape_control::~menu_tape_control()
// populate - populates the main tape control menu
//-------------------------------------------------
-void menu_tape_control::populate(float &customtop, float &custombottom)
+void menu_tape_control::populate()
{
if (current_device())
{
@@ -118,7 +118,7 @@ void menu_tape_control::populate(float &customtop, float &custombottom)
// handle - main tape control menu
//-------------------------------------------------
-void menu_tape_control::handle(event const *ev)
+bool menu_tape_control::handle(event const *ev)
{
// process the menu
if (ev)
@@ -158,6 +158,7 @@ void 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;
}
diff --git a/src/frontend/mame/ui/tapectrl.h b/src/frontend/mame/ui/tapectrl.h
index 1117e88ef5e..f03fe371dda 100644
--- a/src/frontend/mame/ui/tapectrl.h
+++ b/src/frontend/mame/ui/tapectrl.h
@@ -25,8 +25,8 @@ public:
virtual ~menu_tape_control() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ 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);
};
diff --git a/src/frontend/mame/ui/text.cpp b/src/frontend/mame/ui/text.cpp
index fa739126c36..ffbddad418f 100644
--- a/src/frontend/mame/ui/text.cpp
+++ b/src/frontend/mame/ui/text.cpp
@@ -440,7 +440,6 @@ float text_layout::actual_width()
m_calculated_actual_width = 0;
for (const auto &line : m_lines)
m_calculated_actual_width = std::max(m_calculated_actual_width, line->width());
-
}
// return it
@@ -606,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))
+ {
+ return;
+ }
+ else if (ch.source.start >= start)
{
- if (fgcolor != nullptr)
+ 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 a7fdca79f32..33319047bd8 100644
--- a/src/frontend/mame/ui/textbox.cpp
+++ b/src/frontend/mame/ui/textbox.cpp
@@ -12,89 +12,225 @@
#include "textbox.h"
#include "ui/ui.h"
-#include "ui/utils.h"
+
+#include "uiinput.h"
#include <string_view>
+#include <utility>
namespace ui {
+namespace {
+
+inline std::string_view split_column(std::string_view &line)
+{
+ auto const split = line.find('\t');
+ if (std::string::npos == split)
+ {
+ return std::exchange(line, std::string_view());
+ }
+ else
+ {
+ std::string_view result = line.substr(0, split);
+ line.remove_prefix(split + 1);
+ return result;
+ }
+}
+
+
+template <typename T, typename U, typename V>
+void populate_three_column_layout(std::string_view text, T &&l, U &&c, V &&r)
+{
+ while (!text.empty())
+ {
+ // pop a line from the front
+ auto const eol = text.find('\n');
+ std::string_view line = (std::string_view::npos != eol)
+ ? text.substr(0, eol + 1)
+ : text;
+ text.remove_prefix(line.length());
+
+ // left-justify up to the first tab
+ std::string_view const lcol = split_column(line);
+ if (!lcol.empty())
+ l(lcol);
+
+ // centre up to the second tab
+ if (!line.empty())
+ {
+ std::string_view const ccol = split_column(line);
+ if (!ccol.empty())
+ c(ccol);
+ }
+
+ // right-justify the rest
+ if (!line.empty())
+ r(line);
+ }
+}
+
+} // anonymous namespace
+
+
+
//-------------------------------------------------
-// constructor
+// menu_textbox - base text box menu class
//-------------------------------------------------
menu_textbox::menu_textbox(mame_ui_manager &mui, render_container &container)
: menu(mui, container)
, m_layout()
- , m_layout_width(-1.0f)
- , m_desired_width(-1.0f)
+ , 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)
{
}
-//-------------------------------------------------
-// destructor
-//-------------------------------------------------
-
menu_textbox::~menu_textbox()
{
}
-//-------------------------------------------------
-// reset_layout - force repopulate and scroll to
-// top
-//-------------------------------------------------
-
void menu_textbox::reset_layout()
{
- m_layout = std::nullopt;
+ // force recompute and scroll to top
+ m_layout.reset();
m_top_line = 0;
+ m_pointer_action = pointer_action::NONE;
}
-//-------------------------------------------------
-// handle_key - handle basic navigation keys
-//-------------------------------------------------
+void menu_textbox::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ m_layout.reset();
+ m_pointer_action = pointer_action::NONE;
+}
-void menu_textbox::handle_key(int key)
+
+std::tuple<int, bool, bool> menu_textbox::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- switch (key)
+ // 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))
{
- case IPT_UI_UP:
- --m_top_line;
- break;
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
- case IPT_UI_DOWN:
- ++m_top_line;
- break;
+ // 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);
+ }
- case IPT_UI_PAGE_UP:
- m_top_line -= m_window_lines - 3;
+ // handle in-progress actions
+ switch (m_pointer_action)
+ {
+ case pointer_action::NONE:
break;
- case IPT_UI_PAGE_DOWN:
- m_top_line += m_window_lines - 3;
- break;
+ 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);
+ }
- case IPT_UI_HOME:
- m_top_line = 0;
- break;
+ 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 IPT_UI_END:
- m_top_line = m_layout->lines() - m_window_lines;
- break;
+ 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);
}
-//-------------------------------------------------
-// custom_mouse_scroll - handle scroll events
-//-------------------------------------------------
-
bool menu_textbox::custom_mouse_scroll(int lines)
{
m_top_line += lines;
@@ -102,30 +238,77 @@ bool menu_textbox::custom_mouse_scroll(int lines)
}
-//-------------------------------------------------
-// draw - draw the menu
-//-------------------------------------------------
+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 aspect = machine().render().ui_aspect(&container());
- float const line_height = ui().get_line_height();
- float const ud_arrow_width = line_height * aspect;
- float const gutter_width = 0.5f * line_height * aspect;
- float const visible_width = 1.0f - (2.0f * ui().box_lr_border() * aspect);
- float const visible_left = (1.0f - visible_width) * 0.5f;
- float const extra_height = 2.0f * line_height;
+ float const visible_width = 1.0F - (2.0F * lr_border());
+ float const visible_left = (1.0F - visible_width) * 0.5F;
+ float const extra_height = 2.0F * line_height();
float const visible_extra_menu_height = get_customtop() + get_custombottom() + extra_height;
// determine effective positions
- float const maximum_width = visible_width - (2.0f * gutter_width);
+ 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 * ui().box_tb_border() - visible_extra_menu_height;
- m_window_lines = int(std::trunc(visible_main_menu_height / line_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
if (!m_layout || (m_layout_width != maximum_width))
@@ -135,104 +318,236 @@ void menu_textbox::draw(uint32_t flags)
m_layout_width = maximum_width;
}
m_window_lines = (std::min)(m_desired_lines, m_window_lines);
- visible_main_menu_height = float(m_window_lines) * line_height;
+ 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);
std::string_view const itemtext = pitem.text();
- float const itemwidth = gutter_width + ui().get_string_width(itemtext) + gutter_width;
+ float const itemwidth = gutter_width() + get_string_width(itemtext) + gutter_width();
float const draw_width = std::min(maximum_width, std::max(itemwidth, m_desired_width));
// compute text box size
- float const x1 = visible_left + ((maximum_width - draw_width) * 0.5f);
- float const y1 = visible_top - ui().box_tb_border();
- float const x2 = visible_left + visible_width - ((maximum_width - draw_width) * 0.5f);
- float const y2 = visible_top + visible_main_menu_height + ui().box_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;
+ float const x1 = visible_left + ((maximum_width - draw_width) * 0.5F);
+ float const y1 = m_visible_top - tb_border();
+ float const x2 = visible_left + visible_width - ((maximum_width - draw_width) * 0.5F);
+ float const y2 = m_visible_top + visible_main_menu_height + tb_border() + extra_height;
+ float const effective_left = x1 + gutter_width();
+ 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),
- 0.5f * (x1 + x2 + ud_arrow_width), line_y + (0.75f * line_height),
+ 0.5F * (x1 + x2 - ud_arrow_width()), line_y + (0.25F * line_height()),
+ 0.5F * (x1 + x2 + ud_arrow_width()), line_y + (0.75F * line_height()),
fgcolor, ROT0 ^ ORIENTATION_FLIP_Y);
}
// 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(
- x1, separator + (0.5f * line_height),
- x2, separator + (0.5f * line_height),
+ x1, separator + (0.5F * line_height()),
+ 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_y1 = line_y0 + line_height;
-
- if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
- set_hover(0);
+ float const line_y0 = m_visible_top + float(m_window_lines + 1) * line_height();
+ float const line_y1 = line_y0 + line_height();
- 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,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
mame_ui_manager::NORMAL,
ui().colors().selected_color(), ui().colors().selected_bg_color(),
- nullptr, nullptr);
+ nullptr, nullptr,
+ 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);
+}
+
+
+
+//-------------------------------------------------
+// menu_fixed_textbox - text box with three-
+// column content supplied at construction
+//-------------------------------------------------
+
+menu_fixed_textbox::menu_fixed_textbox(
+ mame_ui_manager &mui,
+ render_container &container,
+ std::string &&heading,
+ std::string &&content)
+ : menu_textbox(mui, container)
+ , m_heading(std::move(heading))
+ , m_content(std::move(content))
+{
+}
+
+
+menu_fixed_textbox::~menu_fixed_textbox()
+{
+}
+
+
+void menu_fixed_textbox::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu_textbox::recompute_metrics(width, height, aspect);
+
+ set_custom_space(line_height() + 3.0F * tb_border(), 0.0F);
+}
+
+
+void menu_fixed_textbox::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+ std::string_view const toptext[] = { m_heading };
+ draw_text_box(
+ std::begin(toptext), std::end(toptext),
+ x1, x2, y1 - top, y1 - tb_border(),
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
+ ui().colors().text_color(), UI_GREEN_COLOR);
+}
+
+
+void menu_fixed_textbox::populate_text(
+ std::optional<text_layout> &layout,
+ float &width,
+ int &lines)
+{
+ // ugly - use temporary layouts to compute required width
+ {
+ text_layout l(create_layout(width));
+ text_layout c(create_layout(width));
+ text_layout r(create_layout(width));
+ populate_three_column_layout(
+ m_content,
+ [&l] (std::string_view s)
+ {
+ l.add_text(s, text_layout::text_justify::LEFT);
+ if (s.back() != '\n')
+ l.add_text("\n", text_layout::text_justify::LEFT);
+ },
+ [&c] (std::string_view s) {
+ c.add_text(s, text_layout::text_justify::LEFT);
+ if (s.back() != '\n')
+ c.add_text("\n", text_layout::text_justify::LEFT);
+ },
+ [&r] (std::string_view s) {
+ r.add_text(s, text_layout::text_justify::LEFT);
+ if (s.back() != '\n')
+ r.add_text("\n", text_layout::text_justify::LEFT);
+ });
+ width = (std::min)(l.actual_width() + c.actual_width() + r.actual_width(), width);
+ }
+
+ // now do it for real
+ layout.emplace(create_layout(width));
+ rgb_t const color = ui().colors().text_color();
+ populate_three_column_layout(
+ m_content,
+ [&layout, color] (std::string_view s) { layout->add_text(s, text_layout::text_justify::LEFT, color); },
+ [&layout, color] (std::string_view s) { layout->add_text(s, text_layout::text_justify::CENTER, color); },
+ [&layout, color] (std::string_view s) { layout->add_text(s, text_layout::text_justify::RIGHT, color); });
+ lines = layout->lines();
+}
+
+
+void menu_fixed_textbox::populate()
+{
}
} // namespace ui
diff --git a/src/frontend/mame/ui/textbox.h b/src/frontend/mame/ui/textbox.h
index bdbb63f934a..3a123980c83 100644
--- a/src/frontend/mame/ui/textbox.h
+++ b/src/frontend/mame/ui/textbox.h
@@ -13,7 +13,11 @@
#include "ui/menu.h"
#include "ui/text.h"
+#include <chrono>
#include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
namespace ui {
@@ -27,21 +31,68 @@ protected:
menu_textbox(mame_ui_manager &mui, render_container &container);
void reset_layout();
- void 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;
+};
+
+
+class menu_fixed_textbox : public menu_textbox
+{
+public:
+ menu_fixed_textbox(
+ mame_ui_manager &mui,
+ render_container &container,
+ std::string &&headig,
+ std::string &&content);
+ virtual ~menu_fixed_textbox() 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 populate_text(std::optional<text_layout> &layout, float &width, int &lines) override;
+
+private:
+ virtual void populate() override;
+
+ std::string const m_heading;
+ std::string const m_content;
};
} // namespace ui
diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp
index e0c0de0d4ab..0c4af7b8ba5 100644
--- a/src/frontend/mame/ui/ui.cpp
+++ b/src/frontend/mame/ui/ui.cpp
@@ -38,10 +38,14 @@
#include "render.h"
#include "cheat.h"
#include "rendfont.h"
+#include "rendlay.h"
#include "romload.h"
#include "screen.h"
+#include "speaker.h"
#include "uiinput.h"
+// FIXME: allow OSD module headers to be included in a less ugly way
+#include "../osd/modules/lib/osdlib.h"
#include "../osd/modules/lib/osdobj_common.h"
#include <chrono>
@@ -50,18 +54,6 @@
/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-enum
-{
- LOADSAVE_NONE,
- LOADSAVE_LOAD,
- LOADSAVE_SAVE
-};
-
-
-/***************************************************************************
LOCAL VARIABLES
***************************************************************************/
@@ -162,6 +154,41 @@ 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;
+};
+
+
//-------------------------------------------------
// ctor - set up the user interface
//-------------------------------------------------
@@ -170,8 +197,8 @@ 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_param(0)
+ , m_handler_callback_type(ui_callback_type::NOINPUT)
+ , m_ui_active(true)
, m_single_step(false)
, m_showfps(false)
, m_showfps_end(0)
@@ -179,7 +206,7 @@ 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_pointers_changed(false)
, m_target_font_height(0)
, m_has_warnings(false)
, m_unthrottle_mute(false)
@@ -189,7 +216,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()
{
@@ -210,7 +238,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
{
@@ -218,9 +246,9 @@ 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",
@@ -246,6 +274,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
//-------------------------------------------------
@@ -342,6 +389,7 @@ void mame_ui_manager::config_save(config_type cfg_type, util::xml::data_node *pa
void mame_ui_manager::initialize(running_machine &machine)
{
m_machine_info = std::make_unique<ui::machine_info>(machine);
+ set_ui_active(!machine_info().has_keyboard() || machine.options().ui_active());
// initialize the on-screen display system
slider_list = slider_init(machine);
@@ -372,6 +420,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;
}
@@ -435,17 +489,17 @@ void mame_ui_manager::display_startup_screens(bool first_time)
{
// if the user cancels, exit out completely
machine().schedule_exit();
- return UI_HANDLER_CANCEL;
+ return HANDLER_CANCEL;
}
- else if (machine().ui_input().pressed(IPT_UI_CONFIGURE))
+ else if (machine().ui_input().pressed(IPT_UI_MENU))
{
config_menu = true;
- return UI_HANDLER_CANCEL;
+ return HANDLER_CANCEL;
}
else if (poller.poll() != INPUT_CODE_INVALID)
{
// if any key is pressed, just exit
- return UI_HANDLER_CANCEL;
+ return HANDLER_CANCEL;
}
return 0;
@@ -628,10 +682,14 @@ void mame_ui_manager::set_startup_text(const char *text, bool force)
// render it; called by video.c
//-------------------------------------------------
-void mame_ui_manager::update_and_render(render_container &container)
+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()))
@@ -663,8 +721,13 @@ void mame_ui_manager::update_and_render(render_container &container)
if (machine().phase() >= machine_phase::RESET)
mame_machine_manager::instance()->cheat().render_text(*this, container);
+ // draw the FPS counter if it should be visible
+ if (show_fps_counter())
+ draw_fps_counter(container);
+
// call the current UI handler
- m_handler_param = m_handler_callback(container);
+ machine().ui_input().check_ui_inputs();
+ uint32_t const handler_result = m_handler_callback(container);
// display any popup messages
if (osd_ticks() < m_popup_text_end)
@@ -672,29 +735,34 @@ void mame_ui_manager::update_and_render(render_container &container)
else
m_popup_text_end = 0;
- // display the internal mouse cursor
- 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 *mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
-
- if (mouse_target != nullptr)
+ const float cursor_size = 0.6 * get_line_height();
+ for (auto const &pointer : m_display_pointers)
{
- float mouse_y=-1,mouse_x=-1;
- if (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();
- 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));
- }
+ 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));
}
}
- // cancel takes us back to the ingame handler
- if (m_handler_param == UI_HANDLER_CANCEL)
+ // 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 pointer_update || (handler_result & HANDLER_UPDATE);
}
@@ -716,19 +784,16 @@ render_font *mame_ui_manager::get_font()
// of a line
//-------------------------------------------------
-float mame_ui_manager::get_line_height()
+float mame_ui_manager::get_line_height(float scale)
{
- int32_t raw_font_pixel_height = get_font()->pixel_height();
- render_target &ui_target = machine().render().ui_target();
- int32_t target_pixel_height = ui_target.height();
- float one_to_one_line_height;
- float scale_factor;
+ int32_t const raw_font_pixel_height = get_font()->pixel_height();
+ float target_pixel_height = machine().render().ui_target().height();
// compute the font pixel height at the nominal size
- one_to_one_line_height = (float)raw_font_pixel_height / (float)target_pixel_height;
+ float const one_to_one_line_height = float(raw_font_pixel_height) / target_pixel_height;
// determine the scale factor
- scale_factor = target_font_height() / one_to_one_line_height;
+ float scale_factor = target_font_height() * scale / one_to_one_line_height;
// if our font is small-ish, do integral scaling
if (raw_font_pixel_height < 24)
@@ -739,17 +804,17 @@ float mame_ui_manager::get_line_height()
if (one_to_one_line_height < UI_MAX_FONT_HEIGHT || raw_font_pixel_height < 12)
scale_factor = 1.0f;
}
-
- // otherwise, just ensure an integral scale factor
else
- scale_factor = floor(scale_factor);
+ {
+ // otherwise, just ensure an integral scale factor
+ scale_factor = floorf(scale_factor);
+ }
}
-
- // otherwise, just make sure we hit an even number of pixels
else
{
- int32_t height = scale_factor * one_to_one_line_height * (float)target_pixel_height;
- scale_factor = (float)height / (one_to_one_line_height * (float)target_pixel_height);
+ // otherwise, just make sure we hit an even number of pixels
+ int32_t height = scale_factor * one_to_one_line_height * target_pixel_height;
+ scale_factor = float(height) / (one_to_one_line_height * target_pixel_height);
}
return scale_factor * one_to_one_line_height;
@@ -772,9 +837,14 @@ float mame_ui_manager::get_char_width(char32_t ch)
// character string
//-------------------------------------------------
+float mame_ui_manager::get_string_width(std::string_view s)
+{
+ return get_string_width(s, get_line_height());
+}
+
float mame_ui_manager::get_string_width(std::string_view s, float text_size)
{
- return get_font()->utf8string_width(get_line_height() * text_size, machine().render().ui_aspect(), s);
+ return get_font()->utf8string_width(text_size, machine().render().ui_aspect(), s);
}
@@ -833,18 +903,37 @@ void mame_ui_manager::draw_text_full(
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)
+{
+ draw_text_full(
+ container,
+ origs,
+ x, y, origwrapwidth,
+ justify, wrap,
+ draw, fgcolor, bgcolor,
+ totalwidth, totalheight,
+ get_line_height());
+}
+
+void mame_ui_manager::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)
{
// create the layout
- auto layout = create_layout(container, origwrapwidth, justify, wrap);
+ ui::text_layout layout(
+ *get_font(), machine().render().ui_aspect(&container) * text_size, text_size,
+ origwrapwidth, justify, wrap);
// append text to it
layout.add_text(
origs,
fgcolor,
- (draw == OPAQUE_) ? bgcolor : rgb_t::transparent(),
- text_size);
+ (draw == OPAQUE_) ? bgcolor : rgb_t::transparent());
// and emit it (if we are asked to do so)
if (draw != NONE)
@@ -925,7 +1014,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);
}
@@ -937,10 +1026,7 @@ void mame_ui_manager::set_show_fps(bool show)
{
m_showfps = show;
if (!show)
- {
- m_showfps = 0;
m_showfps_end = 0;
- }
}
@@ -961,7 +1047,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;
@@ -996,17 +1082,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));
}
@@ -1015,10 +1097,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;
}
@@ -1028,48 +1109,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;
+ }
}
}
}
@@ -1116,6 +1262,7 @@ void mame_ui_manager::decrease_frameskip()
bool mame_ui_manager::can_paste()
{
// check to see if the clipboard is not empty
+ // FIXME: this is expensive - need a cheaper way to check if clipboard contains suitable content
return !osd_get_clipboard_text().empty();
}
@@ -1152,28 +1299,6 @@ void mame_ui_manager::draw_profiler(render_container &container)
//-------------------------------------------------
-// start_save_state
-//-------------------------------------------------
-
-void mame_ui_manager::start_save_state()
-{
- show_menu();
- ui::menu::stack_push<ui::menu_save_state>(*this, machine().render().ui_container(), true);
-}
-
-
-//-------------------------------------------------
-// start_load_state
-//-------------------------------------------------
-
-void mame_ui_manager::start_load_state()
-{
- show_menu();
- ui::menu::stack_push<ui::menu_load_state>(*this, machine().render().ui_container(), true);
-}
-
-
-//-------------------------------------------------
// image_handler_ingame - execute display
// callback function for each image device
//-------------------------------------------------
@@ -1213,11 +1338,10 @@ void mame_ui_manager::image_handler_ingame()
uint32_t mame_ui_manager::handler_ingame(render_container &container)
{
- bool is_paused = machine().paused();
+ // let the OSD do its thing first
+ machine().osd().check_osd_inputs();
- // first draw the FPS counter
- if (show_fps_counter())
- draw_fps_counter(container);
+ bool is_paused = machine().paused();
// draw the profiler if visible
if (show_profiler())
@@ -1231,8 +1355,8 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
}
// determine if we should disable the rest of the UI
- bool has_keyboard = machine_info().has_keyboard();
- bool ui_disabled = (has_keyboard && !machine().ui_active());
+ bool const has_keyboard = machine_info().has_keyboard();
+ bool const ui_disabled = !ui_active();
// is ScrLk UI toggling applicable here?
if (has_keyboard)
@@ -1241,20 +1365,33 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
if (machine().ui_input().pressed(IPT_UI_TOGGLE_UI))
{
// toggle the UI
- machine().set_ui_active(!machine().ui_active());
+ set_ui_active(!ui_active());
// display a popup indicating the new status
std::string const name = get_general_input_setting(IPT_UI_TOGGLE_UI);
- if (machine().ui_active())
+ if (ui_active())
popup_time(2, _("UI controls enabled\nUse %1$s to toggle"), name);
else
popup_time(2, _("UI controls disabled\nUse %1$s to toggle"), name);
}
}
- // 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());
+ for (auto const &pointer : m_active_pointers)
+ {
+ layout_view const &view(pointer.target->current_view());
+ if (view.show_pointers())
+ {
+ // TODO: make timeout configurable
+ if (!view.hide_inactive_pointers() || (osd::ui_event_handler::pointer::PEN == pointer.type) || ((now - pointer.updated) <= std::chrono::seconds(3)))
+ pointers.emplace_back(display_pointer{ *pointer.target, pointer.type, pointer.x, pointer.y });
+ }
+ }
+ set_pointers(pointers.begin(), pointers.end());
if (!ui_disabled)
{
@@ -1266,7 +1403,7 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
image_handler_ingame();
if (ui_disabled)
- return ui_disabled;
+ return 0;
if (machine().ui_input().pressed(IPT_UI_CANCEL))
{
@@ -1275,7 +1412,7 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
}
// turn on menus if requested
- if (machine().ui_input().pressed(IPT_UI_CONFIGURE))
+ if (machine().ui_input().pressed(IPT_UI_MENU))
{
show_menu();
return 0;
@@ -1285,8 +1422,8 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
if (!(machine().debug_flags & DEBUG_FLAG_ENABLED) && machine().ui_input().pressed(IPT_UI_ON_SCREEN_DISPLAY))
{
ui::menu::stack_push<ui::menu_sliders>(*this, machine().render().ui_container(), true);
- set_handler(ui_callback_type::MENU, ui::menu::get_ui_handler(*this));
- return 1;
+ show_menu();
+ return 0;
}
// handle a reset request
@@ -1298,17 +1435,23 @@ 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
{
return ui_gfx_ui_handler(container, *this, is_paused);
}));
- return is_paused ? 1 : 0;
+ return 0;
}
// handle a tape control key
@@ -1332,15 +1475,31 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
// handle a save state request
if (machine().ui_input().pressed(IPT_UI_SAVE_STATE))
{
- start_save_state();
- return LOADSAVE_SAVE;
+ ui::menu::stack_push<ui::menu_save_state>(*this, machine().render().ui_container(), true);
+ show_menu();
+ return 0;
}
// handle a load state request
if (machine().ui_input().pressed(IPT_UI_LOAD_STATE))
{
- start_load_state();
- return LOADSAVE_LOAD;
+ ui::menu::stack_push<ui::menu_load_state>(*this, machine().render().ui_container(), true);
+ show_menu();
+ 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
@@ -1365,7 +1524,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))
@@ -1425,8 +1584,8 @@ void mame_ui_manager::request_quit()
}
else
{
- show_menu();
ui::menu::stack_push<ui::menu_confirm_quit>(*this, machine().render().ui_container());
+ show_menu();
}
}
@@ -1439,7 +1598,7 @@ void mame_ui_manager::request_quit()
// ui_get_slider_list - get the list of sliders
//-------------------------------------------------
-std::vector<ui::menu_item>& mame_ui_manager::get_slider_list(void)
+std::vector<ui::menu_item> &mame_ui_manager::get_slider_list()
{
return slider_list;
}
@@ -1463,11 +1622,16 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
mixer_input info;
for (int item = 0; machine.sound().indexed_mixer_input(item, info); item++)
{
- int32_t maxval = 2000;
- int32_t defval = 1000;
-
std::string str = string_format(_("%1$s Volume"), info.stream->input(info.inputnum).name());
- slider_alloc(std::move(str), 0, defval, maxval, 20, std::bind(&mame_ui_manager::slider_mixervol, this, item, _1, _2));
+ slider_alloc(std::move(str), 0, 1000, 4000, 20, std::bind(&mame_ui_manager::slider_mixervol, this, item, _1, _2));
+ }
+
+ // 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));
}
// add analog adjusters
@@ -1477,8 +1641,7 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
{
if (field.type() == IPT_ADJUSTER)
{
- slider_alloc(field.name(), field.minval(), field.defvalue(), field.maxval(), 1,
- std::bind(&mame_ui_manager::slider_adjuster, this, std::ref(field), _1, _2));
+ slider_alloc(field.name(), field.minval(), field.defvalue(), field.maxval(), 1, std::bind(&mame_ui_manager::slider_adjuster, this, std::ref(field), _1, _2));
}
}
}
@@ -1506,10 +1669,10 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
screen_device_enumerator scriter(machine.root_device());
for (screen_device &screen : scriter)
{
- int defxscale = floor(screen.xscale() * 1000.0f + 0.5f);
- int defyscale = floor(screen.yscale() * 1000.0f + 0.5f);
- int defxoffset = floor(screen.xoffset() * 1000.0f + 0.5f);
- int defyoffset = floor(screen.yoffset() * 1000.0f + 0.5f);
+ int defxscale = floorf(screen.xscale() * 1000.0f + 0.5f);
+ int defyscale = floorf(screen.yscale() * 1000.0f + 0.5f);
+ int defxoffset = floorf(screen.xoffset() * 1000.0f + 0.5f);
+ int defyoffset = floorf(screen.yoffset() * 1000.0f + 0.5f);
std::string screen_desc = machine_info().get_screen_desc(screen);
// add refresh rate tweaker
@@ -1544,20 +1707,18 @@ std::vector<ui::menu_item> mame_ui_manager::slider_init(running_machine &machine
{
laserdisc_overlay_config config;
laserdisc.get_overlay_config(config);
- int defxscale = floor(config.m_overscalex * 1000.0f + 0.5f);
- int defyscale = floor(config.m_overscaley * 1000.0f + 0.5f);
- int defxoffset = floor(config.m_overposx * 1000.0f + 0.5f);
- int defyoffset = floor(config.m_overposy * 1000.0f + 0.5f);
+ int defxscale = floorf(config.m_overscalex * 1000.0f + 0.5f);
+ int defyscale = floorf(config.m_overscaley * 1000.0f + 0.5f);
+ int defxoffset = floorf(config.m_overposx * 1000.0f + 0.5f);
+ int defyoffset = floorf(config.m_overposy * 1000.0f + 0.5f);
// add scale and offset controls per-overlay
std::string str = string_format(_("Laserdisc '%1$s' Horiz Stretch"), laserdisc.tag());
- slider_alloc(std::move(str), 500, (defxscale == 0) ? 1000 : defxscale, 1500, 2,
- std::bind(&mame_ui_manager::slider_overxscale, this, std::ref(laserdisc), _1, _2));
+ slider_alloc(std::move(str), 500, (defxscale == 0) ? 1000 : defxscale, 1500, 2, std::bind(&mame_ui_manager::slider_overxscale, this, std::ref(laserdisc), _1, _2));
str = string_format(_("Laserdisc '%1$s' Horiz Position"), laserdisc.tag());
slider_alloc(std::move(str), -500, defxoffset, 500, 2, std::bind(&mame_ui_manager::slider_overxoffset, this, std::ref(laserdisc), _1, _2));
str = string_format(_("Laserdisc '%1$s' Vert Stretch"), laserdisc.tag());
- slider_alloc(std::move(str), 500, (defyscale == 0) ? 1000 : defyscale, 1500, 2,
- std::bind(&mame_ui_manager::slider_overyscale, this, std::ref(laserdisc), _1, _2));
+ slider_alloc(std::move(str), 500, (defyscale == 0) ? 1000 : defyscale, 1500, 2, std::bind(&mame_ui_manager::slider_overyscale, this, std::ref(laserdisc), _1, _2));
str = string_format(_("Laserdisc '%1$s' Vert Position"), laserdisc.tag());
slider_alloc(std::move(str), -500, defyoffset, 500, 2, std::bind(&mame_ui_manager::slider_overyoffset, this, std::ref(laserdisc), _1, _2));
}
@@ -1614,9 +1775,12 @@ int32_t mame_ui_manager::slider_volume(std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
machine().sound().set_attenuation(newval);
+
+ int32_t curval = machine().sound().attenuation();
if (str)
- *str = string_format(_("%1$3ddB"), machine().sound().attenuation());
- return machine().sound().attenuation();
+ *str = string_format(_(u8"%1$3d\u00a0dB"), curval);
+
+ return curval;
}
@@ -1630,15 +1794,61 @@ int32_t mame_ui_manager::slider_mixervol(int item, std::string *str, int32_t new
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);
+
+ int32_t curval = floorf(info.stream->input(info.inputnum).user_gain() * 1000.0f + 0.5f);
+ if (str)
{
- int32_t curval = floor(info.stream->input(info.inputnum).user_gain() * 1000.0f + 0.5f);
- if (newval > curval && (newval - curval) <= 4) newval += 4; // round up on increment
- info.stream->input(info.inputnum).set_user_gain((float)newval * 0.001f);
+ if (curval == 0)
+ *str = _("Mute");
+ else if (curval % 10)
+ *str = string_format(_("%1$.1f%%"), float(curval) * 0.1f);
+ else
+ *str = string_format(_("%1$3d%%"), curval / 10);
}
+
+ return curval;
+}
+
+
+//-------------------------------------------------
+// slider_panning - speaker panning slider
+// callback
+//-------------------------------------------------
+
+int32_t mame_ui_manager::slider_panning(speaker_device &speaker, std::string *str, int32_t newval)
+{
+ if (newval != SLIDER_NOCHANGE)
+ speaker.set_pan(float(newval) * 0.001f);
+
+ int32_t curval = floorf(speaker.pan() * 1000.0f + 0.5f);
if (str)
- *str = string_format("%4.2f", info.stream->input(info.inputnum).user_gain());
- return floorf(info.stream->input(info.inputnum).user_gain() * 1000.0f + 0.5f);
+ {
+ 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;
+ }
+ }
+
+ return curval;
}
@@ -1658,7 +1868,7 @@ int32_t mame_ui_manager::slider_adjuster(ioport_field &field, std::string *str,
field.set_user_settings(settings);
}
if (str)
- *str = string_format(_("%1$d%%"), settings.value);
+ *str = string_format(_("%1$3d%%"), settings.value);
return settings.value;
}
@@ -1671,10 +1881,18 @@ int32_t mame_ui_manager::slider_adjuster(ioport_field &field, std::string *str,
int32_t mame_ui_manager::slider_overclock(device_t &device, std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- device.set_clock_scale((float)newval * 0.001f);
+ device.set_clock_scale(double(newval) * 0.001);
+
+ int32_t curval = floor(device.clock_scale() * 1000.0 + 0.5);
if (str)
- *str = string_format(_("%1$3.0f%%"), floor(device.clock_scale() * 100.0 + 0.5));
- return floor(device.clock_scale() * 1000.0 + 0.5);
+ {
+ if (curval % 10)
+ *str = string_format(_("%1$.1f%%"), float(curval) * 0.1f);
+ else
+ *str = string_format(_("%1$3d%%"), curval / 10);
+ }
+
+ return curval;
}
@@ -1692,11 +1910,11 @@ int32_t mame_ui_manager::slider_refresh(screen_device &screen, std::string *str,
int width = screen.width();
int height = screen.height();
const rectangle &visarea = screen.visible_area();
- screen.configure(width, height, visarea, HZ_TO_ATTOSECONDS(defrefresh + (double)newval * 0.001));
+ screen.configure(width, height, visarea, HZ_TO_ATTOSECONDS(defrefresh + double(newval) * 0.001));
}
if (str)
- *str = string_format(_("%1$.3f" UTF8_NBSP "Hz"), screen.frame_period().as_hz());
+ *str = string_format(_(u8"%1$.3f\u00a0Hz"), screen.frame_period().as_hz());
refresh = screen.frame_period().as_hz();
return floor((refresh - defrefresh) * 1000.0 + 0.5);
}
@@ -1712,12 +1930,12 @@ int32_t mame_ui_manager::slider_brightness(screen_device &screen, std::string *s
render_container::user_settings settings = screen.container().get_user_settings();
if (newval != SLIDER_NOCHANGE)
{
- settings.m_brightness = (float)newval * 0.001f;
+ settings.m_brightness = float(newval) * 0.001f;
screen.container().set_user_settings(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_brightness);
- return floor(settings.m_brightness * 1000.0f + 0.5f);
+ return floorf(settings.m_brightness * 1000.0f + 0.5f);
}
@@ -1731,12 +1949,12 @@ int32_t mame_ui_manager::slider_contrast(screen_device &screen, std::string *str
render_container::user_settings settings = screen.container().get_user_settings();
if (newval != SLIDER_NOCHANGE)
{
- settings.m_contrast = (float)newval * 0.001f;
+ settings.m_contrast = float(newval) * 0.001f;
screen.container().set_user_settings(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_contrast);
- return floor(settings.m_contrast * 1000.0f + 0.5f);
+ return floorf(settings.m_contrast * 1000.0f + 0.5f);
}
@@ -1749,12 +1967,12 @@ int32_t mame_ui_manager::slider_gamma(screen_device &screen, std::string *str, i
render_container::user_settings settings = screen.container().get_user_settings();
if (newval != SLIDER_NOCHANGE)
{
- settings.m_gamma = (float)newval * 0.001f;
+ settings.m_gamma = float(newval) * 0.001f;
screen.container().set_user_settings(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_gamma);
- return floor(settings.m_gamma * 1000.0f + 0.5f);
+ return floorf(settings.m_gamma * 1000.0f + 0.5f);
}
@@ -1768,12 +1986,12 @@ int32_t mame_ui_manager::slider_xscale(screen_device &screen, std::string *str,
render_container::user_settings settings = screen.container().get_user_settings();
if (newval != SLIDER_NOCHANGE)
{
- settings.m_xscale = (float)newval * 0.001f;
+ settings.m_xscale = float(newval) * 0.001f;
screen.container().set_user_settings(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_xscale);
- return floor(settings.m_xscale * 1000.0f + 0.5f);
+ return floorf(settings.m_xscale * 1000.0f + 0.5f);
}
@@ -1787,12 +2005,12 @@ int32_t mame_ui_manager::slider_yscale(screen_device &screen, std::string *str,
render_container::user_settings settings = screen.container().get_user_settings();
if (newval != SLIDER_NOCHANGE)
{
- settings.m_yscale = (float)newval * 0.001f;
+ settings.m_yscale = float(newval) * 0.001f;
screen.container().set_user_settings(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_yscale);
- return floor(settings.m_yscale * 1000.0f + 0.5f);
+ return floorf(settings.m_yscale * 1000.0f + 0.5f);
}
@@ -1806,12 +2024,12 @@ int32_t mame_ui_manager::slider_xoffset(screen_device &screen, std::string *str,
render_container::user_settings settings = screen.container().get_user_settings();
if (newval != SLIDER_NOCHANGE)
{
- settings.m_xoffset = (float)newval * 0.001f;
+ settings.m_xoffset = float(newval) * 0.001f;
screen.container().set_user_settings(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_xoffset);
- return floor(settings.m_xoffset * 1000.0f + 0.5f);
+ return floorf(settings.m_xoffset * 1000.0f + 0.5f);
}
@@ -1825,12 +2043,12 @@ int32_t mame_ui_manager::slider_yoffset(screen_device &screen, std::string *str,
render_container::user_settings settings = screen.container().get_user_settings();
if (newval != SLIDER_NOCHANGE)
{
- settings.m_yoffset = (float)newval * 0.001f;
+ settings.m_yoffset = float(newval) * 0.001f;
screen.container().set_user_settings(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_yoffset);
- return floor(settings.m_yoffset * 1000.0f + 0.5f);
+ return floorf(settings.m_yoffset * 1000.0f + 0.5f);
}
@@ -1846,12 +2064,12 @@ int32_t mame_ui_manager::slider_overxscale(laserdisc_device &laserdisc, std::str
laserdisc.get_overlay_config(settings);
if (newval != SLIDER_NOCHANGE)
{
- settings.m_overscalex = (float)newval * 0.001f;
+ settings.m_overscalex = float(newval) * 0.001f;
laserdisc.set_overlay_config(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_overscalex);
- return floor(settings.m_overscalex * 1000.0f + 0.5f);
+ return floorf(settings.m_overscalex * 1000.0f + 0.5f);
}
@@ -1867,12 +2085,12 @@ int32_t mame_ui_manager::slider_overyscale(laserdisc_device &laserdisc, std::str
laserdisc.get_overlay_config(settings);
if (newval != SLIDER_NOCHANGE)
{
- settings.m_overscaley = (float)newval * 0.001f;
+ settings.m_overscaley = float(newval) * 0.001f;
laserdisc.set_overlay_config(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_overscaley);
- return floor(settings.m_overscaley * 1000.0f + 0.5f);
+ return floorf(settings.m_overscaley * 1000.0f + 0.5f);
}
@@ -1888,12 +2106,12 @@ int32_t mame_ui_manager::slider_overxoffset(laserdisc_device &laserdisc, std::st
laserdisc.get_overlay_config(settings);
if (newval != SLIDER_NOCHANGE)
{
- settings.m_overposx = (float)newval * 0.001f;
+ settings.m_overposx = float(newval) * 0.001f;
laserdisc.set_overlay_config(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_overposx);
- return floor(settings.m_overposx * 1000.0f + 0.5f);
+ return floorf(settings.m_overposx * 1000.0f + 0.5f);
}
@@ -1909,12 +2127,12 @@ int32_t mame_ui_manager::slider_overyoffset(laserdisc_device &laserdisc, std::st
laserdisc.get_overlay_config(settings);
if (newval != SLIDER_NOCHANGE)
{
- settings.m_overposy = (float)newval * 0.001f;
+ settings.m_overposy = float(newval) * 0.001f;
laserdisc.set_overlay_config(settings);
}
if (str)
*str = string_format(_("%1$.3f"), settings.m_overposy);
- return floor(settings.m_overposy * 1000.0f + 0.5f);
+ return floorf(settings.m_overposy * 1000.0f + 0.5f);
}
@@ -1926,10 +2144,10 @@ int32_t mame_ui_manager::slider_overyoffset(laserdisc_device &laserdisc, std::st
int32_t mame_ui_manager::slider_flicker([[maybe_unused]] screen_device &screen, std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- vector_options::s_flicker = (float)newval * 0.001f;
+ vector_options::s_flicker = float(newval) * 0.001f;
if (str)
*str = string_format(_("%1$1.2f"), vector_options::s_flicker);
- return floor(vector_options::s_flicker * 1000.0f + 0.5f);
+ return floorf(vector_options::s_flicker * 1000.0f + 0.5f);
}
@@ -1941,10 +2159,10 @@ int32_t mame_ui_manager::slider_flicker([[maybe_unused]] screen_device &screen,
int32_t mame_ui_manager::slider_beam_width_min([[maybe_unused]] screen_device &screen, std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- vector_options::s_beam_width_min = std::min((float)newval * 0.01f, vector_options::s_beam_width_max);
+ vector_options::s_beam_width_min = std::min(float(newval) * 0.01f, vector_options::s_beam_width_max);
if (str != nullptr)
*str = string_format(_("%1$1.2f"), vector_options::s_beam_width_min);
- return floor(vector_options::s_beam_width_min * 100.0f + 0.5f);
+ return floorf(vector_options::s_beam_width_min * 100.0f + 0.5f);
}
@@ -1956,10 +2174,10 @@ int32_t mame_ui_manager::slider_beam_width_min([[maybe_unused]] screen_device &s
int32_t mame_ui_manager::slider_beam_width_max([[maybe_unused]] screen_device &screen, std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- vector_options::s_beam_width_max = std::max((float)newval * 0.01f, vector_options::s_beam_width_min);
+ vector_options::s_beam_width_max = std::max(float(newval) * 0.01f, vector_options::s_beam_width_min);
if (str != nullptr)
*str = string_format(_("%1$1.2f"), vector_options::s_beam_width_max);
- return floor(vector_options::s_beam_width_max * 100.0f + 0.5f);
+ return floorf(vector_options::s_beam_width_max * 100.0f + 0.5f);
}
@@ -1971,10 +2189,10 @@ int32_t mame_ui_manager::slider_beam_width_max([[maybe_unused]] screen_device &s
int32_t mame_ui_manager::slider_beam_dot_size([[maybe_unused]] screen_device &screen, std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- vector_options::s_beam_dot_size = std::max((float)newval * 0.01f, 0.1f);
+ vector_options::s_beam_dot_size = std::max(float(newval) * 0.01f, 0.1f);
if (str != nullptr)
*str = string_format(_("%1$1.2f"), vector_options::s_beam_dot_size);
- return floor(vector_options::s_beam_dot_size * 100.0f + 0.5f);
+ return floorf(vector_options::s_beam_dot_size * 100.0f + 0.5f);
}
@@ -1986,10 +2204,10 @@ int32_t mame_ui_manager::slider_beam_dot_size([[maybe_unused]] screen_device &sc
int32_t mame_ui_manager::slider_beam_intensity_weight([[maybe_unused]] screen_device &screen, std::string *str, int32_t newval)
{
if (newval != SLIDER_NOCHANGE)
- vector_options::s_beam_intensity_weight = (float)newval * 0.001f;
+ vector_options::s_beam_intensity_weight = float(newval) * 0.001f;
if (str != nullptr)
*str = string_format(_("%1$1.2f"), vector_options::s_beam_intensity_weight);
- return floor(vector_options::s_beam_intensity_weight * 1000.0f + 0.5f);
+ return floorf(vector_options::s_beam_intensity_weight * 1000.0f + 0.5f);
}
@@ -2005,7 +2223,7 @@ int32_t mame_ui_manager::slider_crossscale(ioport_field &field, std::string *str
field.set_crosshair_scale(float(newval) * 0.001);
if (str)
*str = string_format((field.crosshair_axis() == CROSSHAIR_AXIS_X) ? _("Crosshair Scale X %1$1.3f") : _("Crosshair Scale Y %1$1.3f"), float(newval) * 0.001f);
- return floor(field.crosshair_scale() * 1000.0f + 0.5f);
+ return floorf(field.crosshair_scale() * 1000.0f + 0.5f);
}
#endif
@@ -2034,8 +2252,8 @@ int32_t mame_ui_manager::slider_crossoffset(ioport_field &field, std::string *st
ui::text_layout mame_ui_manager::create_layout(render_container &container, float width, ui::text_layout::text_justify justify, ui::text_layout::word_wrapping wrap)
{
// determine scale factors
- float yscale = get_line_height();
- float xscale = yscale * machine().render().ui_aspect(&container);
+ float const yscale = get_line_height();
+ float const xscale = yscale * machine().render().ui_aspect(&container);
// create the layout
return ui::text_layout(*get_font(), xscale, yscale, width, justify, wrap);
@@ -2180,6 +2398,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 e773e669cd6..d213d861f34 100644
--- a/src/frontend/mame/ui/ui.h
+++ b/src/frontend/mame/ui/ui.h
@@ -13,18 +13,23 @@
#pragma once
-#include "render.h"
-#include "moptions.h"
#include "language.h"
#include "ui/uimain.h"
#include "ui/menuitem.h"
+#include "ui/moptions.h"
#include "ui/slider.h"
#include "ui/text.h"
+#include "render.h"
+
+#include "interface/uievents.h"
+
#include <any>
#include <cassert>
#include <ctime>
+#include <functional>
#include <set>
+#include <string>
#include <string_view>
#include <typeindex>
#include <typeinfo>
@@ -57,23 +62,12 @@ class laserdisc_device;
#define UI_YELLOW_COLOR rgb_t(0xef,0xcc,0x7a,0x28)
#define UI_RED_COLOR rgb_t(0xef,0xb2,0x00,0x00)
-/* cancel return value for a UI handler */
-#define UI_HANDLER_CANCEL ((uint32_t)~0)
-
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
class mame_ui_manager;
-enum class ui_callback_type
-{
- GENERAL,
- MODAL,
- MENU,
- VIEWER
-};
-
// ======================> ui_colors
class ui_colors
@@ -122,6 +116,12 @@ private:
class mame_ui_manager : public ui_manager
{
public:
+ enum : uint32_t
+ {
+ HANDLER_UPDATE = 1U << 0, // force video update
+ HANDLER_CANCEL = 1U << 1 // return to in-game event handler
+ };
+
enum draw_mode
{
NONE,
@@ -129,6 +129,18 @@ public:
OPAQUE_
};
+ struct display_pointer
+ {
+ std::reference_wrapper<render_target> target;
+ osd::ui_event_handler::pointer type;
+ float x, y;
+
+ bool operator!=(display_pointer const &that) const noexcept
+ {
+ return (&target.get() != &that.target.get()) || (type != that.type) || (x != that.x) || (y != that.y);
+ }
+ };
+
// construction/destruction
mame_ui_manager(running_machine &machine);
~mame_ui_manager();
@@ -151,15 +163,17 @@ public:
void display_startup_screens(bool first_time);
virtual void set_startup_text(const char *text, bool force) override;
- void update_and_render(render_container &container);
+ bool update_and_render(render_container &container);
render_font *get_font();
- float get_line_height();
+ float get_line_height(float scale = 1.0F);
float get_char_width(char32_t ch);
- float get_string_width(std::string_view s, float text_size = 1.0f);
+ float get_string_width(std::string_view s);
+ float get_string_width(std::string_view s, float text_size);
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_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, float text_size = 1.0f);
+ 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);
void draw_text_box(render_container &container, std::string_view text, ui::text_layout::text_justify justify, float xpos, float ypos, rgb_t backcolor);
void draw_text_box(render_container &container, ui::text_layout &layout, float xpos, float ypos, rgb_t backcolor);
void draw_message_window(render_container &container, std::string_view text);
@@ -170,6 +184,8 @@ public:
void save_main_option();
template <typename Format, typename... Params> void popup_time(int seconds, Format &&fmt, Params &&... args);
+ void set_ui_active(bool active) { m_ui_active = active; }
+ bool ui_active() const { return m_ui_active; }
void show_fps_temp(double seconds);
void set_show_fps(bool show);
bool show_fps() const;
@@ -177,7 +193,6 @@ public:
void set_show_profiler(bool show);
bool show_profiler() const;
void show_menu();
- void show_mouse(bool status);
virtual bool is_menu_active() override;
bool can_paste();
bool found_machine_warnings() const { return m_has_warnings; }
@@ -187,11 +202,40 @@ public:
void request_quit();
void draw_fps_counter(render_container &container);
void draw_profiler(render_container &container);
- void start_save_state();
- void start_load_state();
+
+ // pointer display
+ template <typename T>
+ void set_pointers(T first, T last)
+ {
+ auto dest = m_display_pointers.begin();
+ while ((m_display_pointers.end() != dest) && (first != last))
+ {
+ if (*first != *dest)
+ {
+ *dest = *first;
+ m_pointers_changed = true;
+ }
+ ++dest;
+ ++first;
+ }
+ if (m_display_pointers.end() != dest)
+ {
+ m_display_pointers.erase(dest, m_display_pointers.end());
+ m_pointers_changed = true;
+ }
+ else
+ {
+ while (first != last)
+ {
+ m_display_pointers.emplace_back(*first);
+ m_pointers_changed = true;
+ ++first;
+ }
+ }
+ }
// slider controls
- std::vector<ui::menu_item>& get_slider_list(void);
+ std::vector<ui::menu_item> &get_slider_list();
// metrics
float target_font_height() const { return m_target_font_height; }
@@ -200,7 +244,7 @@ public:
void update_target_font_height();
// other
- void process_natural_keyboard();
+ void process_ui_events();
ui::text_layout create_layout(render_container &container, float width = 1.0, ui::text_layout::text_justify justify = ui::text_layout::text_justify::LEFT, ui::text_layout::word_wrapping wrap = ui::text_layout::word_wrapping::WORD);
void set_image_display_enabled(bool image_display_enabled) { m_image_display_enabled = image_display_enabled; }
bool image_display_enabled() const { return m_image_display_enabled; }
@@ -210,6 +254,7 @@ public:
virtual void popup_time_string(int seconds, std::string message) override;
virtual void menu_reset() override;
+ virtual bool set_ui_event_handler(std::function<bool ()> &&handler) override;
template <typename Owner, typename Data, typename... Param>
Data &get_session_data(Param &&... args)
@@ -227,24 +272,34 @@ public:
std::string get_general_input_setting(ioport_type type, int player = 0, input_seq_type seqtype = SEQ_TYPE_STANDARD);
private:
+ enum class ui_callback_type : int;
+
+ struct active_pointer;
+
using handler_callback_func = delegate<uint32_t (render_container &)>;
using device_feature_set = std::set<std::pair<std::string, std::string> >;
using session_data_map = std::unordered_map<std::type_index, std::any>;
+ using active_pointer_vector = std::vector<active_pointer>;
+ using display_pointer_vector = std::vector<display_pointer>;
// instance variables
std::unique_ptr<render_font> m_font;
handler_callback_func m_handler_callback;
ui_callback_type m_handler_callback_type;
- uint32_t m_handler_param;
+ bool m_ui_active;
bool m_single_step;
bool m_showfps;
osd_ticks_t m_showfps_end;
bool m_show_profiler;
osd_ticks_t m_popup_text_end;
std::unique_ptr<uint8_t []> m_non_char_keys_down;
+
+ active_pointer_vector m_active_pointers;
+ display_pointer_vector m_display_pointers;
bitmap_argb32 m_mouse_bitmap;
render_texture * m_mouse_arrow_texture;
- bool m_mouse_show;
+ bool m_pointers_changed;
+
ui_options m_ui_options;
ui_colors m_ui_colors;
float m_target_font_height;
@@ -271,6 +326,7 @@ private:
// private methods
void set_handler(ui_callback_type callback_type, handler_callback_func &&callback);
+ void frame_update();
void exit();
void config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
void config_save(config_type cfg_type, util::xml::data_node *parentnode);
@@ -279,6 +335,7 @@ private:
// 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_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 6c399f9c924..b0107589e04 100644
--- a/src/frontend/mame/ui/utils.cpp
+++ b/src/frontend/mame/ui/utils.cpp
@@ -14,6 +14,7 @@
#include "ui/inifile.h"
#include "ui/selector.h"
+#include "infoxml.h"
#include "language.h"
#include "mame.h"
@@ -96,6 +97,7 @@ constexpr char const *MACHINE_FILTER_NAMES[machine_filter::COUNT] = {
N_p("machine-filter", "Clones"),
N_p("machine-filter", "Manufacturer"),
N_p("machine-filter", "Year"),
+ N_p("machine-filter", "Source File"),
N_p("machine-filter", "Save Supported"),
N_p("machine-filter", "Save Unsupported"),
N_p("machine-filter", "CHD Required"),
@@ -202,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(); }
@@ -403,8 +398,8 @@ private:
ADD_FILTER
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
bool set_filter_type(unsigned pos, typename Base::type n)
{
@@ -529,7 +524,7 @@ void composite_filter_impl_base<Impl, Base, Type>::show_ui(
template <class Impl, class Base, typename Base::type Type>
-void composite_filter_impl_base<Impl, Base, Type>::menu_configure::populate(float &customtop, float &custombottom)
+void composite_filter_impl_base<Impl, Base, Type>::menu_configure::populate()
{
// add items for each active filter
unsigned i = 0;
@@ -556,110 +551,111 @@ void composite_filter_impl_base<Impl, Base, Type>::menu_configure::populate(floa
}
template <class Impl, class Base, typename Base::type Type>
-void composite_filter_impl_base<Impl, Base, Type>::menu_configure::handle(event const *ev)
+bool composite_filter_impl_base<Impl, Base, Type>::menu_configure::handle(event const *ev)
{
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ m_added = false;
+ bool changed(false);
+ uintptr_t const ref(reinterpret_cast<uintptr_t>(ev->itemref));
+ switch (ev->iptkey)
{
- m_added = false;
- bool changed(false);
- uintptr_t const ref(reinterpret_cast<uintptr_t>(ev->itemref));
- switch (ev->iptkey)
+ case IPT_UI_LEFT:
+ case IPT_UI_RIGHT:
+ if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref))
{
- case IPT_UI_LEFT:
- case IPT_UI_RIGHT:
- if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref))
+ // change filter type
+ unsigned const pos(ref - FILTER_FIRST);
+ typename Base::type const current(m_parent.m_filters[pos]->get_type());
+ if (IPT_UI_LEFT == ev->iptkey)
{
- // change filter type
- unsigned const pos(ref - FILTER_FIRST);
- typename Base::type const current(m_parent.m_filters[pos]->get_type());
- if (IPT_UI_LEFT == ev->iptkey)
+ typename Base::type n(current);
+ while ((Base::FIRST < n) && !changed)
{
- typename Base::type n(current);
- while ((Base::FIRST < n) && !changed)
- {
- if (m_parent.check_type(pos, --n))
- changed = set_filter_type(pos, n);
- }
- }
- else
- {
- typename Base::type n(current);
- while ((Base::LAST > n) && !changed)
- {
- if (m_parent.check_type(pos, ++n))
- changed = set_filter_type(pos, n);
- }
+ if (m_parent.check_type(pos, --n))
+ changed = set_filter_type(pos, n);
}
}
- else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref))
+ else
{
- // change filter value
- Base &pos(*m_parent.m_filters[ref - ADJUST_FIRST]);
- changed = (IPT_UI_LEFT == ev->iptkey) ? pos.adjust_left() : pos.adjust_right();
+ typename Base::type n(current);
+ while ((Base::LAST > n) && !changed)
+ {
+ if (m_parent.check_type(pos, ++n))
+ changed = set_filter_type(pos, n);
+ }
}
- break;
+ }
+ else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref))
+ {
+ // change filter value
+ Base &pos(*m_parent.m_filters[ref - ADJUST_FIRST]);
+ changed = (IPT_UI_LEFT == ev->iptkey) ? pos.adjust_left() : pos.adjust_right();
+ }
+ break;
- case IPT_UI_SELECT:
- if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref))
+ case IPT_UI_SELECT:
+ if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref))
+ {
+ // show selector with non-contradictory types
+ std::vector<typename Base::type> types;
+ std::vector<std::string> names;
+ types.reserve(Base::COUNT);
+ names.reserve(Base::COUNT);
+ int sel(-1);
+ unsigned const pos(ref - FILTER_FIRST);
+ typename Base::type const current(m_parent.m_filters[pos]->get_type());
+ for (typename Base::type candidate = Base::FIRST; Base::COUNT > candidate; ++candidate)
{
- // show selector with non-contradictory types
- std::vector<typename Base::type> types;
- std::vector<std::string> names;
- types.reserve(Base::COUNT);
- names.reserve(Base::COUNT);
- int sel(-1);
- unsigned const pos(ref - FILTER_FIRST);
- typename Base::type const current(m_parent.m_filters[pos]->get_type());
- for (typename Base::type candidate = Base::FIRST; Base::COUNT > candidate; ++candidate)
+ if (Impl::type_allowed(pos, candidate))
{
- if (Impl::type_allowed(pos, candidate))
+ if (current == candidate)
+ sel = types.size();
+ unsigned i = 0;
+ while ((MAX > i) && m_parent.m_filters[i] && ((pos == i) || !Impl::types_contradictory(m_parent.m_filters[i]->get_type(), candidate)))
+ ++i;
+ if ((MAX <= i) || !m_parent.m_filters[i])
{
- if (current == candidate)
- sel = types.size();
- unsigned i = 0;
- while ((MAX > i) && m_parent.m_filters[i] && ((pos == i) || !Impl::types_contradictory(m_parent.m_filters[i]->get_type(), candidate)))
- ++i;
- if ((MAX <= i) || !m_parent.m_filters[i])
- {
- types.emplace_back(candidate);
- names.emplace_back(Base::display_name(candidate));
- }
+ types.emplace_back(candidate);
+ names.emplace_back(Base::display_name(candidate));
}
}
- menu::stack_push<menu_selector>(
- ui(),
- container(),
- std::string(ev->item->text()),
- std::move(names),
- sel,
- [this, pos, t = std::move(types)] (int selection)
- {
- if (set_filter_type(pos, t[selection]))
- reset(reset_options::REMEMBER_REF);
- });
}
- else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref))
- {
- // show selected filter's UI
- m_parent.m_filters[ref - ADJUST_FIRST]->show_ui(ui(), container(), [this] (Base &filter) { reset(reset_options::REMEMBER_REF); });
- }
- else if (REMOVE_FILTER == ref)
- {
- changed = drop_last_filter();
- }
- else if (ADD_FILTER == ref)
- {
- m_added = append_filter();
- }
- break;
+ menu::stack_push<menu_selector>(
+ ui(),
+ container(),
+ std::string(ev->item->text()),
+ std::move(names),
+ sel,
+ [this, pos, t = std::move(types)] (int selection)
+ {
+ if (set_filter_type(pos, t[selection]))
+ reset(reset_options::REMEMBER_REF);
+ });
}
-
- // rebuild if anything changed
- if (changed)
- reset(reset_options::REMEMBER_REF);
- else if (m_added)
- reset(reset_options::SELECT_FIRST);
+ else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref))
+ {
+ // show selected filter's UI
+ m_parent.m_filters[ref - ADJUST_FIRST]->show_ui(ui(), container(), [this] (Base &filter) { reset(reset_options::REMEMBER_REF); });
+ }
+ else if (REMOVE_FILTER == ref)
+ {
+ changed = drop_last_filter();
+ }
+ else if (ADD_FILTER == ref)
+ {
+ m_added = append_filter();
+ }
+ break;
}
+
+ // rebuild if anything changed
+ if (changed)
+ reset(reset_options::REMEMBER_REF);
+ else if (m_added)
+ reset(reset_options::SELECT_FIRST);
+ return false;
}
@@ -799,6 +795,18 @@ public:
};
+class source_file_machine_filter : public choice_filter_impl_base<machine_filter, machine_filter::SOURCE_FILE>
+{
+public:
+ source_file_machine_filter(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
+ : choice_filter_impl_base<machine_filter, machine_filter::SOURCE_FILE>(data.source_files(), value)
+ {
+ }
+
+ virtual bool apply(ui_system_info const &system) const override { return !have_choices() || (selection_valid() && (selection_text() == info_xml_creator::format_sourcefile(system.driver->type.source()))); }
+};
+
+
//-------------------------------------------------
// complementary machine filters
@@ -995,8 +1003,8 @@ private:
INCLUDE_CLONES
};
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
category_machine_filter &m_parent;
std::function<void (machine_filter &)> m_handler;
@@ -1028,9 +1036,10 @@ private:
}
}
- static bool include_clones_default(std::string const &name)
+ static bool include_clones_default(std::string_view name)
{
- return !core_stricmp(name.c_str(), "category.ini") || !core_stricmp(name.c_str(), "alltime.ini");
+ using namespace std::literals;
+ return util::streqlower(name, "category.ini"sv) || util::streqlower(name, "alltime.ini"sv);
}
unsigned m_ini, m_group;
@@ -1046,7 +1055,7 @@ void category_machine_filter::show_ui(mame_ui_manager &mui, render_container &co
}
-void category_machine_filter::menu_configure::populate(float &customtop, float &custombottom)
+void category_machine_filter::menu_configure::populate()
{
inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
unsigned const filecnt(mgr.get_file_count());
@@ -1073,105 +1082,106 @@ void category_machine_filter::menu_configure::populate(float &customtop, float &
item_append(menu_item_type::SEPARATOR);
}
-void category_machine_filter::menu_configure::handle(event const *ev)
+bool category_machine_filter::menu_configure::handle(event const *ev)
{
- if (ev && ev->itemref)
+ if (!ev || !ev->itemref)
+ return false;
+
+ bool changed(false);
+ uintptr_t const ref(reinterpret_cast<uintptr_t>(ev->itemref));
+ inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
+ switch (ev->iptkey)
{
- bool changed(false);
- uintptr_t const ref(reinterpret_cast<uintptr_t>(ev->itemref));
- inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
- switch (ev->iptkey)
+ case IPT_UI_LEFT:
+ if ((INI_FILE == ref) && m_ini)
{
- case IPT_UI_LEFT:
- if ((INI_FILE == ref) && m_ini)
- {
- --m_ini;
- changed = true;
- }
- else if ((SYSTEM_GROUP == ref) && m_state[m_ini].first)
- {
- --m_state[m_ini].first;
- changed = true;
- }
- else if ((INCLUDE_CLONES == ref) && m_state[m_ini].second)
- {
- m_state[m_ini].second = false;
- changed = true;
- }
- break;
- case IPT_UI_RIGHT:
- if ((INI_FILE == ref) && (mgr.get_file_count() > (m_ini + 1)))
- {
- ++m_ini;
- changed = true;
- }
- else if ((SYSTEM_GROUP == ref) && (mgr.get_category_count(m_ini) > (m_state[m_ini].first + 1)))
- {
- ++m_state[m_ini].first;
- changed = true;
- }
- else if ((INCLUDE_CLONES == ref) && !m_state[m_ini].second)
- {
- m_state[m_ini].second = true;
- changed = true;
- }
- break;
+ --m_ini;
+ changed = true;
+ }
+ else if ((SYSTEM_GROUP == ref) && m_state[m_ini].first)
+ {
+ --m_state[m_ini].first;
+ changed = true;
+ }
+ else if ((INCLUDE_CLONES == ref) && m_state[m_ini].second)
+ {
+ m_state[m_ini].second = false;
+ changed = true;
+ }
+ break;
+ case IPT_UI_RIGHT:
+ if ((INI_FILE == ref) && (mgr.get_file_count() > (m_ini + 1)))
+ {
+ ++m_ini;
+ changed = true;
+ }
+ else if ((SYSTEM_GROUP == ref) && (mgr.get_category_count(m_ini) > (m_state[m_ini].first + 1)))
+ {
+ ++m_state[m_ini].first;
+ changed = true;
+ }
+ else if ((INCLUDE_CLONES == ref) && !m_state[m_ini].second)
+ {
+ m_state[m_ini].second = true;
+ changed = true;
+ }
+ break;
- case IPT_UI_SELECT:
- if (INI_FILE == ref)
- {
- std::vector<std::string> choices;
- choices.reserve(mgr.get_file_count());
- for (size_t i = 0; mgr.get_file_count() > i; ++i)
- choices.emplace_back(mgr.get_file_name(i));
- menu::stack_push<menu_selector>(
- ui(),
- container(),
- _("Category File"),
- std::move(choices),
- m_ini,
- [this] (int selection)
+ case IPT_UI_SELECT:
+ if (INI_FILE == ref)
+ {
+ std::vector<std::string> choices;
+ choices.reserve(mgr.get_file_count());
+ for (size_t i = 0; mgr.get_file_count() > i; ++i)
+ choices.emplace_back(mgr.get_file_name(i));
+ menu::stack_push<menu_selector>(
+ ui(),
+ container(),
+ _("Category File"),
+ std::move(choices),
+ m_ini,
+ [this] (int selection)
+ {
+ if (selection != m_ini)
{
- if (selection != m_ini)
- {
- m_ini = selection;
- reset(reset_options::REMEMBER_REF);
- }
- });
- }
- else if (SYSTEM_GROUP == ref)
- {
- std::vector<std::string> choices;
- choices.reserve(mgr.get_category_count(m_ini));
- for (size_t i = 0; mgr.get_category_count(m_ini) > i; ++i)
- choices.emplace_back(mgr.get_category_name(m_ini, i));
- menu::stack_push<menu_selector>(
- ui(),
- container(),
- _("Group"),
- std::move(choices),
- m_state[m_ini].first,
- [this] (int selection)
+ m_ini = selection;
+ reset(reset_options::REMEMBER_REF);
+ }
+ });
+ }
+ else if (SYSTEM_GROUP == ref)
+ {
+ std::vector<std::string> choices;
+ choices.reserve(mgr.get_category_count(m_ini));
+ for (size_t i = 0; mgr.get_category_count(m_ini) > i; ++i)
+ choices.emplace_back(mgr.get_category_name(m_ini, i));
+ menu::stack_push<menu_selector>(
+ ui(),
+ container(),
+ _("Group"),
+ std::move(choices),
+ m_state[m_ini].first,
+ [this] (int selection)
+ {
+ if (selection != m_state[m_ini].first)
{
- if (selection != m_state[m_ini].first)
- {
- m_state[m_ini].first = selection;
- reset(reset_options::REMEMBER_REF);
- }
- });
- }
- else if (INCLUDE_CLONES == ref)
- {
- m_state[m_ini].second = !m_state[m_ini].second;
- reset(reset_options::REMEMBER_REF);
- }
- break;
+ m_state[m_ini].first = selection;
+ reset(reset_options::REMEMBER_REF);
+ }
+ });
}
-
- // rebuild if anything changed
- if (changed)
+ else if (INCLUDE_CLONES == ref)
+ {
+ m_state[m_ini].second = !m_state[m_ini].second;
reset(reset_options::REMEMBER_REF);
+ }
+ break;
}
+
+ // rebuild if anything changed
+ if (changed)
+ reset(reset_options::REMEMBER_REF);
+ return false;
}
@@ -1224,6 +1234,7 @@ public:
case FAVORITE:
case MANUFACTURER:
case YEAR:
+ case SOURCE_FILE:
case CUSTOM:
case COUNT:
break;
@@ -1233,7 +1244,17 @@ public:
static bool is_inclusion(type n)
{
- return (CATEGORY == n) || (MANUFACTURER == n) || (YEAR == n);
+ switch (n)
+ {
+ case CATEGORY:
+ case MANUFACTURER:
+ case YEAR:
+ case SOURCE_FILE:
+ return true;
+
+ default:
+ return false;
+ }
}
private:
@@ -1599,10 +1620,21 @@ void machine_filter_data::add_year(std::string const &year)
m_years.emplace(pos, year);
}
+void machine_filter_data::add_source_file(std::string_view path)
+{
+ std::vector<std::string>::iterator const pos(std::lower_bound(m_source_files.begin(), m_source_files.end(), path));
+ if ((m_source_files.end() == pos) || (*pos != path))
+ m_source_files.emplace(pos, path);
+}
+
void machine_filter_data::finalise()
{
+ for (std::string &path : m_source_files)
+ path = info_xml_creator::format_sourcefile(path);
+
std::stable_sort(m_manufacturers.begin(), m_manufacturers.end());
std::stable_sort(m_years.begin(), m_years.end());
+ std::stable_sort(m_source_files.begin(), m_source_files.end());
}
std::string machine_filter_data::extract_manufacturer(std::string const &manufacturer)
@@ -1777,6 +1809,8 @@ machine_filter::ptr machine_filter::create(type n, machine_filter_data const &da
return std::make_unique<manufacturer_machine_filter>(data, value, file, indent);
case YEAR:
return std::make_unique<year_machine_filter>(data, value, file, indent);
+ case SOURCE_FILE:
+ return std::make_unique<source_file_machine_filter>(data, value, file, indent);
case SAVE:
return std::make_unique<save_machine_filter>(data, value, file, indent);
case NOSAVE:
@@ -1950,15 +1984,11 @@ extern const char UI_VERSION_TAG[];
const char UI_VERSION_TAG[] = "# UI INFO ";
// Globals
-uint8_t ui_globals::rpanel = 0;
uint8_t ui_globals::curdats_view = 0;
uint8_t ui_globals::cur_sw_dats_total = 0;
uint8_t ui_globals::curdats_total = 0;
uint8_t ui_globals::cur_sw_dats_view = 0;
bool ui_globals::reset = false;
-int ui_globals::visible_main_lines = 0;
-int ui_globals::visible_sw_lines = 0;
-uint16_t ui_globals::panels_status = 0;
char* chartrimcarriage(char str[])
{
@@ -1971,11 +2001,6 @@ char* chartrimcarriage(char str[])
return str;
}
-const char* strensure(const char* s)
-{
- return s == nullptr ? "" : s;
-}
-
int getprecisionchr(const char* s)
{
int precision = 1;
diff --git a/src/frontend/mame/ui/utils.h b/src/frontend/mame/ui/utils.h
index 098588086f2..04cb25a840c 100644
--- a/src/frontend/mame/ui/utils.h
+++ b/src/frontend/mame/ui/utils.h
@@ -15,12 +15,17 @@
#include "softlist.h"
#include "unicode.h"
+// FIXME: allow OSD module headers to be included in a less ugly way
+#include "../osd/modules/lib/osdlib.h"
+
#include <algorithm>
#include <functional>
#include <limits>
#include <memory>
#include <string>
+#include <string_view>
#include <unordered_map>
+#include <utility>
#include <vector>
@@ -181,6 +186,7 @@ public:
CLONES,
MANUFACTURER,
YEAR,
+ SOURCE_FILE,
SAVE,
NOSAVE,
CHD,
@@ -271,10 +277,13 @@ class machine_filter_data
public:
std::vector<std::string> const &manufacturers() const { return m_manufacturers; }
std::vector<std::string> const &years() const { return m_years; }
+ std::vector<std::string> const &source_files() const { return m_source_files; }
// adding entries
void add_manufacturer(std::string const &manufacturer);
void add_year(std::string const &year);
+ void add_source_file(std::string_view path);
+
void finalise();
// use heuristics to extract meaningful parts from machine metadata
@@ -301,6 +310,7 @@ private:
std::vector<std::string> m_manufacturers;
std::vector<std::string> m_years;
+ std::vector<std::string> m_source_files;
machine_filter::type m_current_filter = machine_filter::ALL;
filter_map m_filters;
@@ -352,60 +362,29 @@ 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
struct ui_globals
{
- static uint8_t curdats_view, curdats_total, cur_sw_dats_view, cur_sw_dats_total, rpanel;
- static bool default_image, reset;
- static int visible_main_lines, visible_sw_lines;
- static uint16_t panels_status;
+ static uint8_t curdats_view, curdats_total, cur_sw_dats_view, cur_sw_dats_total;
+ static bool reset;
};
// GLOBAL FUNCTIONS
char* chartrimcarriage(char str[]);
-const char* strensure(const char* s);
int getprecisionchr(const char* s);
std::vector<std::string> tokenize(const std::string &text, char sep);
+namespace ui {
+
//-------------------------------------------------
// input_character - inputs a typed character
// into a buffer
@@ -414,31 +393,30 @@ std::vector<std::string> tokenize(const std::string &text, char sep);
template <typename F>
bool input_character(std::string &buffer, std::string::size_type size, char32_t unichar, F &&filter)
{
- bool result = false;
- auto buflen = buffer.size();
-
+ auto const buflen(buffer.length());
if ((unichar == 8) || (unichar == 0x7f))
{
// backspace
if (0 < buflen)
{
- auto buffer_oldend = buffer.c_str() + buflen;
- auto buffer_newend = utf8_previous_char(buffer_oldend);
+ auto const buffer_oldend(buffer.c_str() + buflen);
+ auto const buffer_newend(utf8_previous_char(buffer_oldend));
buffer.resize(buffer_newend - buffer.c_str());
- result = true;
+ return true;
}
}
else if ((unichar >= ' ') && filter(unichar))
{
// append this character - check against the size first
- std::string utf8_char = utf8_from_uchar(unichar);
- if ((buffer.size() + utf8_char.size()) <= size)
+ char utf8char[UTF8_CHAR_MAX];
+ auto const utf8len(utf8_from_uchar(utf8char, std::size(utf8char), unichar));
+ if ((0 < utf8len) && (size >= utf8len) && ((size - utf8len) >= buflen))
{
- buffer += utf8_char;
- result = true;
+ buffer.append(utf8char, utf8len);
+ return true;
}
}
- return result;
+ return false;
}
@@ -450,9 +428,61 @@ bool input_character(std::string &buffer, std::string::size_type size, char32_t
template <typename F>
bool input_character(std::string &buffer, char32_t unichar, F &&filter)
{
- auto size = std::numeric_limits<std::string::size_type>::max();
- return input_character(buffer, size, unichar, filter);
+ auto const size(std::numeric_limits<std::string::size_type>::max());
+ return input_character(buffer, size, unichar, std::forward<F>(filter));
+}
+
+
+//-------------------------------------------------
+// paste_text - paste text from clipboard into a
+// buffer, ignoring invalid characters
+//-------------------------------------------------
+
+template <typename F>
+bool paste_text(std::string &buffer, std::string::size_type size, F &&filter)
+{
+ std::string const clip(osd_get_clipboard_text());
+ std::string_view text(clip);
+ bool updated(false);
+ int codelength;
+ char32_t unichar;
+ while ((codelength = uchar_from_utf8(&unichar, text)) != 0)
+ {
+ text.remove_prefix((0 < codelength) ? codelength : 1);
+ if ((0 < codelength) && filter(unichar))
+ {
+ char utf8char[UTF8_CHAR_MAX];
+ auto const utf8len(utf8_from_uchar(utf8char, std::size(utf8char), unichar));
+ if (0 < utf8len)
+ {
+ if ((size < utf8len) || ((size - utf8len) < buffer.length()))
+ {
+ return updated;
+ }
+ else
+ {
+ buffer.append(utf8char, utf8len);
+ updated = true;
+ }
+ }
+ }
+ }
+ return updated;
}
+//-------------------------------------------------
+// paste_text - paste text from clipboard into a
+// buffer, ignoring invalid characters
+//-------------------------------------------------
+
+template <typename F>
+bool paste_text(std::string &buffer, F &&filter)
+{
+ auto const size(std::numeric_limits<std::string::size_type>::max());
+ return paste_text(buffer, size, std::forward<F>(filter));
+}
+
+} // namespace ui
+
#endif // MAME_FRONTEND_UI_UTILS_H
diff --git a/src/frontend/mame/ui/videoopt.cpp b/src/frontend/mame/ui/videoopt.cpp
index 253271beb4f..84ae6b6867d 100644
--- a/src/frontend/mame/ui/videoopt.cpp
+++ b/src/frontend/mame/ui/videoopt.cpp
@@ -11,6 +11,7 @@
#include "emu.h"
#include "ui/videoopt.h"
+#include "rendfont.h"
#include "rendlay.h"
#include "rendutil.h"
@@ -44,7 +45,7 @@ menu_video_targets::~menu_video_targets()
{
}
-void menu_video_targets::populate(float &customtop, float &custombottom)
+void menu_video_targets::populate()
{
// find the targets
for (unsigned targetnum = 0; ; targetnum++)
@@ -68,7 +69,7 @@ void menu_video_targets::populate(float &customtop, float &custombottom)
menu
-------------------------------------------------*/
-void menu_video_targets::handle(event const *ev)
+bool menu_video_targets::handle(event const *ev)
{
if (ev && (ev->iptkey == IPT_UI_SELECT))
{
@@ -80,6 +81,8 @@ void menu_video_targets::handle(event const *ev)
*target,
&machine().video().snapshot_target() == target);
}
+
+ return false;
}
@@ -111,7 +114,7 @@ menu_video_options::~menu_video_options()
{
}
-void menu_video_options::populate(float &customtop, float &custombottom)
+void menu_video_options::populate()
{
uintptr_t ref;
@@ -119,7 +122,7 @@ void menu_video_options::populate(float &customtop, float &custombottom)
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);
}
@@ -147,10 +150,10 @@ void menu_video_options::populate(float &customtop, float &custombottom)
// add a rotate item
switch (m_target.orientation())
{
- case ROT0: subtext = "None"; break;
- case ROT90: subtext = "CW 90" UTF8_DEGREES; break;
- case ROT180: subtext = "180" UTF8_DEGREES; break;
- case ROT270: subtext = "CCW 90" UTF8_DEGREES; break;
+ case ROT0: subtext = "None"; break;
+ case ROT90: subtext = u8"CW 90°"; break;
+ case ROT180: subtext = u8"180°"; break;
+ case ROT270: subtext = u8"CCW 90°"; break;
}
item_append(_("Rotate"), subtext, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, reinterpret_cast<void *>(ITEM_ROTATE));
@@ -198,9 +201,14 @@ void menu_video_options::populate(float &customtop, float &custombottom)
menu
-------------------------------------------------*/
-void menu_video_options::handle(event const *ev)
+bool menu_video_options::handle(event const *ev)
{
- auto const lockout_popup([this] () { machine().popmessage(_("Cannot change options while recording!")); });
+ auto const lockout_popup(
+ [this] ()
+ {
+ machine().popmessage(_("Cannot change options while recording!"));
+ return true;
+ });
bool const snap_lockout(m_snapshot && machine().video().is_recording());
bool changed(false);
@@ -338,6 +346,7 @@ void menu_video_options::handle(event const *ev)
// if something changed, rebuild the menu
if (changed)
reset(reset_options::REMEMBER_REF);
+ return false;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/videoopt.h b/src/frontend/mame/ui/videoopt.h
index b296cad06ab..a725cba0f71 100644
--- a/src/frontend/mame/ui/videoopt.h
+++ b/src/frontend/mame/ui/videoopt.h
@@ -26,8 +26,8 @@ public:
virtual ~menu_video_targets() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
};
@@ -38,8 +38,8 @@ public:
virtual ~menu_video_options() override;
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
render_target &m_target;
bool const m_snapshot;
diff --git a/src/frontend/mame/ui/viewgfx.cpp b/src/frontend/mame/ui/viewgfx.cpp
index 0b7b9a47e1d..cbb104d5460 100644
--- a/src/frontend/mame/ui/viewgfx.cpp
+++ b/src/frontend/mame/ui/viewgfx.cpp
@@ -21,6 +21,8 @@
#include "util/unicode.h"
+#include "osdepend.h"
+
#include <cmath>
#include <vector>
@@ -56,10 +58,88 @@ public:
if (!is_relevant())
return cancel(uistate);
+ // let the OSD do its thing
+ mui.machine().osd().check_osd_inputs();
+
// always mark the bitmap dirty if not paused
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)
{
@@ -514,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;
}
@@ -527,7 +614,7 @@ private:
}
// cancel or graphics viewer dismisses the viewer
- if (input.pressed(IPT_UI_CANCEL) || input.pressed(IPT_UI_SHOW_GFX))
+ if (input.pressed(IPT_UI_BACK) || input.pressed(IPT_UI_SHOW_GFX))
return cancel(uistate);
return uistate;
@@ -537,8 +624,15 @@ 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 UI_HANDLER_CANCEL;
+ return mame_ui_manager::HANDLER_CANCEL;
}
uint32_t handle_palette(mame_ui_manager &mui, render_container &container, bool uistate);
@@ -589,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;
@@ -955,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));
}
}
@@ -1002,8 +1105,7 @@ uint32_t gfx_viewer::handle_palette(mame_ui_manager &mui, render_container &cont
container.add_point(0.5f * (x0 + cellboxbounds.x0), y0 + 0.5f * cellheight, UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
// draw the row header
- char buffer[10];
- sprintf(buffer, "%5X", index);
+ auto buffer = util::string_format("%5X", index);
for (int x = 4; x >= 0; x--)
{
x0 -= ui_font->char_width(chheight, aspect, buffer[x]);
@@ -1158,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)
@@ -1208,8 +1315,7 @@ uint32_t gfx_viewer::handle_gfxset(mame_ui_manager &mui, render_container &conta
container.add_point(0.5f * (x0 + boxbounds.x0 + 6.0f * chwidth), y0 + 0.5f * cellheight, UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
// draw the row header
- char buffer[10];
- sprintf(buffer, "%5X", set.m_offset + (y * xcells));
+ auto buffer = util::string_format("%5X", set.m_offset + (y * xcells));
for (int x = 4; x >= 0; x--)
{
x0 -= ui_font->char_width(chheight, aspect, buffer[x]);
@@ -1327,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
{