summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame')
-rw-r--r--src/frontend/mame/audit.cpp50
-rw-r--r--src/frontend/mame/cheat.cpp33
-rw-r--r--src/frontend/mame/cheat.h12
-rw-r--r--src/frontend/mame/clifront.cpp382
-rw-r--r--src/frontend/mame/clifront.h1
-rw-r--r--src/frontend/mame/infoxml.cpp337
-rw-r--r--src/frontend/mame/infoxml.h3
-rw-r--r--src/frontend/mame/language.cpp1
-rw-r--r--src/frontend/mame/luaengine.cpp1194
-rw-r--r--src/frontend/mame/luaengine.h96
-rw-r--r--src/frontend/mame/luaengine.ipp266
-rw-r--r--src/frontend/mame/luaengine_debug.cpp397
-rw-r--r--src/frontend/mame/luaengine_input.cpp79
-rw-r--r--src/frontend/mame/luaengine_mem.cpp494
-rw-r--r--src/frontend/mame/luaengine_render.cpp920
-rw-r--r--src/frontend/mame/mame.cpp91
-rw-r--r--src/frontend/mame/mame.h29
-rw-r--r--src/frontend/mame/mameopts.cpp80
-rw-r--r--src/frontend/mame/mameopts.h1
-rw-r--r--src/frontend/mame/media_ident.cpp33
-rw-r--r--src/frontend/mame/pluginopts.cpp22
-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.cpp656
-rw-r--r--src/frontend/mame/ui/analogipt.h37
-rw-r--r--src/frontend/mame/ui/auditmenu.cpp46
-rw-r--r--src/frontend/mame/ui/auditmenu.h9
-rw-r--r--src/frontend/mame/ui/barcode.cpp114
-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.cpp243
-rw-r--r--src/frontend/mame/ui/confswitch.h11
-rw-r--r--src/frontend/mame/ui/custui.cpp770
-rw-r--r--src/frontend/mame/ui/custui.h37
-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.cpp60
-rw-r--r--src/frontend/mame/ui/devopt.h3
-rw-r--r--src/frontend/mame/ui/dirmenu.cpp756
-rw-r--r--src/frontend/mame/ui/dirmenu.h87
-rw-r--r--src/frontend/mame/ui/filecreate.cpp153
-rw-r--r--src/frontend/mame/ui/filecreate.h38
-rw-r--r--src/frontend/mame/ui/filemngr.cpp148
-rw-r--r--src/frontend/mame/ui/filemngr.h20
-rw-r--r--src/frontend/mame/ui/filesel.cpp389
-rw-r--r--src/frontend/mame/ui/filesel.h72
-rw-r--r--src/frontend/mame/ui/floppycntrl.cpp149
-rw-r--r--src/frontend/mame/ui/floppycntrl.h6
-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.cpp162
-rw-r--r--src/frontend/mame/ui/info.h19
-rw-r--r--src/frontend/mame/ui/info_pty.cpp9
-rw-r--r--src/frontend/mame/ui/info_pty.h4
-rw-r--r--src/frontend/mame/ui/inifile.cpp75
-rw-r--r--src/frontend/mame/ui/inifile.h2
-rw-r--r--src/frontend/mame/ui/inputdevices.cpp310
-rw-r--r--src/frontend/mame/ui/inputdevices.h34
-rw-r--r--src/frontend/mame/ui/inputmap.cpp158
-rw-r--r--src/frontend/mame/ui/inputmap.h26
-rw-r--r--src/frontend/mame/ui/inputopts.cpp144
-rw-r--r--src/frontend/mame/ui/inputopts.h37
-rw-r--r--src/frontend/mame/ui/inputtoggle.cpp220
-rw-r--r--src/frontend/mame/ui/inputtoggle.h42
-rw-r--r--src/frontend/mame/ui/keyboard.cpp79
-rw-r--r--src/frontend/mame/ui/keyboard.h4
-rw-r--r--src/frontend/mame/ui/mainmenu.cpp129
-rw-r--r--src/frontend/mame/ui/mainmenu.h4
-rw-r--r--src/frontend/mame/ui/menu.cpp1656
-rw-r--r--src/frontend/mame/ui/menu.h324
-rw-r--r--src/frontend/mame/ui/miscmenu.cpp335
-rw-r--r--src/frontend/mame/ui/miscmenu.h35
-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.cpp117
-rw-r--r--src/frontend/mame/ui/optsmenu.h16
-rw-r--r--src/frontend/mame/ui/pluginopt.cpp51
-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.cpp85
-rw-r--r--src/frontend/mame/ui/selector.h22
-rw-r--r--src/frontend/mame/ui/selgame.cpp327
-rw-r--r--src/frontend/mame/ui/selgame.h19
-rw-r--r--src/frontend/mame/ui/selmenu.cpp3461
-rw-r--r--src/frontend/mame/ui/selmenu.h238
-rw-r--r--src/frontend/mame/ui/selsoft.cpp187
-rw-r--r--src/frontend/mame/ui/selsoft.h16
-rw-r--r--src/frontend/mame/ui/simpleselgame.cpp294
-rw-r--r--src/frontend/mame/ui/simpleselgame.h14
-rw-r--r--src/frontend/mame/ui/sliders.cpp252
-rw-r--r--src/frontend/mame/ui/sliders.h7
-rw-r--r--src/frontend/mame/ui/slotopt.cpp64
-rw-r--r--src/frontend/mame/ui/slotopt.h9
-rw-r--r--src/frontend/mame/ui/sndmenu.cpp28
-rw-r--r--src/frontend/mame/ui/sndmenu.h5
-rw-r--r--src/frontend/mame/ui/state.cpp153
-rw-r--r--src/frontend/mame/ui/state.h17
-rw-r--r--src/frontend/mame/ui/submenu.cpp74
-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.cpp10
-rw-r--r--src/frontend/mame/ui/tapectrl.cpp6
-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.cpp517
-rw-r--r--src/frontend/mame/ui/textbox.h53
-rw-r--r--src/frontend/mame/ui/ui.cpp690
-rw-r--r--src/frontend/mame/ui/ui.h112
-rw-r--r--src/frontend/mame/ui/utils.cpp562
-rw-r--r--src/frontend/mame/ui/utils.h142
-rw-r--r--src/frontend/mame/ui/videoopt.cpp61
-rw-r--r--src/frontend/mame/ui/videoopt.h15
-rw-r--r--src/frontend/mame/ui/viewgfx.cpp2201
-rw-r--r--src/frontend/mame/ui/viewgfx.h6
123 files changed, 15422 insertions, 8034 deletions
diff --git a/src/frontend/mame/audit.cpp b/src/frontend/mame/audit.cpp
index 0a37c4a3fff..dbbb9ad8f3d 100644
--- a/src/frontend/mame/audit.cpp
+++ b/src/frontend/mame/audit.cpp
@@ -15,10 +15,12 @@
#include "emuopts.h"
#include "drivenum.h"
+#include "fileio.h"
#include "romload.h"
#include "softlist_dev.h"
#include "chd.h"
+#include "path.h"
#include <algorithm>
@@ -45,8 +47,41 @@ class parent_rom_vector : public std::vector<parent_rom>
public:
using std::vector<parent_rom>::vector;
- void remove_redundant_parents()
+ void remove_redundant_parents(device_t const &device)
{
+ // remove parents with no shared ROMs
+ const_reverse_iterator firstparent(crend());
+ for (rom_entry const *region = rom_first_region(device); region && ((crend() == firstparent) || (back().type.get() != firstparent->type.get())); region = rom_next_region(region))
+ {
+ for (rom_entry const *rom = rom_first_file(region); rom; rom = rom_next_file(rom))
+ {
+ util::hash_collection const hashes(rom->hashdata());
+ auto const match(
+ std::find_if(
+ crbegin(),
+ firstparent,
+ [rom, &hashes] (parent_rom const &r)
+ {
+ if (r.length != rom_file_size(rom))
+ return false;
+ else if (!hashes.flag(util::hash_collection::FLAG_NO_DUMP))
+ return r.hashes == hashes;
+ else
+ return r.name == rom->name();
+ }));
+ if (match != firstparent)
+ {
+ firstparent = std::find_if(
+ crbegin(),
+ match,
+ [&match] (parent_rom const &r) { return r.type.get() == match->type.get(); });
+ if (back().type.get() == match->type.get())
+ break;
+ }
+ }
+ }
+ erase(firstparent.base(), cend());
+
while (!empty())
{
// find where the next parent starts
@@ -201,7 +236,7 @@ media_auditor::summary media_auditor::audit_media(const char *validation)
}
}
}
- parentroms.remove_redundant_parents();
+ parentroms.remove_redundant_parents(m_enumerator.config()->root_device());
// count ROMs required/found
std::size_t found(0);
@@ -277,8 +312,8 @@ media_auditor::summary media_auditor::audit_media(const char *validation)
LOG("Total required=%u (shared=%u) found=%u (shared=%u parent=%u)\n", required, shared_required, found, shared_found, parent_found);
}
- // if we only find files that are in the parent & either the set has no unique files or the parent is not found, then assume we don't have the set at all
- if ((found == shared_found) && required && ((required != shared_required) || !parent_found))
+ // if we only find files that are in the parent and either the set has no unique files or the parent is not found, then assume we don't have the set at all
+ if ((found == shared_found) && required && (found != required) && ((required != shared_required) || !parent_found))
{
m_record_list.clear();
return NOTFOUND;
@@ -422,8 +457,10 @@ media_auditor::summary media_auditor::audit_samples()
emu_file file(m_enumerator.options().sample_path(), OPEN_FLAG_READ | OPEN_FLAG_NO_PRELOAD);
path_iterator path(searchpath);
std::string curpath;
- while (path.next(curpath, samplename))
+ while (path.next(curpath))
{
+ util::path_append(curpath, samplename);
+
// attempt to access the file (.flac) or (.wav)
std::error_condition filerr = file.open(curpath + ".flac");
if (filerr)
@@ -619,6 +656,9 @@ media_auditor::audit_record &media_auditor::audit_one_disk(const rom_entry *rom,
chd_file source;
const std::error_condition err = rom_load_manager::open_disk_image(m_enumerator.options(), std::forward<T>(args)..., rom, source);
+ // FIXME: A CHD with an invalid header or missing parent is treated as not found.
+ // We need a way to report more detailed errors for bad disk images.
+
// if we succeeded, get the hashes
if (!err)
{
diff --git a/src/frontend/mame/cheat.cpp b/src/frontend/mame/cheat.cpp
index dbbd0ed78bc..e0d890ebda6 100644
--- a/src/frontend/mame/cheat.cpp
+++ b/src/frontend/mame/cheat.cpp
@@ -81,6 +81,7 @@
#include "corestr.h"
#include "emuopts.h"
+#include "fileio.h"
#include <cstring>
#include <iterator>
@@ -204,7 +205,7 @@ const char *cheat_parameter::text()
// save - save a single cheat parameter
//-------------------------------------------------
-void cheat_parameter::save(emu_file &cheatfile) const
+void cheat_parameter::save(util::core_file &cheatfile) const
{
// output the parameter tag
cheatfile.printf("\t\t<parameter");
@@ -367,7 +368,7 @@ void cheat_script::execute(cheat_manager &manager, uint64_t &argindex)
// save - save a single cheat script
//-------------------------------------------------
-void cheat_script::save(emu_file &cheatfile) const
+void cheat_script::save(util::core_file &cheatfile) const
{
// output the script tag
cheatfile.printf("\t\t<script");
@@ -531,7 +532,7 @@ void cheat_script::script_entry::execute(cheat_manager &manager, uint64_t &argin
// save - save a single action or output
//-------------------------------------------------
-void cheat_script::script_entry::save(emu_file &cheatfile) const
+void cheat_script::script_entry::save(util::core_file &cheatfile) const
{
if (m_format.empty())
{
@@ -544,7 +545,7 @@ void cheat_script::script_entry::save(emu_file &cheatfile) const
else
{
// output an output
- cheatfile.printf("\t\t\t<output format=\"%s\"", m_format.c_str());
+ cheatfile.printf("\t\t\t<output format=\"%s\"", m_format);
if (!m_condition.is_empty())
cheatfile.printf(" condition=\"%s\"", cheat_manager::quote_expression(m_condition));
@@ -662,7 +663,7 @@ int cheat_script::script_entry::output_argument::values(uint64_t &argindex, uint
// save - save a single output argument
//-------------------------------------------------
-void cheat_script::script_entry::output_argument::save(emu_file &cheatfile) const
+void cheat_script::script_entry::output_argument::save(util::core_file &cheatfile) const
{
cheatfile.printf("\t\t\t\t<argument");
if (m_count != 1)
@@ -762,7 +763,7 @@ cheat_entry::~cheat_entry()
// save - save a single cheat entry
//-------------------------------------------------
-void cheat_entry::save(emu_file &cheatfile) const
+void cheat_entry::save(util::core_file &cheatfile) const
{
// determine if we have scripts
bool const has_scripts(m_off_script || m_on_script || m_run_script || m_change_script);
@@ -1097,7 +1098,7 @@ cheat_manager::cheat_manager(running_machine &machine)
// cheat engine
//-------------------------------------------------
-void cheat_manager::set_enable(bool enable)
+void cheat_manager::set_enable(bool enable, bool show)
{
// if the cheat engine is disabled, we're done
if (!machine().options().cheat())
@@ -1113,7 +1114,8 @@ void cheat_manager::set_enable(bool enable)
if (cheat->state() == SCRIPT_STATE_RUN)
cheat->execute_off_script();
}
- machine().popmessage("Cheats Disabled");
+ if (show)
+ machine().popmessage("Cheats Disabled");
m_disabled = true;
}
else if (m_disabled && enable)
@@ -1127,7 +1129,8 @@ void cheat_manager::set_enable(bool enable)
if (cheat->state() == SCRIPT_STATE_RUN)
cheat->execute_on_script();
}
- machine().popmessage("Cheats Enabled");
+ if (show)
+ machine().popmessage("Cheats Enabled");
}
}
@@ -1303,6 +1306,11 @@ std::string cheat_manager::quote_expression(const parsed_expression &expression)
strreplace(str, "& ", " band ");
strreplace(str, "&", " band ");
+ strreplace(str, " << ", " lshift ");
+ strreplace(str, " <<", " lshift ");
+ strreplace(str, "<< ", " lshift ");
+ strreplace(str, "<<", " lshift ");
+
strreplace(str, " <= ", " le ");
strreplace(str, " <=", " le ");
strreplace(str, "<= ", " le ");
@@ -1313,11 +1321,6 @@ std::string cheat_manager::quote_expression(const parsed_expression &expression)
strreplace(str, "< ", " lt ");
strreplace(str, "<", " lt ");
- strreplace(str, " << ", " lshift ");
- strreplace(str, " <<", " lshift ");
- strreplace(str, "<< ", " lshift ");
- strreplace(str, "<<", " lshift ");
-
return str;
}
@@ -1400,7 +1403,7 @@ void cheat_manager::load_cheats(std::string const &filename)
emu_file cheatfile(std::move(searchstr), OPEN_FLAG_READ);
try
{
- // loop over all instrances of the files found in our search paths
+ // loop over all instances of the files found in our search paths
for (std::error_condition filerr = cheatfile.open(filename + ".xml"); !filerr; filerr = cheatfile.open_next())
{
osd_printf_verbose("Loading cheats file from %s\n", cheatfile.fullpath());
diff --git a/src/frontend/mame/cheat.h b/src/frontend/mame/cheat.h
index b8f3e89a4e5..04f8006ae97 100644
--- a/src/frontend/mame/cheat.h
+++ b/src/frontend/mame/cheat.h
@@ -102,7 +102,7 @@ public:
bool set_next_state();
// actions
- void save(emu_file &cheatfile) const;
+ void save(util::core_file &cheatfile) const;
private:
// a single item in a parameter item list
@@ -160,7 +160,7 @@ public:
// actions
void execute(cheat_manager &manager, uint64_t &argindex);
- void save(emu_file &cheatfile) const;
+ void save(util::core_file &cheatfile) const;
private:
// an entry within the script
@@ -177,7 +177,7 @@ private:
// actions
void execute(cheat_manager &manager, uint64_t &argindex);
- void save(emu_file &cheatfile) const;
+ void save(util::core_file &cheatfile) const;
private:
// an argument for output
@@ -196,7 +196,7 @@ private:
int values(uint64_t &argindex, uint64_t *result);
// actions
- void save(emu_file &cheatfile) const;
+ void save(util::core_file &cheatfile) const;
private:
// internal state
@@ -267,7 +267,7 @@ public:
bool select_default_state();
bool select_previous_state();
bool select_next_state();
- void save(emu_file &cheatfile) const;
+ void save(util::core_file &cheatfile) const;
// UI helpers
void menu_text(std::string &description, std::string &state, uint32_t &flags);
@@ -314,7 +314,7 @@ public:
std::vector<std::unique_ptr<cheat_entry>> const &entries() const { return m_cheatlist; }
// setters
- void set_enable(bool enable);
+ void set_enable(bool enable, bool show);
// actions
void reload();
diff --git a/src/frontend/mame/clifront.cpp b/src/frontend/mame/clifront.cpp
index 312c96eff65..d7d9a8fdf08 100644
--- a/src/frontend/mame/clifront.cpp
+++ b/src/frontend/mame/clifront.cpp
@@ -23,6 +23,7 @@
#include "pluginopts.h"
#include "emuopts.h"
+#include "fileio.h"
#include "romload.h"
#include "softlist_dev.h"
#include "validity.h"
@@ -30,6 +31,7 @@
#include "chd.h"
#include "corestr.h"
+#include "path.h"
#include "unzip.h"
#include "xmlfile.h"
@@ -40,6 +42,7 @@
#include <set>
#include <tuple>
#include <cctype>
+#include <iostream>
//**************************************************************************
@@ -63,6 +66,7 @@
#define CLICOMMAND_LISTBROTHERS "listbrothers"
#define CLICOMMAND_LISTCRC "listcrc"
#define CLICOMMAND_LISTROMS "listroms"
+#define CLICOMMAND_LISTBIOS "listbios"
#define CLICOMMAND_LISTSAMPLES "listsamples"
#define CLICOMMAND_VERIFYROMS "verifyroms"
#define CLICOMMAND_VERIFYSAMPLES "verifysamples"
@@ -89,40 +93,41 @@ namespace {
const options_entry cli_option_entries[] =
{
/* core commands */
- { nullptr, nullptr, OPTION_HEADER, "CORE COMMANDS" },
- { CLICOMMAND_HELP ";h;?", "0", OPTION_COMMAND, "show help message" },
- { CLICOMMAND_VALIDATE ";valid", "0", OPTION_COMMAND, "perform validation on system drivers and devices" },
+ { nullptr, nullptr, core_options::option_type::HEADER, "CORE COMMANDS" },
+ { CLICOMMAND_HELP ";h;?", "0", core_options::option_type::COMMAND, "show help message" },
+ { CLICOMMAND_VALIDATE ";valid", "0", core_options::option_type::COMMAND, "perform validation on system drivers and devices" },
/* configuration commands */
- { nullptr, nullptr, OPTION_HEADER, "CONFIGURATION COMMANDS" },
- { CLICOMMAND_CREATECONFIG ";cc", "0", OPTION_COMMAND, "create the default configuration file" },
- { CLICOMMAND_SHOWCONFIG ";sc", "0", OPTION_COMMAND, "display running parameters" },
- { CLICOMMAND_SHOWUSAGE ";su", "0", OPTION_COMMAND, "show this help" },
+ { nullptr, nullptr, core_options::option_type::HEADER, "CONFIGURATION COMMANDS" },
+ { CLICOMMAND_CREATECONFIG ";cc", "0", core_options::option_type::COMMAND, "create the default configuration file" },
+ { CLICOMMAND_SHOWCONFIG ";sc", "0", core_options::option_type::COMMAND, "display running parameters" },
+ { CLICOMMAND_SHOWUSAGE ";su", "0", core_options::option_type::COMMAND, "show this help" },
/* frontend commands */
- { nullptr, nullptr, OPTION_HEADER, "FRONTEND COMMANDS" },
- { CLICOMMAND_LISTXML ";lx", "0", OPTION_COMMAND, "all available info on driver in XML format" },
- { CLICOMMAND_LISTFULL ";ll", "0", OPTION_COMMAND, "short name, full name" },
- { CLICOMMAND_LISTSOURCE ";ls", "0", OPTION_COMMAND, "driver sourcefile" },
- { CLICOMMAND_LISTCLONES ";lc", "0", OPTION_COMMAND, "show clones" },
- { CLICOMMAND_LISTBROTHERS ";lb", "0", OPTION_COMMAND, "show \"brothers\", or other drivers from same sourcefile" },
- { CLICOMMAND_LISTCRC, "0", OPTION_COMMAND, "CRC-32s" },
- { CLICOMMAND_LISTROMS ";lr", "0", OPTION_COMMAND, "list required ROMs for a driver" },
- { CLICOMMAND_LISTSAMPLES, "0", OPTION_COMMAND, "list optional samples for a driver" },
- { CLICOMMAND_VERIFYROMS, "0", OPTION_COMMAND, "report romsets that have problems" },
- { CLICOMMAND_VERIFYSAMPLES, "0", OPTION_COMMAND, "report samplesets that have problems" },
- { CLICOMMAND_ROMIDENT, "0", OPTION_COMMAND, "compare files with known MAME ROMs" },
- { CLICOMMAND_LISTDEVICES ";ld", "0", OPTION_COMMAND, "list available devices" },
- { CLICOMMAND_LISTSLOTS ";lslot", "0", OPTION_COMMAND, "list available slots and slot devices" },
- { CLICOMMAND_LISTMEDIA ";lm", "0", OPTION_COMMAND, "list available media for the system" },
- { CLICOMMAND_LISTSOFTWARE ";lsoft", "0", OPTION_COMMAND, "list known software for the system" },
- { CLICOMMAND_VERIFYSOFTWARE ";vsoft", "0", OPTION_COMMAND, "verify known software for the system" },
- { CLICOMMAND_GETSOFTLIST ";glist", "0", OPTION_COMMAND, "retrieve software list by name" },
- { CLICOMMAND_VERIFYSOFTLIST ";vlist", "0", OPTION_COMMAND, "verify software list by name" },
- { CLICOMMAND_VERSION, "0", OPTION_COMMAND, "get MAME version" },
-
- { nullptr, nullptr, OPTION_HEADER, "FRONTEND COMMAND OPTIONS" },
- { CLIOPTION_DTD, "1", OPTION_BOOLEAN, "include DTD in XML output" },
+ { nullptr, nullptr, core_options::option_type::HEADER, "FRONTEND COMMANDS" },
+ { CLICOMMAND_LISTXML ";lx", "0", core_options::option_type::COMMAND, "all available info on driver in XML format" },
+ { CLICOMMAND_LISTFULL ";ll", "0", core_options::option_type::COMMAND, "short name, full name" },
+ { CLICOMMAND_LISTSOURCE ";ls", "0", core_options::option_type::COMMAND, "driver sourcefile" },
+ { CLICOMMAND_LISTCLONES ";lc", "0", core_options::option_type::COMMAND, "show clones" },
+ { CLICOMMAND_LISTBROTHERS ";lb", "0", core_options::option_type::COMMAND, "show \"brothers\", or other drivers from same sourcefile" },
+ { CLICOMMAND_LISTCRC, "0", core_options::option_type::COMMAND, "CRC-32s" },
+ { CLICOMMAND_LISTROMS ";lr", "0", core_options::option_type::COMMAND, "list required ROMs for a driver" },
+ { CLICOMMAND_LISTSAMPLES, "0", core_options::option_type::COMMAND, "list optional samples for a driver" },
+ { CLICOMMAND_VERIFYROMS, "0", core_options::option_type::COMMAND, "report romsets that have problems" },
+ { CLICOMMAND_VERIFYSAMPLES, "0", core_options::option_type::COMMAND, "report samplesets that have problems" },
+ { CLICOMMAND_ROMIDENT, "0", core_options::option_type::COMMAND, "compare files with known MAME ROMs" },
+ { CLICOMMAND_LISTDEVICES ";ld", "0", core_options::option_type::COMMAND, "list devices in a system" },
+ { CLICOMMAND_LISTSLOTS ";lslot", "0", core_options::option_type::COMMAND, "list available slots and slot devices" },
+ { CLICOMMAND_LISTBIOS, "0", core_options::option_type::COMMAND, "list BIOS options for a system" },
+ { CLICOMMAND_LISTMEDIA ";lm", "0", core_options::option_type::COMMAND, "list available media for the system" },
+ { CLICOMMAND_LISTSOFTWARE ";lsoft", "0", core_options::option_type::COMMAND, "list known software for the system" },
+ { CLICOMMAND_VERIFYSOFTWARE ";vsoft", "0", core_options::option_type::COMMAND, "verify known software for the system" },
+ { CLICOMMAND_GETSOFTLIST ";glist", "0", core_options::option_type::COMMAND, "retrieve software list by name" },
+ { CLICOMMAND_VERIFYSOFTLIST ";vlist", "0", core_options::option_type::COMMAND, "verify software list by name" },
+ { CLICOMMAND_VERSION, "0", core_options::option_type::COMMAND, "get MAME version" },
+
+ { nullptr, nullptr, core_options::option_type::HEADER, "FRONTEND COMMAND OPTIONS" },
+ { CLIOPTION_DTD, "1", core_options::option_type::BOOLEAN, "include DTD in XML output" },
{ nullptr }
};
@@ -297,7 +302,7 @@ int cli_frontend::execute(std::vector<std::string> &args)
// reason for failure, offer some suggestions
if (m_result == EMU_ERR_NO_SUCH_SYSTEM
&& !m_options.attempted_system_name().empty()
- && !core_iswildstr(m_options.attempted_system_name().c_str())
+ && !core_iswildstr(m_options.attempted_system_name())
&& mame_options::system(m_options) == nullptr)
{
// get the top 16 approximate matches
@@ -398,7 +403,10 @@ void cli_frontend::listsource(const std::vector<std::string> &args)
{
auto const list_system_source = [] (device_type type)
{
- osd_printf_info("%-16s %s\n", type.shortname(), core_filename_extract_base(type.source()));
+ osd_printf_info(
+ "%-16s %s\n",
+ type.shortname(),
+ info_xml_creator::format_sourcefile(type.source()));
};
apply_action(
args,
@@ -497,11 +505,12 @@ void cli_frontend::listbrothers(const std::vector<std::string> &args)
drivlist.reset();
while (drivlist.next())
{
- int clone_of = drivlist.clone();
+ auto const src(info_xml_creator::format_sourcefile(drivlist.driver().type.source()));
+ int const clone_of(drivlist.clone());
if (clone_of != -1)
- osd_printf_info("%-20s %-16s %s\n", core_filename_extract_base(drivlist.driver().type.source()), drivlist.driver().name, (clone_of == -1 ? "" : drivlist.driver(clone_of).name));
+ osd_printf_info("%-20s %-16s %s\n", src, drivlist.driver().name, (clone_of == -1 ? "" : drivlist.driver(clone_of).name));
else
- osd_printf_info("%-20s %s\n", core_filename_extract_base(drivlist.driver().type.source()), drivlist.driver().name);
+ osd_printf_info("%-20s %s\n", src, drivlist.driver().name);
}
}
@@ -631,6 +640,69 @@ void cli_frontend::listroms(const std::vector<std::string> &args)
//-------------------------------------------------
+// listbios - output the BIOS options for a system
+// by matching systems/devices
+//-------------------------------------------------
+
+void cli_frontend::listbios(const std::vector<std::string> &args)
+{
+ const char *gamename = args.empty() ? nullptr : args[0].c_str();
+
+ // determine which drivers to output; return an error if none found
+ driver_enumerator drivlist(m_options, gamename);
+ if (drivlist.count() == 0)
+ throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename);
+
+ // iterate over drivers
+ bool firstsystem = true;
+ std::vector<std::pair<std::string, std::string> > bioses;
+ while (drivlist.next())
+ {
+ device_t &root = drivlist.config()->root_device();
+ if (firstsystem)
+ firstsystem = false;
+ else
+ printf("\n");
+
+ // print system BIOS options if there are any
+ bool firstbios = true;
+ for (const romload::system_bios &bios : romload::system_bioses(root.rom_region()))
+ {
+ if (firstbios)
+ {
+ printf("BIOS options for system %s (%s):\n", root.name(), root.shortname());
+ firstbios = false;
+ }
+ printf(" %-16s %s\n", bios.get_name(), bios.get_description());
+ }
+ if (firstbios)
+ printf("No BIOS options for system %s (%s)\n", root.name(), root.shortname());
+
+ // iterate over slots
+ for (const device_slot_interface &slot : slot_interface_enumerator(root))
+ {
+ // ignore fixed or empty slots
+ device_t *const card = slot.get_card_device();
+ if (slot.fixed() || !card || !card->rom_region())
+ continue;
+
+ // print card BIOS options if there are any
+ bool firstcard = true;
+ for (const romload::system_bios &bios : romload::system_bioses(card->rom_region()))
+ {
+ if (firstcard)
+ {
+ printf("\n BIOS options for device %s (-%s %s):\n", card->name(), slot.device().tag() + 1, card->basetag());
+ firstcard = false;
+ }
+ printf(" %-16s %s\n", bios.get_name(), bios.get_description());
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
// listsamples - output the list of samples
// referenced by a given game or set of games
//-------------------------------------------------
@@ -757,7 +829,7 @@ void cli_frontend::listdevices(const std::vector<std::string> &args)
//-------------------------------------------------
// listslots - output the list of slot devices
-// referenced by a given game or set of games
+// present in a system or set of systems
//-------------------------------------------------
void cli_frontend::listslots(const std::vector<std::string> &args)
@@ -776,11 +848,12 @@ void cli_frontend::listslots(const std::vector<std::string> &args)
// iterate over drivers
while (drivlist.next())
{
- // iterate
+ // iterate over slots
bool first = true;
for (const device_slot_interface &slot : slot_interface_enumerator(drivlist.config()->root_device()))
{
- if (slot.fixed()) continue;
+ if (slot.fixed())
+ continue;
// build a list of user-selectable options
std::vector<device_slot_interface::slot_option const *> option_list;
@@ -789,9 +862,13 @@ void cli_frontend::listslots(const std::vector<std::string> &args)
option_list.push_back(option.second.get());
// sort them by name
- std::sort(option_list.begin(), option_list.end(), [](device_slot_interface::slot_option const *opt1, device_slot_interface::slot_option const *opt2) {
- return strcmp(opt1->name(), opt2->name()) < 0;
- });
+ std::sort(
+ option_list.begin(),
+ option_list.end(),
+ [] (device_slot_interface::slot_option const *opt1, device_slot_interface::slot_option const *opt2)
+ {
+ return strcmp(opt1->name(), opt2->name()) < 0;
+ });
// output the line, up to the list of extensions
@@ -884,7 +961,7 @@ void cli_frontend::listmedia(const std::vector<std::string> &args)
//-------------------------------------------------
void cli_frontend::verifyroms(const std::vector<std::string> &args)
{
- bool const iswild((1U != args.size()) || core_iswildstr(args[0].c_str()));
+ bool const iswild((1U != args.size()) || core_iswildstr(args[0]));
std::vector<bool> matched(args.size(), false);
unsigned matchcount = 0;
auto const included = [&args, &matched, &matchcount] (char const *name) -> bool
@@ -899,7 +976,7 @@ void cli_frontend::verifyroms(const std::vector<std::string> &args)
auto it = matched.begin();
for (std::string const &pat : args)
{
- if (!core_strwildcmp(pat.c_str(), name))
+ if (!core_strwildcmp(pat, name))
{
++matchcount;
result = true;
@@ -1038,18 +1115,17 @@ void cli_frontend::verifysamples(const std::vector<std::string> &args)
if (matched == 0)
throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename);
- // if we didn't get anything at all, display a generic end message
if (matched > 0 && correct == 0 && incorrect == 0)
{
+ // if we didn't get anything at all, display a generic end message
if (notfound > 0)
throw emu_fatalerror(EMU_ERR_MISSING_FILES, "sampleset \"%s\" not found!\n", gamename);
else
throw emu_fatalerror(EMU_ERR_MISSING_FILES, "sampleset \"%s\" not required!\n", gamename);
}
-
- // otherwise, print a summary
else
{
+ // otherwise, print a summary
if (incorrect > 0)
throw emu_fatalerror(EMU_ERR_MISSING_FILES, "%u samplesets found, %u were OK.\n", correct + incorrect, correct);
osd_printf_info("%u samplesets found, %u were OK.\n", correct, correct);
@@ -1057,98 +1133,100 @@ void cli_frontend::verifysamples(const std::vector<std::string> &args)
}
const char cli_frontend::s_softlist_xml_dtd[] =
- "<?xml version=\"1.0\"?>\n" \
- "<!DOCTYPE softwarelists [\n" \
- "<!ELEMENT softwarelists (softwarelist*)>\n" \
- "\t<!ELEMENT softwarelist (notes?, software+)>\n" \
- "\t\t<!ATTLIST softwarelist name CDATA #REQUIRED>\n" \
- "\t\t<!ATTLIST softwarelist description CDATA #IMPLIED>\n" \
- "\t\t<!ELEMENT notes (#PCDATA)>\n" \
- "\t\t<!ELEMENT software (description, year, publisher, notes?, info*, sharedfeat*, part*)>\n" \
- "\t\t\t<!ATTLIST software name CDATA #REQUIRED>\n" \
- "\t\t\t<!ATTLIST software cloneof CDATA #IMPLIED>\n" \
- "\t\t\t<!ATTLIST software supported (yes|partial|no) \"yes\">\n" \
- "\t\t\t<!ELEMENT description (#PCDATA)>\n" \
- "\t\t\t<!ELEMENT year (#PCDATA)>\n" \
- "\t\t\t<!ELEMENT publisher (#PCDATA)>\n" \
- "\t\t\t<!ELEMENT notes (#PCDATA)>\n" \
- "\t\t\t<!ELEMENT info EMPTY>\n" \
- "\t\t\t\t<!ATTLIST info name CDATA #REQUIRED>\n" \
- "\t\t\t\t<!ATTLIST info value CDATA #IMPLIED>\n" \
- "\t\t\t<!ELEMENT sharedfeat EMPTY>\n" \
- "\t\t\t\t<!ATTLIST sharedfeat name CDATA #REQUIRED>\n" \
- "\t\t\t\t<!ATTLIST sharedfeat value CDATA #IMPLIED>\n" \
- "\t\t\t<!ELEMENT part (feature*, dataarea*, diskarea*, dipswitch*)>\n" \
- "\t\t\t\t<!ATTLIST part name CDATA #REQUIRED>\n" \
- "\t\t\t\t<!ATTLIST part interface CDATA #REQUIRED>\n" \
- "\t\t\t\t<!ELEMENT feature EMPTY>\n" \
- "\t\t\t\t\t<!ATTLIST feature name CDATA #REQUIRED>\n" \
- "\t\t\t\t\t<!ATTLIST feature value CDATA #IMPLIED>\n" \
- "\t\t\t\t<!ELEMENT dataarea (rom*)>\n" \
- "\t\t\t\t\t<!ATTLIST dataarea name CDATA #REQUIRED>\n" \
- "\t\t\t\t\t<!ATTLIST dataarea size CDATA #REQUIRED>\n" \
- "\t\t\t\t\t<!ATTLIST dataarea databits (8|16|32|64) \"8\">\n" \
- "\t\t\t\t\t<!ATTLIST dataarea endian (big|little) \"little\">\n" \
- "\t\t\t\t\t<!ELEMENT rom EMPTY>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom name CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom size CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom length CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom crc CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom sha1 CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom offset CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom value CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST rom status (baddump|nodump|good) \"good\">\n" \
- "\t\t\t\t\t\t<!ATTLIST rom loadflag (load16_byte|load16_word|load16_word_swap|load32_byte|load32_word|load32_word_swap|load32_dword|load64_word|load64_word_swap|reload|fill|continue|reload_plain) #IMPLIED>\n" \
- "\t\t\t\t<!ELEMENT diskarea (disk*)>\n" \
- "\t\t\t\t\t<!ATTLIST diskarea name CDATA #REQUIRED>\n" \
- "\t\t\t\t\t<!ELEMENT disk EMPTY>\n" \
- "\t\t\t\t\t\t<!ATTLIST disk name CDATA #REQUIRED>\n" \
- "\t\t\t\t\t\t<!ATTLIST disk sha1 CDATA #IMPLIED>\n" \
- "\t\t\t\t\t\t<!ATTLIST disk status (baddump|nodump|good) \"good\">\n" \
- "\t\t\t\t\t\t<!ATTLIST disk writeable (yes|no) \"no\">\n" \
- "\t\t\t\t<!ELEMENT dipswitch (dipvalue*)>\n" \
- "\t\t\t\t\t<!ATTLIST dipswitch name CDATA #REQUIRED>\n" \
- "\t\t\t\t\t<!ATTLIST dipswitch tag CDATA #REQUIRED>\n" \
- "\t\t\t\t\t<!ATTLIST dipswitch mask CDATA #REQUIRED>\n" \
- "\t\t\t\t\t<!ELEMENT dipvalue EMPTY>\n" \
- "\t\t\t\t\t\t<!ATTLIST dipvalue name CDATA #REQUIRED>\n" \
- "\t\t\t\t\t\t<!ATTLIST dipvalue value CDATA #REQUIRED>\n" \
- "\t\t\t\t\t\t<!ATTLIST dipvalue default (yes|no) \"no\">\n" \
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE softwarelists [\n"
+ "<!ELEMENT softwarelists (softwarelist*)>\n"
+ "\t<!ELEMENT softwarelist (notes?, software+)>\n"
+ "\t\t<!ATTLIST softwarelist name CDATA #REQUIRED>\n"
+ "\t\t<!ATTLIST softwarelist description CDATA #IMPLIED>\n"
+ "\t\t<!ELEMENT notes (#PCDATA)>\n"
+ "\t\t<!ELEMENT software (description, year, publisher, notes?, info*, sharedfeat*, part*)>\n"
+ "\t\t\t<!ATTLIST software name CDATA #REQUIRED>\n"
+ "\t\t\t<!ATTLIST software cloneof CDATA #IMPLIED>\n"
+ "\t\t\t<!ATTLIST software supported (yes|partial|no) \"yes\">\n"
+ "\t\t\t<!ELEMENT description (#PCDATA)>\n"
+ "\t\t\t<!ELEMENT year (#PCDATA)>\n"
+ "\t\t\t<!ELEMENT publisher (#PCDATA)>\n"
+ "\t\t\t<!ELEMENT notes (#PCDATA)>\n"
+ "\t\t\t<!ELEMENT info EMPTY>\n"
+ "\t\t\t\t<!ATTLIST info name CDATA #REQUIRED>\n"
+ "\t\t\t\t<!ATTLIST info value CDATA #IMPLIED>\n"
+ "\t\t\t<!ELEMENT sharedfeat EMPTY>\n"
+ "\t\t\t\t<!ATTLIST sharedfeat name CDATA #REQUIRED>\n"
+ "\t\t\t\t<!ATTLIST sharedfeat value CDATA #IMPLIED>\n"
+ "\t\t\t<!ELEMENT part (feature*, dataarea*, diskarea*, dipswitch*)>\n"
+ "\t\t\t\t<!ATTLIST part name CDATA #REQUIRED>\n"
+ "\t\t\t\t<!ATTLIST part interface CDATA #REQUIRED>\n"
+ "\t\t\t\t<!ELEMENT feature EMPTY>\n"
+ "\t\t\t\t\t<!ATTLIST feature name CDATA #REQUIRED>\n"
+ "\t\t\t\t\t<!ATTLIST feature value CDATA #IMPLIED>\n"
+ "\t\t\t\t<!ELEMENT dataarea (rom*)>\n"
+ "\t\t\t\t\t<!ATTLIST dataarea name CDATA #REQUIRED>\n"
+ "\t\t\t\t\t<!ATTLIST dataarea size CDATA #REQUIRED>\n"
+ "\t\t\t\t\t<!ATTLIST dataarea databits (8|16|32|64) \"8\">\n"
+ "\t\t\t\t\t<!ATTLIST dataarea endian (big|little) \"little\">\n"
+ "\t\t\t\t\t<!ELEMENT rom EMPTY>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom name CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom size CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom length CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom crc CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom sha1 CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom offset CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom value CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST rom status (baddump|nodump|good) \"good\">\n"
+ "\t\t\t\t\t\t<!ATTLIST rom loadflag (load16_byte|load16_word|load16_word_swap|load32_byte|load32_word|load32_word_swap|load32_dword|load64_word|load64_word_swap|reload|fill|continue|reload_plain) #IMPLIED>\n"
+ "\t\t\t\t<!ELEMENT diskarea (disk*)>\n"
+ "\t\t\t\t\t<!ATTLIST diskarea name CDATA #REQUIRED>\n"
+ "\t\t\t\t\t<!ELEMENT disk EMPTY>\n"
+ "\t\t\t\t\t\t<!ATTLIST disk name CDATA #REQUIRED>\n"
+ "\t\t\t\t\t\t<!ATTLIST disk sha1 CDATA #IMPLIED>\n"
+ "\t\t\t\t\t\t<!ATTLIST disk status (baddump|nodump|good) \"good\">\n"
+ "\t\t\t\t\t\t<!ATTLIST disk writeable (yes|no) \"no\">\n"
+ "\t\t\t\t<!ELEMENT dipswitch (dipvalue*)>\n"
+ "\t\t\t\t\t<!ATTLIST dipswitch name CDATA #REQUIRED>\n"
+ "\t\t\t\t\t<!ATTLIST dipswitch tag CDATA #REQUIRED>\n"
+ "\t\t\t\t\t<!ATTLIST dipswitch mask CDATA #REQUIRED>\n"
+ "\t\t\t\t\t<!ELEMENT dipvalue EMPTY>\n"
+ "\t\t\t\t\t\t<!ATTLIST dipvalue name CDATA #REQUIRED>\n"
+ "\t\t\t\t\t\t<!ATTLIST dipvalue value CDATA #REQUIRED>\n"
+ "\t\t\t\t\t\t<!ATTLIST dipvalue default (yes|no) \"no\">\n"
"]>\n\n";
void cli_frontend::output_single_softlist(std::ostream &out, software_list_device &swlistdev)
{
- util::stream_format(out, "\t<softwarelist name=\"%s\" description=\"%s\">\n", swlistdev.list_name(), util::xml::normalize_string(swlistdev.description().c_str()));
+ using util::xml::normalize_string;
+
+ util::stream_format(out, "\t<softwarelist name=\"%s\" description=\"%s\">\n", swlistdev.list_name(), normalize_string(swlistdev.description()));
for (const software_info &swinfo : swlistdev.get_info())
{
- util::stream_format(out, "\t\t<software name=\"%s\"", util::xml::normalize_string(swinfo.shortname().c_str()));
+ util::stream_format(out, "\t\t<software name=\"%s\"", normalize_string(swinfo.shortname()));
if (!swinfo.parentname().empty())
- util::stream_format(out, " cloneof=\"%s\"", util::xml::normalize_string(swinfo.parentname().c_str()));
+ util::stream_format(out, " cloneof=\"%s\"", normalize_string(swinfo.parentname()));
if (swinfo.supported() == software_support::PARTIALLY_SUPPORTED)
out << " supported=\"partial\"";
else if (swinfo.supported() == software_support::UNSUPPORTED)
out << " supported=\"no\"";
out << ">\n";
- util::stream_format(out, "\t\t\t<description>%s</description>\n", util::xml::normalize_string(swinfo.longname().c_str()));
- util::stream_format(out, "\t\t\t<year>%s</year>\n", util::xml::normalize_string(swinfo.year().c_str()));
- util::stream_format(out, "\t\t\t<publisher>%s</publisher>\n", util::xml::normalize_string(swinfo.publisher().c_str()));
+ util::stream_format(out, "\t\t\t<description>%s</description>\n", normalize_string(swinfo.longname()));
+ util::stream_format(out, "\t\t\t<year>%s</year>\n", normalize_string(swinfo.year()));
+ util::stream_format(out, "\t\t\t<publisher>%s</publisher>\n", normalize_string(swinfo.publisher()));
for (const auto &flist : swinfo.info())
- util::stream_format(out, "\t\t\t<info name=\"%s\" value=\"%s\"/>\n", flist.name(), util::xml::normalize_string(flist.value().c_str()));
+ util::stream_format(out, "\t\t\t<info name=\"%s\" value=\"%s\"/>\n", flist.name(), normalize_string(flist.value()));
for (const auto &flist : swinfo.shared_features())
- util::stream_format(out, "\t\t\t<sharedfeat name=\"%s\" value=\"%s\"/>\n", flist.name(), util::xml::normalize_string(flist.value().c_str()));
+ util::stream_format(out, "\t\t\t<sharedfeat name=\"%s\" value=\"%s\"/>\n", flist.name(), normalize_string(flist.value()));
for (const software_part &part : swinfo.parts())
{
- util::stream_format(out, "\t\t\t<part name=\"%s\"", util::xml::normalize_string(part.name().c_str()));
+ util::stream_format(out, "\t\t\t<part name=\"%s\"", normalize_string(part.name()));
if (!part.interface().empty())
- util::stream_format(out, " interface=\"%s\"", util::xml::normalize_string(part.interface().c_str()));
+ util::stream_format(out, " interface=\"%s\"", normalize_string(part.interface()));
out << ">\n";
for (const auto &flist : part.features())
- util::stream_format(out, "\t\t\t\t<feature name=\"%s\" value=\"%s\" />\n", flist.name(), util::xml::normalize_string(flist.value().c_str()));
+ util::stream_format(out, "\t\t\t\t<feature name=\"%s\" value=\"%s\" />\n", flist.name(), normalize_string(flist.value()));
// TODO: display ROM region information
for (const rom_entry *region = part.romdata().data(); region; region = rom_next_region(region))
@@ -1156,18 +1234,18 @@ void cli_frontend::output_single_softlist(std::ostream &out, software_list_devic
int is_disk = ROMREGION_ISDISKDATA(region);
if (!is_disk)
- util::stream_format(out, "\t\t\t\t<dataarea name=\"%s\" size=\"%d\">\n", util::xml::normalize_string(region->name().c_str()), region->get_length());
+ util::stream_format(out, "\t\t\t\t<dataarea name=\"%s\" size=\"%d\">\n", normalize_string(region->name()), region->get_length());
else
- util::stream_format(out, "\t\t\t\t<diskarea name=\"%s\">\n", util::xml::normalize_string(region->name().c_str()));
+ util::stream_format(out, "\t\t\t\t<diskarea name=\"%s\">\n", normalize_string(region->name()));
for (const rom_entry *rom = rom_first_file(region); rom && !ROMENTRY_ISREGIONEND(rom); rom++)
{
if (ROMENTRY_ISFILE(rom))
{
if (!is_disk)
- util::stream_format(out, "\t\t\t\t\t<rom name=\"%s\" size=\"%d\"", util::xml::normalize_string(ROM_GETNAME(rom)), rom_file_size(rom));
+ util::stream_format(out, "\t\t\t\t\t<rom name=\"%s\" size=\"%d\"", normalize_string(ROM_GETNAME(rom)), rom_file_size(rom));
else
- util::stream_format(out, "\t\t\t\t\t<disk name=\"%s\"", util::xml::normalize_string(ROM_GETNAME(rom)));
+ util::stream_format(out, "\t\t\t\t\t<disk name=\"%s\"", normalize_string(ROM_GETNAME(rom)));
// dump checksum information only if there is a known dump
util::hash_collection hashes(rom->hashdata());
@@ -1283,27 +1361,24 @@ void cli_frontend::listsoftware(const std::vector<std::string> &args)
-------------------------------------------------*/
void cli_frontend::verifysoftware(const std::vector<std::string> &args)
{
- const char *gamename = args.empty() ? "*" : args[0].c_str();
+ char const *const gamename = args.empty() ? "*" : args[0].c_str();
+
+ // determine which drivers to process; return an error if none found
+ driver_enumerator drivlist(m_options, gamename);
+ if (!drivlist.count())
+ throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename);
std::unordered_set<std::string> list_map;
unsigned correct = 0;
unsigned incorrect = 0;
unsigned notfound = 0;
- unsigned matched = 0;
unsigned nrlists = 0;
- // determine which drivers to process; return an error if none found
- driver_enumerator drivlist(m_options, gamename);
- if (drivlist.count() == 0)
- throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename);
-
media_auditor auditor(drivlist);
util::ovectorstream summary_string;
while (drivlist.next())
{
- matched++;
-
for (software_list_device &swlistdev : software_list_device_enumerator(drivlist.config()->root_device()))
{
if (swlistdev.is_original())
@@ -1333,18 +1408,19 @@ void cli_frontend::verifysoftware(const std::vector<std::string> &args)
util::archive_file::cache_clear();
// return an error if none found
- if (matched == 0)
- throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", gamename);
-
- // if we didn't get anything at all, display a generic end message
- if (matched > 0 && correct == 0 && incorrect == 0)
+ if (!nrlists)
{
- throw emu_fatalerror(EMU_ERR_MISSING_FILES, "romset \"%s\" has no software entries defined!\n", gamename);
+ throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No software list items are defined for systems matching '%s'", gamename);
+ }
+ else if (!correct && !incorrect)
+ {
+ // if we didn't get anything at all, display a generic end message
+ throw emu_fatalerror(EMU_ERR_MISSING_FILES, "No software items found for systems matching '%s'", gamename);
}
- // otherwise, print a summary
else
{
- if (incorrect > 0)
+ // otherwise, print a summary
+ if (incorrect)
throw emu_fatalerror(EMU_ERR_MISSING_FILES, "%u romsets found in %u software lists, %u were OK.\n", correct + incorrect, nrlists, correct);
osd_printf_info("%u romsets found in %u software lists, %u romsets were OK.\n", correct, nrlists, correct);
}
@@ -1368,7 +1444,7 @@ void cli_frontend::getsoftlist(const std::vector<std::string> &args)
{
for (software_list_device &swlistdev : software_list_device_enumerator(root))
{
- if (core_strwildcmp(gamename, swlistdev.list_name().c_str()) == 0 && list_map.insert(swlistdev.list_name()).second)
+ if (core_strwildcmp(gamename, swlistdev.list_name()) == 0 && list_map.insert(swlistdev.list_name()).second)
{
if (!swlistdev.get_info().empty())
{
@@ -1413,7 +1489,7 @@ void cli_frontend::verifysoftlist(const std::vector<std::string> &args)
{
for (software_list_device &swlistdev : software_list_device_enumerator(drivlist.config()->root_device()))
{
- if (core_strwildcmp(gamename, swlistdev.list_name().c_str()) == 0 && list_map.insert(swlistdev.list_name()).second)
+ if (core_strwildcmp(gamename, swlistdev.list_name()) == 0 && list_map.insert(swlistdev.list_name()).second)
{
if (!swlistdev.get_info().empty())
{
@@ -1509,7 +1585,7 @@ void cli_frontend::romident(const std::vector<std::string> &args)
template <typename T, typename U> void cli_frontend::apply_action(const std::vector<std::string> &args, T &&drvact, U &&devact)
{
- bool const iswild((1U != args.size()) || core_iswildstr(args[0].c_str()));
+ bool const iswild((1U != args.size()) || core_iswildstr(args[0]));
std::vector<bool> matched(args.size(), false);
auto const included = [&args, &matched] (char const *name) -> bool
{
@@ -1520,7 +1596,7 @@ template <typename T, typename U> void cli_frontend::apply_action(const std::vec
auto it = matched.begin();
for (std::string const &pat : args)
{
- if (!core_strwildcmp(pat.c_str(), name))
+ if (!core_strwildcmp(pat, name))
{
result = true;
*it = true;
@@ -1616,6 +1692,7 @@ const cli_frontend::info_command_struct *cli_frontend::find_command(const std::s
{ CLICOMMAND_LISTCRC, 0, -1, &cli_frontend::listcrc, "[system name]" },
{ CLICOMMAND_LISTDEVICES, 0, 1, &cli_frontend::listdevices, "[system name]" },
{ CLICOMMAND_LISTSLOTS, 0, 1, &cli_frontend::listslots, "[system name]" },
+ { CLICOMMAND_LISTBIOS, 0, 1, &cli_frontend::listbios, "[system name]" },
{ CLICOMMAND_LISTROMS, 0, -1, &cli_frontend::listroms, "[pattern] ..." },
{ CLICOMMAND_LISTSAMPLES, 0, 1, &cli_frontend::listsamples, "[system name]" },
{ CLICOMMAND_VERIFYROMS, 0, -1, &cli_frontend::verifyroms, "[pattern] ..." },
@@ -1685,36 +1762,41 @@ void cli_frontend::execute_commands(std::string_view exename)
// createconfig?
if (m_options.command() == CLICOMMAND_CREATECONFIG)
{
- // attempt to open the output file
+ // attempt to open the output file and generate the updated (mame).ini
emu_file file(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
if (file.open(std::string(emulator_info::get_configname()) + ".ini"))
throw emu_fatalerror("Unable to create file %s.ini\n",emulator_info::get_configname());
- // generate the updated INI
file.puts(m_options.output_ini());
+ // ui.ini
ui_options ui_opts;
emu_file file_ui(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
if (file_ui.open("ui.ini"))
throw emu_fatalerror("Unable to create file ui.ini\n");
- // generate the updated INI
file_ui.puts(ui_opts.output_ini());
+ // plugin.ini
plugin_options plugin_opts;
path_iterator iter(m_options.plugins_path());
std::string pluginpath;
while (iter.next(pluginpath))
- {
- osd_subst_env(pluginpath, pluginpath);
plugin_opts.scan_directory(pluginpath, true);
- }
- emu_file file_plugin(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- if (file_plugin.open("plugin.ini"))
- throw emu_fatalerror("Unable to create file plugin.ini\n");
- // generate the updated INI
- file_plugin.puts(plugin_opts.output_ini());
+ std::string plugins(plugin_opts.output_ini());
+
+ // only update the file when it found plugins
+ if (!plugins.empty())
+ {
+ emu_file file_plugin(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ if (file_plugin.open("plugin.ini"))
+ throw emu_fatalerror("Unable to create file plugin.ini\n");
+
+ file_plugin.puts(plugins);
+ }
+ else
+ osd_printf_error("Skipped plugin.ini, could not find any plugins\n");
return;
}
diff --git a/src/frontend/mame/clifront.h b/src/frontend/mame/clifront.h
index bc0ab3baab8..0857a3d98b1 100644
--- a/src/frontend/mame/clifront.h
+++ b/src/frontend/mame/clifront.h
@@ -54,6 +54,7 @@ private:
void listbrothers(const std::vector<std::string> &args);
void listcrc(const std::vector<std::string> &args);
void listroms(const std::vector<std::string> &args);
+ void listbios(const std::vector<std::string> &args);
void listsamples(const std::vector<std::string> &args);
void listdevices(const std::vector<std::string> &args);
void listslots(const std::vector<std::string> &args);
diff --git a/src/frontend/mame/infoxml.cpp b/src/frontend/mame/infoxml.cpp
index 8ff033194bf..37d649a1131 100644
--- a/src/frontend/mame/infoxml.cpp
+++ b/src/frontend/mame/infoxml.cpp
@@ -13,16 +13,20 @@
#include "mameopts.h"
+// devices
#include "machine/ram.h"
#include "sound/samples.h"
+// emu
#include "config.h"
#include "drivenum.h"
+#include "main.h"
#include "romload.h"
#include "screen.h"
#include "softlist_dev.h"
#include "speaker.h"
+// lib/util
#include "corestr.h"
#include "xmlfile.h"
@@ -98,10 +102,8 @@ private:
};
-typedef std::set<std::add_pointer_t<device_type>, device_type_compare> device_type_set;
-
-std::string normalize_string(const char *string);
-std::string normalize_string(std::string_view string);
+using device_type_set = std::set<std::add_pointer_t<device_type>, device_type_compare>;
+using device_type_vector = std::vector<std::add_pointer_t<device_type> >;
// internal helper
void output_header(std::ostream &out, bool dtd);
@@ -110,7 +112,7 @@ void output_footer(std::ostream &out);
void output_one(std::ostream &out, driver_enumerator &drivlist, const game_driver &driver, device_type_set *devtypes);
void output_sampleof(std::ostream &out, device_t &device);
void output_bios(std::ostream &out, device_t const &device);
-void output_rom(std::ostream &out, machine_config &config, driver_enumerator *drivlist, const game_driver *driver, device_t &device);
+void output_rom(std::ostream &out, machine_config &config, driver_list const *drivlist, const game_driver *driver, device_t &device);
void output_device_refs(std::ostream &out, device_t &root);
void output_sample(std::ostream &out, device_t &device);
void output_chips(std::ostream &out, device_t &device, const char *root_tag);
@@ -131,7 +133,7 @@ void output_ramoptions(std::ostream &out, device_t &root);
void output_one_device(std::ostream &out, machine_config &config, device_t &device, const char *devtag);
void output_devices(std::ostream &out, emu_options &lookup_options, device_type_set const *filter);
-char const *get_merge_name(driver_enumerator &drivlist, game_driver const &driver, util::hash_collection const &romhashes);
+char const *get_merge_name(driver_list const &drivlist, game_driver const &driver, util::hash_collection const &romhashes);
char const *get_merge_name(machine_config &config, device_t const &device, util::hash_collection const &romhashes);
char const *get_merge_name(tiny_rom_entry const *roms, util::hash_collection const &romhashes);
@@ -338,7 +340,7 @@ constexpr std::pair<device_t::feature_type, char const *> f_feature_names[] = {
//-------------------------------------------------
-// get_feature_name - get XML name for feature
+// feature_name - get XML name for feature
//-------------------------------------------------
char const *info_xml_creator::feature_name(device_t::feature_type feature)
@@ -356,6 +358,33 @@ char const *info_xml_creator::feature_name(device_t::feature_type feature)
//-------------------------------------------------
+// format_sourcefile - sanitise source file path
+//-------------------------------------------------
+
+std::string info_xml_creator::format_sourcefile(std::string_view path)
+{
+ using namespace std::literals;
+
+ if (auto prefix(path.rfind("src/mame/"sv)); std::string_view::npos != prefix)
+ path.remove_prefix(prefix + 9);
+ else if (auto prefix(path.rfind("src\\mame\\"sv)); std::string_view::npos != prefix)
+ path.remove_prefix(prefix + 9);
+ else if (auto prefix(path.rfind("/src/"sv)); std::string_view::npos != prefix)
+ path.remove_prefix(prefix + 5);
+ else if (auto prefix(path.rfind("\\src\\"sv)); std::string_view::npos != prefix)
+ path.remove_prefix(prefix + 5);
+ else if (path.substr(0, 4) == "src/"sv)
+ path.remove_prefix(4);
+ else if (path.substr(0, 4) == "src\\"sv)
+ path.remove_prefix(4);
+
+ std::string result(path);
+ std::replace(result.begin(), result.end(), '\\', '/');
+ return result;
+}
+
+
+//-------------------------------------------------
// info_xml_creator - constructor
//-------------------------------------------------
@@ -388,7 +417,7 @@ void info_xml_creator::output(std::ostream &out, const std::vector<std::string>
auto it = matched.begin();
for (const std::string &pat : patterns)
{
- if (!core_strwildcmp(pat.c_str(), shortname))
+ if (!core_strwildcmp(pat, shortname))
{
// this driver matches the pattern - tell the caller
result = true;
@@ -397,7 +426,7 @@ void info_xml_creator::output(std::ostream &out, const std::vector<std::string>
if (!*it)
{
*it = true;
- if (!core_iswildstr(pat.c_str()))
+ if (!core_iswildstr(pat))
{
exact_matches++;
@@ -436,17 +465,18 @@ void info_xml_creator::output(std::ostream &out, const std::function<bool(const
prepared_info() = default;
prepared_info(const prepared_info &) = delete;
prepared_info(prepared_info &&) = default;
+#if defined(_CPPLIB_VER) && defined(_MSVC_STL_VERSION)
+ // MSVCPRT currently requires default-constructible std::future promise types to be assignable
+ // remove this workaround when that's fixed
+ prepared_info &operator=(const prepared_info &) = default;
+#else
prepared_info &operator=(const prepared_info &) = delete;
+#endif
std::string m_xml_snippet;
device_type_set m_dev_set;
};
- // TODO: maybe not the best place for this as it affects the stream passed in
- // if the device part is threaded, the local streams used by the tasks can be
- // imbued and the stream passed in can be left alone
- out.imbue(std::locale::classic());
-
// prepare a driver enumerator and the queue
driver_enumerator drivlist(m_lookup_options);
device_filter devfilter(filter);
@@ -454,14 +484,14 @@ void info_xml_creator::output(std::ostream &out, const std::function<bool(const
bool header_outputted = false;
// essentially a local method to emit the header if necessary
- auto output_header_if_necessary = [this, &header_outputted](std::ostream &out)
- {
- if (!header_outputted)
- {
- output_header(out, m_dtd);
- header_outputted = true;
- }
- };
+ auto const output_header_if_necessary = [this, &header_outputted] (std::ostream &out)
+ {
+ if (!header_outputted)
+ {
+ output_header(out, m_dtd);
+ header_outputted = true;
+ }
+ };
// only keep a device set when we're asked to track it
std::optional<device_type_set> devset;
@@ -470,14 +500,14 @@ void info_xml_creator::output(std::ostream &out, const std::function<bool(const
// prepare a queue of tasks - this is a FIFO queue because of the
// need to be deterministic
- std::queue<std::future<prepared_info>> tasks;
+ std::queue<std::future<prepared_info> > tasks;
// while we want to be deterministic, asynchronous task scheduling is not; so we want to
// track the amount of active tasks so that we can keep on spawning tasks even if we're
// waiting on the task in the front of the queue
std::atomic<unsigned int> active_task_count = 0;
- unsigned int maximum_active_task_count = std::thread::hardware_concurrency() + 10;
- unsigned int maximum_outstanding_task_count = maximum_active_task_count + 20;
+ unsigned int const maximum_active_task_count = std::thread::hardware_concurrency() + 10;
+ unsigned int const maximum_outstanding_task_count = maximum_active_task_count + 20;
// loop until we're done enumerating drivers, and until there are no outstanding tasks
while (!filtered_drivlist.done() || !tasks.empty())
@@ -485,32 +515,32 @@ void info_xml_creator::output(std::ostream &out, const std::function<bool(const
// loop until there are as many outstanding tasks as possible (we want to separately cap outstanding
// tasks and active tasks)
while (!filtered_drivlist.done()
- && active_task_count < maximum_active_task_count
- && tasks.size() < maximum_outstanding_task_count)
+ && (active_task_count < maximum_active_task_count)
+ && (tasks.size() < maximum_outstanding_task_count))
{
// we want to launch a task; grab a packet of drivers to process
- std::vector<std::reference_wrapper<const game_driver>> drivers = filtered_drivlist.next(20);
+ std::vector<std::reference_wrapper<const game_driver> > drivers = filtered_drivlist.next(20);
if (drivers.empty())
break;
- // do the dirty work asychronously
- auto task_proc = [&drivlist, drivers{ std::move(drivers) }, include_devices, &active_task_count]
- {
- prepared_info result;
- std::ostringstream stream;
- stream.imbue(std::locale::classic());
+ // do the dirty work asynchronously
+ auto task_proc = [&drivlist, drivers = std::move(drivers), include_devices, &active_task_count]
+ {
+ prepared_info result;
+ std::ostringstream stream;
+ stream.imbue(std::locale::classic());
- // output each of the drivers
- for (const game_driver &driver : drivers)
- output_one(stream, drivlist, driver, include_devices ? &result.m_dev_set : nullptr);
+ // output each of the drivers
+ for (const game_driver &driver : drivers)
+ output_one(stream, drivlist, driver, include_devices ? &result.m_dev_set : nullptr);
- // capture the XML snippet
- result.m_xml_snippet = stream.str();
+ // capture the XML snippet
+ result.m_xml_snippet = std::move(stream).str();
- // we're done with the task; decrement the counter and return
- active_task_count--;
- return result;
- };
+ // we're done with the task; decrement the counter and return
+ active_task_count--;
+ return result;
+ };
// add this task to the queue
active_task_count++;
@@ -520,8 +550,7 @@ void info_xml_creator::output(std::ostream &out, const std::function<bool(const
// we've put as many outstanding tasks out as we can; are there any tasks outstanding?
if (!tasks.empty())
{
- // wait for the task at the front of the queue to complete and get the info, in the
- // spirit of determinism
+ // wait for the oldest task to complete and get the info, in the spirit of determinism
prepared_info pi = tasks.front().get();
tasks.pop();
@@ -571,39 +600,6 @@ namespace
{
//-------------------------------------------------
-// normalize_string
-//-------------------------------------------------
-
-std::string normalize_string(const char *string)
-{
- if (string)
- return normalize_string(std::string_view(string));
- else
- return std::string();
-}
-
-std::string normalize_string(std::string_view string)
-{
- std::string result;
- result.reserve(string.length());
-
- for (char ch : string)
- {
- switch (ch)
- {
- case '\"': result.append("&quot;"); break;
- case '&': result.append("&amp;"); break;
- case '<': result.append("&lt;"); break;
- case '>': result.append("&gt;"); break;
- default: result.append(1, ch); break;
- }
- }
-
- return result;
-}
-
-
-//-------------------------------------------------
// device_filter::filter - apply the filter, if
// present
//-------------------------------------------------
@@ -619,9 +615,10 @@ bool device_filter::filter(const char *shortname)
// of game_drivers, while applying filters
//-------------------------------------------------
-std::vector<std::reference_wrapper<const game_driver>> filtered_driver_enumerator::next(int count)
+std::vector<std::reference_wrapper<const game_driver> > filtered_driver_enumerator::next(int count)
{
- std::vector<std::reference_wrapper<const game_driver>> results;
+ std::vector<std::reference_wrapper<const game_driver> > results;
+ results.reserve(count);
while (!done() && results.size() < count)
{
if (!m_drivlist.next())
@@ -633,7 +630,7 @@ std::vector<std::reference_wrapper<const game_driver>> filtered_driver_enumerato
else if (m_devfilter.filter(m_drivlist.driver().name))
{
const game_driver &driver(m_drivlist.driver());
- results.push_back(driver);
+ results.emplace_back(driver);
}
}
return results;
@@ -659,6 +656,7 @@ void output_header(std::ostream &out, bool dtd)
}
// top-level tag
+ assert(emulator_info::get_build_version() != nullptr);
util::stream_format(out,
"<%s build=\"%s\" debug=\""
#ifdef MAME_DEBUG
@@ -668,7 +666,7 @@ void output_header(std::ostream &out, bool dtd)
#endif
"\" mameconfig=\"%d\">\n",
XML_ROOT,
- normalize_string(emulator_info::get_build_version()),
+ util::xml::normalize_string(emulator_info::get_build_version()),
configuration_manager::CONFIG_VERSION);
}
@@ -691,6 +689,8 @@ void output_footer(std::ostream &out)
void output_one(std::ostream &out, driver_enumerator &drivlist, const game_driver &driver, device_type_set *devtypes)
{
+ using util::xml::normalize_string;
+
machine_config config(driver, drivlist.options());
device_enumerator iter(config.root_device());
@@ -741,12 +741,8 @@ void output_one(std::ostream &out, driver_enumerator &drivlist, const game_drive
// print the header and the machine name
util::stream_format(out, "\t<%s name=\"%s\"", XML_TOP, normalize_string(driver.name));
- // strip away any path information from the source_file and output it
- const char *start = strrchr(driver.type.source(), '/');
- if (!start)
- start = strrchr(driver.type.source(), '\\');
- start = start ? (start + 1) : driver.type.source();
- util::stream_format(out, " sourcefile=\"%s\"", normalize_string(start));
+ // strip away extra path information from the source file and output it
+ util::stream_format(out, " sourcefile=\"%s\"", normalize_string(info_xml_creator::format_sourcefile(driver.type.source())));
// append bios and runnable flags
if (driver.flags & machine_flags::IS_BIOS_ROOT)
@@ -809,6 +805,8 @@ void output_one(std::ostream &out, driver_enumerator &drivlist, const game_drive
void output_one_device(std::ostream &out, machine_config &config, device_t &device, const char *devtag)
{
+ using util::xml::normalize_string;
+
bool has_speaker = false, has_input = false;
// check if the device adds speakers to the system
sound_interface_enumerator snditer(device);
@@ -838,9 +836,7 @@ void output_one_device(std::ostream &out, machine_config &config, device_t &devi
// start to output info
util::stream_format(out, "\t<%s name=\"%s\"", XML_TOP, normalize_string(device.shortname()));
- std::string src(device.source());
- strreplace(src,"../", "");
- util::stream_format(out, " sourcefile=\"%s\" isdevice=\"yes\" runnable=\"no\"", normalize_string(src));
+ util::stream_format(out, " sourcefile=\"%s\" isdevice=\"yes\" runnable=\"no\"", normalize_string(info_xml_creator::format_sourcefile(device.source())));
auto const parent(device.type().parent_rom_device_type());
if (parent)
util::stream_format(out, " romof=\"%s\"", normalize_string(parent->shortname()));
@@ -879,38 +875,85 @@ void output_one_device(std::ostream &out, machine_config &config, device_t &devi
void output_devices(std::ostream &out, emu_options &lookup_options, device_type_set const *filter)
{
- // get config for empty machine
- machine_config config(GAME_NAME(___empty), lookup_options);
-
- auto const action = [&config, &out] (device_type type)
+ auto const action = [&lookup_options, &out] (auto &types, auto deref)
{
- // add it at the root of the machine config
- device_t *dev;
+ // machinery for making output order deterministic and capping outstanding tasks
+ std::queue<std::future<std::string> > tasks;
+ std::atomic<unsigned int> active_task_count = 0;
+ unsigned int const maximum_active_task_count = std::thread::hardware_concurrency() + 10;
+ unsigned int const maximum_outstanding_task_count = maximum_active_task_count + 20;
+
+ // loop until we're done enumerating devices and there are no outstanding tasks
+ auto it = std::begin(types);
+ while ((std::end(types) != it) || !tasks.empty())
{
- machine_config::token const tok(config.begin_configuration(config.root_device()));
- dev = config.device_add("_tmp", type, 0);
- }
+ // look until there are as many outstanding tasks as possible
+ while ((std::end(types) != it)
+ && (active_task_count < maximum_active_task_count)
+ && (tasks.size() < maximum_outstanding_task_count))
+ {
+ device_type_vector batch;
+ batch.reserve(10);
+ while ((std::end(types) != it) && (batch.size() < 10))
+ batch.emplace_back(deref(*it++));
+ if (batch.empty())
+ break;
- // notify this device and all its subdevices that they are now configured
- for (device_t &device : device_enumerator(*dev))
- if (!device.configured())
- device.config_complete();
+ // do the dirty work asynchronously
+ auto task_proc = [&active_task_count, &lookup_options, batch = std::move(batch)]
+ {
+ // use a single machine configuration and stream for a batch of devices
+ machine_config config(GAME_NAME(___empty), lookup_options);
+ std::ostringstream stream;
+ stream.imbue(std::locale::classic());
+ for (auto type : batch)
+ {
+ // add it at the root of the machine config
+ device_t *dev;
+ {
+ machine_config::token const tok(config.begin_configuration(config.root_device()));
+ dev = config.device_add("_tmp", *type, 0);
+ }
+
+ // notify this device and all its subdevices that they are now configured
+ for (device_t &device : device_enumerator(*dev))
+ if (!device.configured())
+ device.config_complete();
+
+ // print details and remove it
+ output_one_device(stream, config, *dev, dev->tag());
+ machine_config::token const tok(config.begin_configuration(config.root_device()));
+ config.device_remove("_tmp");
+ }
+
+ // we're done with the task; decrement the counter and return
+ active_task_count--;
+ return std::move(stream).str();
+ };
+
+ // add this task to the queue
+ active_task_count++;
+ tasks.emplace(std::async(std::launch::async, std::move(task_proc)));
+ }
+
+ // we've put as many outstanding tasks out as we can; are there any tasks outstanding?
+ if (!tasks.empty())
+ {
+ // wait for the oldest task to complete and get the info, in the spirit of determinism
+ std::string snippet = tasks.front().get();
+ tasks.pop();
- // print details and remove it
- output_one_device(out, config, *dev, dev->tag());
- machine_config::token const tok(config.begin_configuration(config.root_device()));
- config.device_remove("_tmp");
+ // emit whatever XML we accumulated in the task
+ out << snippet;
+ }
+ }
};
// run through devices
if (filter)
- {
- for (std::add_pointer_t<device_type> type : *filter) action(*type);
- }
+ action(*filter, [] (auto &x) { return x; });
else
- {
- for (device_type type : registered_device_types) action(type);
- }
+ action(registered_device_types, [] (auto &x) { return &x; });
}
@@ -923,7 +966,7 @@ void output_device_refs(std::ostream &out, device_t &root)
{
for (device_t &device : device_enumerator(root))
if (&device != &root)
- util::stream_format(out, "\t\t<device_ref name=\"%s\"/>\n", normalize_string(device.shortname()));
+ util::stream_format(out, "\t\t<device_ref name=\"%s\"/>\n", util::xml::normalize_string(device.shortname()));
}
@@ -940,7 +983,7 @@ void output_sampleof(std::ostream &out, device_t &device)
samples_iterator sampiter(samples);
if (sampiter.altbasename() != nullptr)
{
- util::stream_format(out, " sampleof=\"%s\"", normalize_string(sampiter.altbasename()));
+ util::stream_format(out, " sampleof=\"%s\"", util::xml::normalize_string(sampiter.altbasename()));
// must stop here, as there can only be one attribute of the same name
return;
@@ -968,8 +1011,8 @@ void output_bios(std::ostream &out, device_t const &device)
{
// output extracted name and descriptions'
out << "\t\t<biosset";
- util::stream_format(out, " name=\"%s\"", normalize_string(bios.get_name()));
- util::stream_format(out, " description=\"%s\"", normalize_string(bios.get_description()));
+ util::stream_format(out, " name=\"%s\"", util::xml::normalize_string(bios.get_name()));
+ util::stream_format(out, " description=\"%s\"", util::xml::normalize_string(bios.get_description()));
if (defaultname && !std::strcmp(defaultname, bios.get_name()))
out << " default=\"yes\"";
out << "/>\n";
@@ -982,7 +1025,7 @@ void output_bios(std::ostream &out, device_t const &device)
// the XML output
//-------------------------------------------------
-void output_rom(std::ostream &out, machine_config &config, driver_enumerator *drivlist, const game_driver *driver, device_t &device)
+void output_rom(std::ostream &out, machine_config &config, driver_list const *drivlist, const game_driver *driver, device_t &device)
{
enum class type { BIOS, NORMAL, DISK };
std::map<u32, char const *> biosnames;
@@ -1040,6 +1083,8 @@ void output_rom(std::ostream &out, machine_config &config, driver_enumerator *dr
tiny_rom_entry const *region(nullptr);
for (tiny_rom_entry const *rom = device.rom_region(); rom && !ROMENTRY_ISEND(rom); ++rom)
{
+ using util::xml::normalize_string;
+
if (ROMENTRY_ISREGION(rom))
region = rom;
else if (ROMENTRY_ISSYSTEM_BIOS(rom))
@@ -1053,7 +1098,7 @@ void output_rom(std::ostream &out, machine_config &config, driver_enumerator *dr
if ((type::DISK == pass) != is_disk)
continue;
- // BIOS ROMs only apply to bioses
+ // BIOS ROMs only apply to BIOSes
// FIXME: disk images associated with a system BIOS will never be listed
u32 const biosno(ROM_GETBIOSFLAGS(rom));
if ((type::BIOS == pass) != bool(biosno))
@@ -1134,7 +1179,7 @@ void output_sample(std::ostream &out, device_t &device)
continue;
// output the sample name
- util::stream_format(out, "\t\t<sample name=\"%s\"/>\n", normalize_string(samplename));
+ util::stream_format(out, "\t\t<sample name=\"%s\"/>\n", util::xml::normalize_string(samplename));
}
}
}
@@ -1147,6 +1192,8 @@ void output_sample(std::ostream &out, device_t &device)
void output_chips(std::ostream &out, device_t &device, const char *root_tag)
{
+ using util::xml::normalize_string;
+
// iterate over executable devices
for (device_execute_interface &exec : execute_interface_enumerator(device))
{
@@ -1199,7 +1246,7 @@ void output_display(std::ostream &out, device_t &device, machine_flags::type con
std::string newtag(screendev.tag()), oldtag(":");
newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length());
- util::stream_format(out, "\t\t<display tag=\"%s\"", normalize_string(newtag));
+ util::stream_format(out, "\t\t<display tag=\"%s\"", util::xml::normalize_string(newtag));
switch (screendev.screen_type())
{
@@ -1311,7 +1358,7 @@ void output_ioport_condition(std::ostream &out, const ioport_condition &conditio
case ioport_condition::NOTLESSTHAN: rel = "ge"; break;
}
- util::stream_format(out, "<condition tag=\"%s\" mask=\"%u\" relation=\"%s\" value=\"%u\"/>\n", normalize_string(condition.tag()), condition.mask(), rel, condition.value());
+ util::stream_format(out, "<condition tag=\"%s\" mask=\"%u\" relation=\"%s\" value=\"%u\"/>\n", util::xml::normalize_string(condition.tag()), condition.mask(), rel, condition.value());
}
//-------------------------------------------------
@@ -1373,14 +1420,14 @@ void output_input(std::ostream &out, const ioport_list &portlist)
int player; // player which the input belongs to
int nbuttons; // total number of buttons
int reqbuttons; // total number of non-optional buttons
- int maxbuttons; // max index of buttons (using IPT_BUTTONn) [probably to be removed soonish]
+ uint32_t maxbuttons; // max index of buttons (using IPT_BUTTONn) [probably to be removed soonish]
int ways; // directions for joystick
bool analog; // is analog input?
- uint8_t helper[3]; // for dual joysticks [possibly to be removed soonish]
- int32_t min; // analog minimum value
- int32_t max; // analog maximum value
- int32_t sensitivity; // default analog sensitivity
- int32_t keydelta; // default analog keydelta
+ uint8_t helper[3]; // for dual joysticks [possibly to be removed soonish]
+ int32_t min; // analog minimum value
+ int32_t max; // analog maximum value
+ int32_t sensitivity; // default analog sensitivity
+ int32_t keydelta; // default analog keydelta
bool reverse; // default analog reverse setting
} control_info[CTRL_COUNT * CTRL_PCOUNT];
@@ -1388,7 +1435,7 @@ void output_input(std::ostream &out, const ioport_list &portlist)
// tracking info as we iterate
int nplayer = 0;
- int ncoin = 0;
+ uint32_t ncoin = 0;
bool service = false;
bool tilt = false;
@@ -1716,7 +1763,7 @@ void output_input(std::ostream &out, const ioport_list &portlist)
out << "\t\t<input";
util::stream_format(out, " players=\"%d\"", nplayer);
if (ncoin != 0)
- util::stream_format(out, " coins=\"%d\"", ncoin);
+ util::stream_format(out, " coins=\"%u\"", ncoin);
if (service)
util::stream_format(out, " service=\"yes\"");
if (tilt)
@@ -1727,6 +1774,8 @@ void output_input(std::ostream &out, const ioport_list &portlist)
for (auto & elem : control_info)
if (elem.type != nullptr)
{
+ using util::xml::normalize_string;
+
//printf("type %s - player %d - buttons %d\n", elem.type, elem.player, elem.nbuttons);
if (elem.analog)
{
@@ -1735,7 +1784,7 @@ void output_input(std::ostream &out, const ioport_list &portlist)
util::stream_format(out, " player=\"%d\"", elem.player);
if (elem.nbuttons > 0)
{
- util::stream_format(out, " buttons=\"%d\"", strcmp(elem.type, "stick") ? elem.nbuttons : elem.maxbuttons);
+ util::stream_format(out, " buttons=\"%u\"", strcmp(elem.type, "stick") ? elem.nbuttons : elem.maxbuttons);
if (elem.reqbuttons < elem.nbuttons)
util::stream_format(out, " reqbuttons=\"%d\"", elem.reqbuttons);
}
@@ -1761,7 +1810,7 @@ void output_input(std::ostream &out, const ioport_list &portlist)
util::stream_format(out, " player=\"%d\"", elem.player);
if (elem.nbuttons > 0)
{
- util::stream_format(out, " buttons=\"%d\"", strcmp(elem.type, "joy") ? elem.nbuttons : elem.maxbuttons);
+ util::stream_format(out, " buttons=\"%u\"", strcmp(elem.type, "joy") ? elem.nbuttons : elem.maxbuttons);
if (elem.reqbuttons < elem.nbuttons)
util::stream_format(out, " reqbuttons=\"%d\"", elem.reqbuttons);
}
@@ -1820,11 +1869,14 @@ void output_switches(std::ostream &out, const ioport_list &portlist, const char
for (ioport_field const &field : port.second->fields())
if (field.type() == type)
{
+ using util::xml::normalize_string;
+
std::string newtag(port.second->tag()), oldtag(":");
newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length());
// output the switch name information
- std::string const normalized_field_name(normalize_string(field.name()));
+ assert(field.specific_name() != nullptr);
+ std::string const normalized_field_name(normalize_string(field.specific_name()));
std::string const normalized_newtag(normalize_string(newtag));
util::stream_format(out, "\t\t<%s name=\"%s\" tag=\"%s\" mask=\"%u\">\n", outertag, normalized_field_name, normalized_newtag, field.mask());
if (!field.condition().none())
@@ -1871,7 +1923,7 @@ void output_ports(std::ostream &out, const ioport_list &portlist)
// cycle through ports
for (auto &port : portlist)
{
- util::stream_format(out, "\t\t<port tag=\"%s\">\n", normalize_string(port.second->tag()));
+ util::stream_format(out, "\t\t<port tag=\"%s\">\n", util::xml::normalize_string(port.second->tag()));
for (ioport_field const &field : port.second->fields())
{
if (field.is_analog())
@@ -1894,7 +1946,7 @@ void output_adjusters(std::ostream &out, const ioport_list &portlist)
for (ioport_field const &field : port.second->fields())
if (field.type() == IPT_ADJUSTER)
{
- util::stream_format(out, "\t\t<adjuster name=\"%s\" default=\"%d\"/>\n", normalize_string(field.name()), field.defvalue());
+ util::stream_format(out, "\t\t<adjuster name=\"%s\" default=\"%d\"/>\n", util::xml::normalize_string(field.specific_name()), field.defvalue());
}
}
@@ -2000,11 +2052,14 @@ void output_images(std::ostream &out, device_t &device, const char *root_tag)
{
if (strcmp(imagedev.device().tag(), device.tag()))
{
+ using util::xml::normalize_string;
+
bool loadable = imagedev.user_loadable();
std::string newtag(imagedev.device().tag()), oldtag(":");
newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length());
// print m_output device type
+ assert(imagedev.image_type_name() != nullptr);
util::stream_format(out, "\t\t<device type=\"%s\"", normalize_string(imagedev.image_type_name()));
// does this device have a tag?
@@ -2064,6 +2119,8 @@ void output_slots(std::ostream &out, machine_config &config, device_t &device, c
if (devtypes || listed)
{
+ using util::xml::normalize_string;
+
machine_config::token const tok(config.begin_configuration(slot.device()));
std::string newtag(slot.device().tag()), oldtag(":");
newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length());
@@ -2112,6 +2169,8 @@ void output_software_lists(std::ostream &out, device_t &root, const char *root_t
{
for (const software_list_device &swlist : software_list_device_enumerator(root))
{
+ using util::xml::normalize_string;
+
if (&static_cast<const device_t &>(swlist) == &root)
{
assert(swlist.list_name().empty());
@@ -2149,11 +2208,11 @@ void output_ramoptions(std::ostream &out, device_t &root)
{
assert(!havedefault);
havedefault = true;
- util::stream_format(out, "\t\t<ramoption name=\"%s\" default=\"yes\">%u</ramoption>\n", normalize_string(option.first), option.second);
+ util::stream_format(out, "\t\t<ramoption name=\"%s\" default=\"yes\">%u</ramoption>\n", util::xml::normalize_string(option.first), option.second);
}
else
{
- util::stream_format(out, "\t\t<ramoption name=\"%s\">%u</ramoption>\n", normalize_string(option.first), option.second);
+ util::stream_format(out, "\t\t<ramoption name=\"%s\">%u</ramoption>\n", util::xml::normalize_string(option.first), option.second);
}
}
if (!havedefault)
@@ -2169,7 +2228,7 @@ void output_ramoptions(std::ostream &out, device_t &root)
// parent set
//-------------------------------------------------
-char const *get_merge_name(driver_enumerator &drivlist, game_driver const &driver, util::hash_collection const &romhashes)
+char const *get_merge_name(driver_list const &drivlist, game_driver const &driver, util::hash_collection const &romhashes)
{
char const *result = nullptr;
diff --git a/src/frontend/mame/infoxml.h b/src/frontend/mame/infoxml.h
index 00a614bcd3a..06141ef83ba 100644
--- a/src/frontend/mame/infoxml.h
+++ b/src/frontend/mame/infoxml.h
@@ -16,6 +16,8 @@
#include "emuopts.h"
#include <functional>
+#include <string>
+#include <string_view>
#include <vector>
@@ -35,6 +37,7 @@ public:
void output(std::ostream &out, const std::function<bool(const char *shortname, bool &done)> &filter = { }, bool include_devices = true);
static char const *feature_name(device_t::feature_type feature);
+ static std::string format_sourcefile(std::string_view path);
private:
// internal state
diff --git a/src/frontend/mame/language.cpp b/src/frontend/mame/language.cpp
index 6602dffaa8e..3e660130ffd 100644
--- a/src/frontend/mame/language.cpp
+++ b/src/frontend/mame/language.cpp
@@ -12,6 +12,7 @@
#include "language.h"
#include "emuopts.h"
+#include "fileio.h"
#include "corestr.h"
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index 2cc98b971fe..6aa2d3840ec 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -21,14 +21,19 @@
#include "debugger.h"
#include "drivenum.h"
#include "emuopts.h"
+#include "fileio.h"
#include "inputdev.h"
#include "natkeyboard.h"
+#include "screen.h"
#include "softlist.h"
#include "uiinput.h"
#include "corestr.h"
+#include <algorithm>
+#include <condition_variable>
#include <cstring>
+#include <mutex>
#include <thread>
@@ -36,15 +41,11 @@
// LUA ENGINE
//**************************************************************************
-extern "C" {
-
-int luaopen_zlib(lua_State *L);
-int luaopen_lfs(lua_State *L);
+int luaopen_zlib(lua_State *const L);
+extern "C" int luaopen_lfs(lua_State *L);
int luaopen_linenoise(lua_State *L);
int luaopen_lsqlite3(lua_State *L);
-} // extern "C"
-
template <typename T>
struct lua_engine::devenum
@@ -59,6 +60,128 @@ struct lua_engine::devenum
namespace {
+struct save_item
+{
+ void *base;
+ unsigned int size;
+ unsigned int count;
+ unsigned int valcount;
+ unsigned int blockcount;
+ unsigned int stride;
+};
+
+struct thread_context
+{
+private:
+ sol::state m_state;
+ std::string m_result;
+ std::mutex m_guard;
+ std::condition_variable m_sync;
+ bool m_busy = false;
+
+public:
+ bool m_yield = false;
+
+ thread_context()
+ {
+ m_state.open_libraries();
+ m_state["package"]["preload"]["zlib"] = &luaopen_zlib;
+ m_state["package"]["preload"]["lfs"] = &luaopen_lfs;
+ m_state["package"]["preload"]["linenoise"] = &luaopen_linenoise;
+ m_state.set_function("yield",
+ [this] ()
+ {
+ std::unique_lock<std::mutex> yield_lock(m_guard);
+ m_result = m_state["status"];
+ m_yield = true;
+ m_sync.wait(yield_lock);
+ m_yield = false;
+ });
+ }
+
+ bool start(sol::this_state s, char const *scr)
+ {
+ std::unique_lock<std::mutex> caller_lock(m_guard);
+ if (m_busy)
+ return false;
+
+ sol::load_result res = m_state.load(scr);
+ if (!res.valid())
+ {
+ sol::error err = res;
+ luaL_error(s, err.what());
+ return false; // unreachable - luaL_error throws
+ }
+
+ std::thread th(
+ [this, func = res.get<sol::protected_function>()] ()
+ {
+ auto ret = func();
+ std::unique_lock<std::mutex> result_lock(m_guard);
+ if (ret.valid())
+ {
+ auto result = ret.get<std::optional<char const *> >();
+ if (!result)
+ osd_printf_error("[LUA ERROR] in thread: return value must be string\n");
+ else if (!*result)
+ m_result.clear();
+ else
+ m_result = *result;
+ }
+ else
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in thread: %s\n", err.what());
+ }
+ m_busy = false;
+ });
+ m_busy = true;
+ m_yield = false;
+ th.detach(); // FIXME: this is unsafe as the thread function modifies members of the object
+ return true;
+ }
+
+ void resume(char const *val)
+ {
+ std::unique_lock<std::mutex> lock(m_guard);
+ if (m_yield)
+ {
+ if (val)
+ m_state["status"] = val;
+ else
+ m_state["status"] = sol::lua_nil;
+ m_sync.notify_all();
+ }
+ }
+
+ char const *result()
+ {
+ std::unique_lock<std::mutex> lock(m_guard);
+ if (m_busy && !m_yield)
+ return "";
+ else
+ return m_result.c_str();
+ }
+
+ bool busy() const
+ {
+ return m_busy;
+ }
+};
+
+
+struct device_state_entries
+{
+ device_state_entries(device_state_interface const &s) : state(s) { }
+ device_state_interface::entrylist_type const &items() { return state.state_entries(); }
+
+ static device_state_entry const &unwrap(device_state_interface::entrylist_type::const_iterator const &it) { return **it; }
+ static int push_key(lua_State *L, device_state_interface::entrylist_type::const_iterator const &it, std::size_t ix) { return sol::stack::push_reference(L, (*it)->symbol()); }
+
+ device_state_interface const &state;
+};
+
+
struct image_interface_formats
{
image_interface_formats(device_image_interface &i) : image(i) { }
@@ -82,26 +205,39 @@ struct plugin_options_plugins
plugin_options &options;
};
-} // anonymous namespace
-
-namespace sol
+template <typename T>
+void resume_tasks(lua_State *L, T &&tasks, bool status)
{
+ for (int ref : tasks)
+ {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+ lua_State *const thread = lua_tothread(L, -1);
+ lua_pop(L, 1);
+ lua_pushboolean(thread, status ? 1 : 0);
+ int nresults = 0;
+ int const stat = lua_resume(thread, nullptr, 1, &nresults);
+ if ((stat != LUA_OK) && (stat != LUA_YIELD))
+ {
+ osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(thread, -1));
+ lua_pop(thread, 1);
+ }
+ else
+ {
+ lua_pop(thread, nresults);
+ }
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+ }
+}
-template <> struct is_container<image_interface_formats> : std::true_type { };
-template <> struct is_container<plugin_options_plugins> : std::true_type { };
+} // anonymous namespace
-sol::buffer *sol_lua_get(sol::types<buffer *>, lua_State *L, int index, sol::stack::record &tracking)
-{
- return new sol::buffer(stack::get<int>(L, index), L);
-}
+namespace sol {
-int sol_lua_push(sol::types<buffer *>, lua_State *L, buffer *value)
-{
- delete value;
- return 1;
-}
+template <> struct is_container<device_state_entries> : std::true_type { };
+template <> struct is_container<image_interface_formats> : std::true_type { };
+template <> struct is_container<plugin_options_plugins> : std::true_type { };
template <typename T>
@@ -208,6 +344,34 @@ public:
template <>
+struct usertype_container<device_state_entries> : lua_engine::immutable_sequence_helper<device_state_entries, device_state_interface::entrylist_type const, device_state_interface::entrylist_type::const_iterator>
+{
+private:
+ using entrylist_type = device_state_interface::entrylist_type;
+
+public:
+ static int get(lua_State *L)
+ {
+ device_state_entries &self(get_self(L));
+ char const *const symbol(stack::unqualified_get<char const *>(L));
+ auto const found(std::find_if(
+ self.state.state_entries().begin(),
+ self.state.state_entries().end(),
+ [&symbol] (std::unique_ptr<device_state_entry> const &v) { return !std::strcmp(v->symbol(), symbol); }));
+ if (self.state.state_entries().end() != found)
+ return stack::push_reference(L, std::cref(**found));
+ else
+ return stack::push(L, lua_nil);
+ }
+
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
+};
+
+
+template <>
struct usertype_container<image_interface_formats> : lua_engine::immutable_sequence_helper<image_interface_formats, device_image_interface::formatlist_type const, device_image_interface::formatlist_type::const_iterator>
{
private:
@@ -223,7 +387,7 @@ public:
self.image.formatlist().end(),
[&name] (std::unique_ptr<image_device_format> const &v) { return v->name() == name; }));
if (self.image.formatlist().end() != found)
- return stack::push_reference(L, **found);
+ return stack::push_reference(L, std::cref(**found));
else
return stack::push(L, lua_nil);
}
@@ -251,7 +415,7 @@ public:
self.options.plugins().end(),
[&name] (plugin_options::plugin const &p) { return p.m_name == name; }));
if (self.options.plugins().end() != found)
- return stack::push_reference(L, *found);
+ return stack::push_reference(L, std::cref(*found));
else
return stack::push(L, lua_nil);
}
@@ -287,26 +451,6 @@ int sol_lua_push(sol::types<screen_type_enum>, lua_State *L, screen_type_enum &&
return sol::stack::push(L, "unknown");
}
-int sol_lua_push(sol::types<image_init_result>, lua_State *L, image_init_result &&value)
-{
- switch (value)
- {
- case image_init_result::PASS: return sol::stack::push(L, "pass");
- case image_init_result::FAIL: return sol::stack::push(L, "fail");
- }
- return sol::stack::push(L, "invalid");
-}
-
-int sol_lua_push(sol::types<image_verify_result>, lua_State *L, image_verify_result &&value)
-{
- switch (value)
- {
- case image_verify_result::PASS: return sol::stack::push(L, "pass");
- case image_verify_result::FAIL: return sol::stack::push(L, "fail");
- }
- return sol::stack::push(L, "invalid");
-}
-
//-------------------------------------------------
// process_snapshot_filename - processes a snapshot
@@ -330,13 +474,16 @@ static std::string process_snapshot_filename(running_machine &machine, const cha
//-------------------------------------------------
lua_engine::lua_engine()
+ : m_lua_state(nullptr)
+ , m_machine(nullptr)
+ , m_timer(nullptr)
{
- m_machine = nullptr;
- m_lua_state = luaL_newstate(); /* create state */
+ m_lua_state = luaL_newstate(); // create state
m_sol_state = std::make_unique<sol::state_view>(m_lua_state); // create sol view
+ m_notifiers.emplace();
luaL_checkversion(m_lua_state);
- lua_gc(m_lua_state, LUA_GCSTOP, 0); /* stop collector during initialization */
+ lua_gc(m_lua_state, LUA_GCSTOP, 0); // stop collector during initialization
sol().open_libraries();
// Get package.preload so we can store builtins in it.
@@ -361,16 +508,18 @@ sol::object lua_engine::call_plugin(const std::string &name, sol::object in)
{
std::string field = "cb_" + name;
sol::object obj = sol().registry()[field];
- if(obj.is<sol::protected_function>())
+ if (obj.is<sol::protected_function>())
{
auto res = invoke(obj.as<sol::protected_function>(), in);
- if(!res.valid())
+ if (!res.valid())
{
sol::error err = res;
osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what());
}
else
+ {
return res.get<sol::object>();
+ }
}
return sol::lua_nil;
}
@@ -389,7 +538,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
}
else
{
- std::tuple<sol::table, std::optional<long>, std::string> table = res;
+ std::tuple<sol::table, std::optional<long>, std::optional<std::string> > table = res;
for (auto &entry : std::get<0>(table))
{
if (entry.second.is<sol::table>())
@@ -398,7 +547,10 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3));
}
}
- flags = std::get<2>(table);
+ if (std::get<2>(table))
+ flags = *std::get<2>(table);
+ else
+ flags.clear();
return std::get<1>(table);
}
}
@@ -409,7 +561,7 @@ std::optional<long> lua_engine::menu_populate(const std::string &menu, std::vect
std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
{
std::string field = "menu_cb_" + menu;
- std::pair<bool, std::optional<long> > ret(false, std::nullopt);
+ std::pair<std::optional<bool>, std::optional<long> > ret(false, std::nullopt);
sol::object obj = sol().registry()[field];
if (obj.is<sol::protected_function>())
{
@@ -424,7 +576,7 @@ std::pair<bool, std::optional<long> > lua_engine::menu_callback(const std::strin
ret = res;
}
}
- return ret;
+ return std::make_pair(std::get<0>(ret) && *std::get<0>(ret), std::get<1>(ret));
}
void lua_engine::set_machine(running_machine *machine)
@@ -432,9 +584,10 @@ void lua_engine::set_machine(running_machine *machine)
m_machine = machine;
}
-int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback)
+template <typename T>
+size_t lua_engine::enumerate_functions(const char *id, T &&callback)
{
- int count = 0;
+ size_t count = 0;
sol::object functable = sol().registry()[id];
if (functable.is<sol::table>())
{
@@ -448,23 +601,24 @@ int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol
break;
}
}
- return true;
}
return count;
}
bool lua_engine::execute_function(const char *id)
{
- int count = enumerate_functions(id, [this](const sol::protected_function &func)
- {
- auto ret = invoke(func);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
- }
- return true;
- });
+ size_t count = enumerate_functions(
+ id,
+ [this] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func);
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
+ }
+ return true;
+ });
return count > 0;
}
@@ -482,13 +636,25 @@ void lua_engine::on_machine_prestart()
execute_function("LUA_ON_PRESTART");
}
-void lua_engine::on_machine_start()
+void lua_engine::on_machine_reset()
{
+ m_notifiers->on_reset();
execute_function("LUA_ON_START");
}
void lua_engine::on_machine_stop()
{
+ // clear waiting tasks
+ m_timer = nullptr;
+ std::vector<int> expired;
+ expired.reserve(m_waiting_tasks.size());
+ for (auto const &waiting : m_waiting_tasks)
+ expired.emplace_back(waiting.second);
+ m_waiting_tasks.clear();
+ resume_tasks(m_lua_state, expired, false);
+ expired.clear();
+
+ m_notifiers->on_stop();
execute_function("LUA_ON_STOP");
}
@@ -499,22 +665,45 @@ void lua_engine::on_machine_before_load_settings()
void lua_engine::on_machine_pause()
{
+ m_notifiers->on_pause();
execute_function("LUA_ON_PAUSE");
}
void lua_engine::on_machine_resume()
{
+ m_notifiers->on_resume();
execute_function("LUA_ON_RESUME");
}
void lua_engine::on_machine_frame()
{
+ std::vector<int> tasks = std::move(m_frame_tasks);
+ m_frame_tasks.clear();
+ resume_tasks(m_lua_state, tasks, true); // TODO: doesn't need to return anything
+
+ m_notifiers->on_frame();
+
execute_function("LUA_ON_FRAME");
}
-void lua_engine::on_frame_done()
+void lua_engine::on_machine_presave()
+{
+ m_notifiers->on_presave();
+}
+
+void lua_engine::on_machine_postload()
{
- execute_function("LUA_ON_FRAME_DONE");
+ // clear waiting tasks
+ m_timer->reset();
+ std::vector<int> expired;
+ expired.reserve(m_waiting_tasks.size());
+ for (auto const &waiting : m_waiting_tasks)
+ expired.emplace_back(waiting.second);
+ m_waiting_tasks.clear();
+ resume_tasks(m_lua_state, expired, false);
+ expired.clear();
+
+ m_notifiers->on_postload();
}
void lua_engine::on_sound_update()
@@ -530,32 +719,38 @@ void lua_engine::on_periodic()
bool lua_engine::on_missing_mandatory_image(const std::string &instance_name)
{
bool handled = false;
- enumerate_functions("LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE", [this, &instance_name, &handled](const sol::protected_function &func)
- {
- auto ret = invoke(func, instance_name);
+ enumerate_functions(
+ "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE",
+ [this, &instance_name, &handled] (const sol::protected_function &func)
+ {
+ auto ret = invoke(func, instance_name);
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
- }
- else if (ret.get<bool>())
- {
- handled = true;
- }
- return !handled;
- });
+ if (!ret.valid())
+ {
+ sol::error err = ret;
+ osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
+ }
+ else if (ret.get<bool>())
+ {
+ handled = true;
+ }
+ return !handled;
+ });
return handled;
}
void lua_engine::attach_notifiers()
{
machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_prestart, this), true);
- machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_start, this));
+ machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_reset, this));
machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&lua_engine::on_machine_stop, this));
machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&lua_engine::on_machine_pause, this));
machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&lua_engine::on_machine_resume, this));
machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&lua_engine::on_machine_frame, this));
+ machine().save().register_presave(save_prepost_delegate(FUNC(lua_engine::on_machine_presave), this));
+ machine().save().register_postload(save_prepost_delegate(FUNC(lua_engine::on_machine_postload), this));
+
+ m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(lua_engine::resume), this));
}
//-------------------------------------------------
@@ -596,9 +791,6 @@ void lua_engine::initialize()
* emu.unpause() - unpause emulation
* emu.step() - advance one frame
* emu.keypost(keys) - post keys to natural keyboard
- * emu.wait(len) - wait for len within coroutine
- * emu.lang_translate(str) - get translation for str if available
- * emu.subst_env(str) - substitute environment variables with values for str (semantics are OS-specific)
*
* emu.register_prestart(callback) - register callback before reset
* emu.register_start(callback) - register callback after reset
@@ -615,11 +807,6 @@ void lua_engine::initialize()
* emu.register_before_load_settings(callback) - register callback to be run before settings are loaded
* emu.show_menu(menu_name) - show menu by name and pause the machine
*
- * emu.print_verbose(str) - output to stderr at verbose level
- * emu.print_error(str) - output to stderr at error level
- * emu.print_info(str) - output to stderr at info level
- * emu.print_debug(str) - output to stderr at debug level
- *
* emu.device_enumerator(dev) - get device enumerator starting at arbitrary point in tree
* emu.screen_enumerator(dev) - get screen device enumerator starting at arbitrary point in tree
* emu.image_enumerator(dev) - get image interface enumerator starting at arbitrary point in tree
@@ -627,6 +814,79 @@ void lua_engine::initialize()
*/
sol::table emu = sol().create_named_table("emu");
+ emu["wait"] = sol::yielding(
+ [this] (sol::this_state s, sol::object duration, sol::variadic_args args)
+ {
+ attotime delay;
+ if (!duration)
+ {
+ luaL_error(s, "waiting duration expected");
+ }
+ else if (duration.is<attotime>())
+ {
+ delay = duration.as<attotime>();
+ }
+ else
+ {
+ auto seconds = duration.as<std::optional<double> >();
+ if (!seconds)
+ luaL_error(s, "waiting duration must be attotime or number");
+ delay = attotime::from_double(*seconds);
+ }
+ attotime const expiry = machine().time() + delay;
+
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ int const ref = luaL_ref(s, LUA_REGISTRYINDEX);
+
+ auto const pos = std::upper_bound(
+ m_waiting_tasks.begin(),
+ m_waiting_tasks.end(),
+ expiry,
+ [] (auto const &a, auto const &b) { return a < b.first; });
+ if (m_waiting_tasks.begin() == pos)
+ m_timer->reset(delay);
+ m_waiting_tasks.emplace(pos, expiry, ref);
+
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu["wait_next_update"] = sol::yielding(
+ [this] (sol::this_state s, sol::variadic_args args)
+ {
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ m_update_tasks.emplace_back(luaL_ref(s, LUA_REGISTRYINDEX));
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu["wait_next_frame"] = sol::yielding(
+ [this] (sol::this_state s, sol::variadic_args args)
+ {
+ int const ret = lua_pushthread(s);
+ if (ret == 1)
+ luaL_error(s, "cannot wait from outside coroutine");
+ m_frame_tasks.emplace_back(luaL_ref(s, LUA_REGISTRYINDEX));
+ return sol::variadic_results(args.begin(), args.end());
+ });
+ emu.set_function("add_machine_reset_notifier", make_notifier_adder(m_notifiers->on_reset, "machine reset"));
+ emu.set_function("add_machine_stop_notifier", make_notifier_adder(m_notifiers->on_stop, "machine stop"));
+ emu.set_function("add_machine_pause_notifier", make_notifier_adder(m_notifiers->on_pause, "machine pause"));
+ emu.set_function("add_machine_resume_notifier", make_notifier_adder(m_notifiers->on_resume, "machine resume"));
+ emu.set_function("add_machine_frame_notifier", make_notifier_adder(m_notifiers->on_frame, "machine frame"));
+ emu.set_function("add_machine_pre_save_notifier", make_notifier_adder(m_notifiers->on_presave, "machine pre-save"));
+ emu.set_function("add_machine_post_load_notifier", make_notifier_adder(m_notifiers->on_postload, "machine post-load"));
+ emu.set_function("print_error", [] (const char *str) { osd_printf_error("%s\n", str); });
+ emu.set_function("print_warning", [] (const char *str) { osd_printf_warning("%s\n", str); });
+ emu.set_function("print_info", [] (const char *str) { osd_printf_info("%s\n", str); });
+ emu.set_function("print_verbose", [] (const char *str) { osd_printf_verbose("%s\n", str); });
+ emu.set_function("print_debug", [] (const char *str) { osd_printf_debug("%s\n", str); });
+ emu["lang_translate"] = sol::overload(
+ static_cast<char const *(*)(char const *)>(&lang_translate),
+ static_cast<char const *(*)(char const *, char const *)>(&lang_translate));
+ emu.set_function("subst_env", &osd_subst_env);
+
+ // TODO: stuff below here needs to be rationalised
emu["app_name"] = &emulator_info::get_appname_lower;
emu["app_version"] = &emulator_info::get_bare_build_version;
emu["gamename"] = [this] () { return machine().system().type.fullname(); };
@@ -654,11 +914,11 @@ void lua_engine::initialize()
machine().resume();
};
emu["register_prestart"] = [this] (sol::function func) { register_function(func, "LUA_ON_PRESTART"); };
- emu["register_start"] = [this] (sol::function func) { register_function(func, "LUA_ON_START"); };
- emu["register_stop"] = [this] (sol::function func) { register_function(func, "LUA_ON_STOP"); };
- emu["register_pause"] = [this] (sol::function func) { register_function(func, "LUA_ON_PAUSE"); };
- emu["register_resume"] = [this] (sol::function func) { register_function(func, "LUA_ON_RESUME"); };
- emu["register_frame"] = [this] (sol::function func) { register_function(func, "LUA_ON_FRAME"); };
+ emu["register_start"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_start is deprecated - please use emu.add_machine_reset_notifier\n"); register_function(func, "LUA_ON_START"); };
+ emu["register_stop"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_stop is deprecated - please use emu.add_machine_stop_notifier\n"); register_function(func, "LUA_ON_STOP"); };
+ emu["register_pause"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_pause is deprecated - please use emu.add_machine_pause_notifier\n"); register_function(func, "LUA_ON_PAUSE"); };
+ emu["register_resume"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_resume is deprecated - please use emu.add_machine_resume_notifier\n"); register_function(func, "LUA_ON_RESUME"); };
+ emu["register_frame"] = [this] (sol::function func) { osd_printf_warning("[LUA] emu.register_frame is deprecated - please use emu.add_machine_frame_notifier\n"); register_function(func, "LUA_ON_FRAME"); };
emu["register_frame_done"] = [this] (sol::function func) { register_function(func, "LUA_ON_FRAME_DONE"); };
emu["register_sound_update"] = [this] (sol::function func) { register_function(func, "LUA_ON_SOUND_UPDATE"); };
emu["register_periodic"] = [this] (sol::function func) { register_function(func, "LUA_ON_PERIODIC"); };
@@ -678,7 +938,7 @@ void lua_engine::initialize()
{
mame_ui_manager &mui = mame_machine_manager::instance()->ui();
render_container &container = machine().render().ui_container();
- ui::menu_plugin::show_menu(mui, container, (char *)name);
+ ui::menu_plugin::show_menu(mui, container, name);
};
emu["register_callback"] =
[this] (sol::function cb, const std::string &name)
@@ -686,10 +946,6 @@ void lua_engine::initialize()
std::string field = "cb_" + name;
sol().registry()[field] = cb;
};
- emu["print_verbose"] = [] (const char *str) { osd_printf_verbose("%s\n", str); };
- emu["print_error"] = [] (const char *str) { osd_printf_error("%s\n", str); };
- emu["print_info"] = [] (const char *str) { osd_printf_info("%s\n", str); };
- emu["print_debug"] = [] (const char *str) { osd_printf_debug("%s\n", str); };
emu["osd_ticks"] = &osd_ticks;
emu["osd_ticks_per_second"] = &osd_ticks_per_second;
emu["driver_find"] =
@@ -700,32 +956,13 @@ void lua_engine::initialize()
return sol::lua_nil;
return sol::make_object(s, driver_list::driver(i));
};
- emu["wait"] = lua_CFunction(
- [] (lua_State *L)
- {
- lua_engine *engine = mame_machine_manager::instance()->lua();
- luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
- int ret = lua_pushthread(L);
- if (ret == 1)
- return luaL_error(L, "cannot wait from outside coroutine");
- int ref = luaL_ref(L, LUA_REGISTRYINDEX);
- engine->machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), engine), ref, nullptr);
- return lua_yield(L, 0);
- });
- emu["lang_translate"] = sol::overload(
- static_cast<char const *(*)(char const *)>(&lang_translate),
- static_cast<char const *(*)(char const *, char const *)>(&lang_translate));
emu["pid"] = &osd_getpid;
- emu["subst_env"] =
- [] (const std::string &str)
- {
- std::string result;
- osd_subst_env(result, str);
- return result;
- };
emu["device_enumerator"] = sol::overload(
[] (device_t &dev) { return devenum<device_enumerator>(dev); },
[] (device_t &dev, int maxdepth) { return devenum<device_enumerator>(dev, maxdepth); });
+ emu["palette_enumerator"] = sol::overload(
+ [] (device_t &dev) { return devenum<palette_interface_enumerator>(dev); },
+ [] (device_t &dev, int maxdepth) { return devenum<palette_interface_enumerator>(dev, maxdepth); });
emu["screen_enumerator"] = sol::overload(
[] (device_t &dev) { return devenum<screen_device_enumerator>(dev); },
[] (device_t &dev, int maxdepth) { return devenum<screen_device_enumerator>(dev, maxdepth); });
@@ -740,6 +977,10 @@ void lua_engine::initialize()
[] (device_t &dev, int maxdepth) { return devenum<slot_interface_enumerator>(dev, maxdepth); });
+ auto notifier_subscription_type = sol().registry().new_usertype<util::notifier_subscription>("notifier_subscription", sol::no_constructor);
+ notifier_subscription_type["unsubscribe"] = &util::notifier_subscription::reset;
+ notifier_subscription_type["is_active"] = sol::property(&util::notifier_subscription::operator bool);
+
auto attotime_type = emu.new_usertype<attotime>(
"attotime",
sol::call_constructor, sol::constructors<attotime(), attotime(seconds_t, attoseconds_t), attotime(attotime const &)>());
@@ -827,7 +1068,15 @@ void lua_engine::initialize()
}
new (&file) emu_file(path, flags);
}));
- file_type.set("read", [](emu_file &file, sol::buffer *buff) { buff->set_len(file.read(buff->get_ptr(), buff->get_len())); return buff; });
+ file_type.set("read",
+ [] (emu_file &file, sol::this_state s, size_t len)
+ {
+ buffer_helper buf(s);
+ auto space = buf.prepare(len);
+ space.add(file.read(space.get(), len));
+ buf.push();
+ return sol::make_reference(s, sol::stack_reference(s, -1));
+ });
file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); });
file_type.set("puts", &emu_file::puts);
file_type.set("open", static_cast<std::error_condition (emu_file::*)(std::string_view)>(&emu_file::open));
@@ -871,75 +1120,17 @@ void lua_engine::initialize()
* thread runs until yield() and/or terminates on return.
* thread:continue(val) - resume thread that has yielded and pass val to it
*
- * thread.result - get result of a terminated thread as string
- * thread.busy - check if thread is running
- * thread.yield - check if thread is yielded
+ * thread.result - get result of a terminated or yielding thread as string
+ * thread.busy - check if thread is running or yielding
+ * thread.yield - check if thread is yielding
*/
- auto thread_type = emu.new_usertype<context>("thread", sol::call_constructor, sol::constructors<sol::types<>>());
- thread_type.set("start", [](context &ctx, const char *scr) {
- std::string script(scr);
- if (ctx.busy)
- return false;
- std::thread th([&ctx, script]() {
- sol::state thstate;
- thstate.open_libraries();
- thstate["package"]["preload"]["zlib"] = &luaopen_zlib;
- thstate["package"]["preload"]["lfs"] = &luaopen_lfs;
- thstate["package"]["preload"]["linenoise"] = &luaopen_linenoise;
- sol::load_result res = thstate.load(script);
- if(res.valid())
- {
- sol::protected_function func = res.get<sol::protected_function>();
- thstate["yield"] = [&ctx, &thstate]() {
- std::mutex m;
- std::unique_lock<std::mutex> lock(m);
- ctx.result = thstate["status"];
- ctx.yield = true;
- ctx.sync.wait(lock);
- ctx.yield = false;
- thstate["status"] = ctx.result;
- };
- auto ret = func();
- if (ret.valid())
- {
- const char *tmp = ret.get<const char *>();
- if (tmp != nullptr)
- ctx.result = tmp;
- else
- osd_printf_error("[LUA ERROR] in thread: return value must be string\n");
- }
- else
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in thread: %s\n", err.what());
- }
- }
- else
- {
- sol::error err = res;
- osd_printf_error("[LUA ERROR] when loading script for thread: %s\n", err.what());
- }
- ctx.busy = false;
- });
- ctx.busy = true;
- ctx.yield = false;
- th.detach();
- return true;
- });
- thread_type.set("continue", [](context &ctx, const char *val) {
- if (!ctx.yield)
- return;
- ctx.result = val;
- ctx.sync.notify_all();
- });
- thread_type.set("result", sol::property([](context &ctx) -> std::string {
- if (ctx.busy && !ctx.yield)
- return "";
- return ctx.result;
- }));
- thread_type.set("busy", sol::readonly(&context::busy));
- thread_type.set("yield", sol::readonly(&context::yield));
+ auto thread_type = emu.new_usertype<thread_context>("thread", sol::call_constructor, sol::constructors<sol::types<>>());
+ thread_type.set_function("start", &thread_context::start);
+ thread_type.set_function("continue", &thread_context::resume);
+ thread_type["result"] = sol::property(&thread_context::result);
+ thread_type["busy"] = sol::property(&thread_context::busy);
+ thread_type["yield"] = sol::readonly(&thread_context::m_yield);
/* save_item library
@@ -971,12 +1162,14 @@ void lua_engine::initialize()
}));
item_type.set("size", sol::readonly(&save_item::size));
item_type.set("count", sol::readonly(&save_item::count));
- item_type.set("read", [this](save_item &item, int offset) -> sol::object {
- if(!item.base || (offset >= item.count))
+ item_type.set("read",
+ [this] (save_item &item, int offset) -> sol::object
+ {
+ if (!item.base || (offset >= item.count))
return sol::lua_nil;
const void *const data = reinterpret_cast<const uint8_t *>(item.base) + (item.stride * (offset / item.valcount));
uint64_t ret = 0;
- switch(item.size)
+ switch (item.size)
{
case 1:
default:
@@ -994,29 +1187,38 @@ void lua_engine::initialize()
}
return sol::make_object(sol(), ret);
});
- item_type.set("read_block", [](save_item &item, int offset, sol::buffer *buff) {
- if(!item.base || ((offset + buff->get_len()) > (item.size * item.count)))
+ item_type.set("read_block",
+ [] (save_item &item, sol::this_state s, uint32_t offset, size_t len) -> sol::object
+ {
+ if (!item.base)
{
- buff->set_len(0);
+ luaL_error(s, "Invalid save item");
+ return sol::lua_nil;
}
- else
+
+ if ((offset + len) > (item.size * item.count))
{
- const uint32_t blocksize = item.size * item.valcount;
- uint32_t remaining = buff->get_len();
- uint8_t *dest = reinterpret_cast<uint8_t *>(buff->get_ptr());
- while(remaining)
- {
- const uint32_t blockno = offset / blocksize;
- const uint32_t available = blocksize - (offset % blocksize);
- const uint32_t chunk = (available < remaining) ? available : remaining;
- const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
- std::memcpy(dest, source, chunk);
- offset += chunk;
- remaining -= chunk;
- dest += chunk;
- }
+ luaL_error(s, "Range extends beyond end of save item");
+ return sol::lua_nil;
}
- return buff;
+
+ luaL_Buffer buff;
+ uint8_t *dest = reinterpret_cast<uint8_t *>(luaL_buffinitsize(s, &buff, len));
+ const uint32_t blocksize = item.size * item.valcount;
+ size_t remaining = len;
+ while (remaining)
+ {
+ const uint32_t blockno = offset / blocksize;
+ const uint32_t available = blocksize - (offset % blocksize);
+ const uint32_t chunk = (available < remaining) ? available : remaining;
+ const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
+ std::memcpy(dest, source, chunk);
+ offset += chunk;
+ remaining -= chunk;
+ dest += chunk;
+ }
+ luaL_pushresultsize(&buff, len);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
});
item_type.set("write", [](save_item &item, int offset, uint64_t value) {
if(!item.base || (offset >= item.count))
@@ -1134,25 +1336,25 @@ void lua_engine::initialize()
auto core_options_entry_type = sol().registry().new_usertype<core_options::entry>("core_options_entry", "new", sol::no_constructor);
core_options_entry_type.set("value", sol::overload(
[this](core_options::entry &e, bool val) {
- if(e.type() != OPTION_BOOLEAN)
+ if(e.type() != core_options::option_type::BOOLEAN)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(val ? "1" : "0", OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, float val) {
- if(e.type() != OPTION_FLOAT)
+ if(e.type() != core_options::option_type::FLOAT)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(string_format("%f", val), OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, int val) {
- if(e.type() != OPTION_INTEGER)
+ if(e.type() != core_options::option_type::INTEGER)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(string_format("%d", val), OPTION_PRIORITY_CMDLINE);
},
[this](core_options::entry &e, const char *val) {
- if(e.type() != OPTION_STRING)
+ if(e.type() != core_options::option_type::STRING && e.type() != core_options::option_type::PATH && e.type() != core_options::option_type::MULTIPATH)
luaL_error(m_lua_state, "Cannot set option to wrong type");
else
e.set_value(val, OPTION_PRIORITY_CMDLINE);
@@ -1180,54 +1382,54 @@ void lua_engine::initialize()
auto machine_type = sol().registry().new_usertype<running_machine>("machine", sol::no_constructor);
- machine_type["exit"] = &running_machine::schedule_exit;
- machine_type["hard_reset"] = &running_machine::schedule_hard_reset;
- machine_type["soft_reset"] = &running_machine::schedule_soft_reset;
- machine_type["save"] = &running_machine::schedule_save; // TODO: some kind of completion notification?
- machine_type["load"] = &running_machine::schedule_load; // TODO: some kind of completion notification?
- machine_type["buffer_save"] =
- [] (running_machine &m, sol::this_state s)
- {
- // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
- // right now it's broken by anonymous timers, synchronize, etc.
- lua_State *L = s;
- luaL_Buffer buff;
- int size = ram_state::get_size(m.save());
- u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
- save_error error = m.save().write_buffer(ptr, size);
- if (error == STATERR_NONE)
+ machine_type.set_function("exit", &running_machine::schedule_exit);
+ machine_type.set_function("hard_reset", &running_machine::schedule_hard_reset);
+ machine_type.set_function("soft_reset", &running_machine::schedule_soft_reset);
+ machine_type.set_function("save", &running_machine::schedule_save); // TODO: some kind of completion notification?
+ machine_type.set_function("load", &running_machine::schedule_load); // TODO: some kind of completion notification?
+ machine_type.set_function("buffer_save",
+ [] (running_machine &m, sol::this_state s)
{
- luaL_pushresultsize(&buff, size);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- }
- luaL_error(L, "State save error.");
- return sol::make_reference(L, nullptr);
- };
- machine_type["buffer_load"] =
- [] (running_machine &m, sol::this_state s, std::string str)
- {
- // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
- // right now it's broken by anonymous timers, synchronize, etc.
- save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
- if (error == STATERR_NONE)
+ // FIXME: this needs to schedule saving to a buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ lua_State *L = s;
+ luaL_Buffer buff;
+ int size = ram_state::get_size(m.save());
+ u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
+ save_error error = m.save().write_buffer(ptr, size);
+ if (error == STATERR_NONE)
+ {
+ luaL_pushresultsize(&buff, size);
+ return sol::make_reference(L, sol::stack_reference(L, -1));
+ }
+ luaL_error(L, "State save error.");
+ return sol::make_reference(L, nullptr);
+ });
+ machine_type.set_function("buffer_load",
+ [] (running_machine &m, sol::this_state s, std::string str)
{
- return true;
- }
- else
+ // FIXME: this needs to schedule loading from the buffer and return asynchronously somehow
+ // right now it's broken by anonymous timers, synchronize, etc.
+ save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
+ if (error == STATERR_NONE)
+ {
+ return true;
+ }
+ else
+ {
+ luaL_error(s,"State load error.");
+ return false;
+ }
+ });
+ machine_type.set_function("popmessage",
+ [] (running_machine &m, std::optional<const char *> str)
{
- luaL_error(s,"State load error.");
- return false;
- }
- };
- machine_type["popmessage"] =
- [] (running_machine &m, const char *str)
- {
- if (str)
- m.popmessage("%s", str);
- else
- m.popmessage();
- };
- machine_type["logerror"] = [] (running_machine &m, std::string const *str) { m.logerror("[luaengine] %s\n", str); };
+ if (str)
+ m.popmessage("%s", *str);
+ else
+ m.popmessage();
+ });
+ machine_type.set_function("logerror", [] (running_machine &m, char const *str) { m.logerror("[luaengine] %s\n", str); });
machine_type["time"] = sol::property(&running_machine::time);
machine_type["system"] = sol::property(&running_machine::system);
machine_type["parameters"] = sol::property(&running_machine::parameters);
@@ -1254,6 +1456,7 @@ void lua_engine::initialize()
machine_type["exit_pending"] = sol::property(&running_machine::exit_pending);
machine_type["hard_reset_pending"] = sol::property(&running_machine::hard_reset_pending);
machine_type["devices"] = sol::property([] (running_machine &m) { return devenum<device_enumerator>(m.root_device()); });
+ machine_type["palettes"] = sol::property([] (running_machine &m) { return devenum<palette_interface_enumerator>(m.root_device()); });
machine_type["screens"] = sol::property([] (running_machine &m) { return devenum<screen_device_enumerator>(m.root_device()); });
machine_type["cassettes"] = sol::property([] (running_machine &m) { return devenum<cassette_device_enumerator>(m.root_device()); });
machine_type["images"] = sol::property([] (running_machine &m) { return devenum<image_interface_enumerator>(m.root_device()); });
@@ -1267,7 +1470,7 @@ void lua_engine::initialize()
game_driver_type["manufacturer"] = sol::readonly(&game_driver::manufacturer);
game_driver_type["parent"] = sol::readonly(&game_driver::parent);
game_driver_type["compatible_with"] = sol::property([] (game_driver const &driver) { return strcmp(driver.compatible_with, "0") ? driver.compatible_with : nullptr; });
- game_driver_type["source_file"] = sol::property([] (game_driver const &driver) { return &driver.type.source()[0]; });
+ game_driver_type["source_file"] = sol::property([] (game_driver const &driver) { return driver.type.source(); });
game_driver_type["orientation"] = sol::property(
[] (game_driver const &driver)
{
@@ -1294,35 +1497,11 @@ void lua_engine::initialize()
}
return rot;
});
- game_driver_type["type"] = sol::property(
- [] (game_driver const &driver)
- {
- // FIXME: this shouldn't be called type - there's potendial for confusion with the device type
- // also, this should eventually go away in favour of richer flags
- std::string type;
- switch (driver.flags & machine_flags::MASK_TYPE)
- {
- case machine_flags::TYPE_ARCADE:
- type = "arcade";
- break;
- case machine_flags::TYPE_CONSOLE:
- type = "console";
- break;
- case machine_flags::TYPE_COMPUTER:
- type = "computer";
- break;
- default:
- type = "other";
- break;
- }
- return type;
- });
game_driver_type["not_working"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) != 0; });
game_driver_type["supports_save"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) != 0; });
game_driver_type["no_cocktail"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) != 0; });
game_driver_type["is_bios_root"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) != 0; });
game_driver_type["requires_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) != 0; });
- game_driver_type["clickable_artwork"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) != 0; });
game_driver_type["unofficial"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) != 0; });
game_driver_type["no_sound_hw"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) != 0; });
game_driver_type["mechanical"] = sol::property([] (game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) != 0; });
@@ -1330,15 +1509,16 @@ void lua_engine::initialize()
auto device_type = sol().registry().new_usertype<device_t>("device", sol::no_constructor);
- device_type["subtag"] = &device_t::subtag;
- device_type["siblingtag"] = &device_t::siblingtag;
- device_type["memregion"] = &device_t::memregion;
- device_type["memshare"] = &device_t::memshare;
- device_type["membank"] = &device_t::membank;
- device_type["ioport"] = &device_t::ioport;
- device_type["subdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice);
- device_type["siblingdevice"] = static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice);
- device_type["parameter"] = &device_t::parameter;
+ device_type.set_function(sol::meta_function::to_string, [] (device_t &d) { return util::string_format("%s(%s)", d.shortname(), d.tag()); });
+ device_type.set_function("subtag", &device_t::subtag);
+ device_type.set_function("siblingtag", &device_t::siblingtag);
+ device_type.set_function("memregion", &device_t::memregion);
+ device_type.set_function("memshare", &device_t::memshare);
+ device_type.set_function("membank", &device_t::membank);
+ device_type.set_function("ioport", &device_t::ioport);
+ device_type.set_function("subdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::subdevice));
+ device_type.set_function("siblingdevice", static_cast<device_t *(device_t::*)(std::string_view) const>(&device_t::siblingdevice));
+ device_type.set_function("parameter", &device_t::parameter);
device_type["tag"] = sol::property(&device_t::tag);
device_type["basetag"] = sol::property(&device_t::basetag);
device_type["name"] = sol::property(&device_t::name);
@@ -1367,18 +1547,13 @@ void lua_engine::initialize()
}
return sp_table;
});
- // FIXME: improve this
device_type["state"] = sol::property(
- [this] (device_t &dev)
+ [] (device_t &dev, sol::this_state s) -> sol::object
{
- sol::table st_table = sol().create_table();
- const device_state_interface *state;
- if(!dev.interface(state))
- return st_table;
- // XXX: refrain from exporting non-visible entries?
- for(auto &s : state->state_entries())
- st_table[s->symbol()] = s.get();
- return st_table;
+ device_state_interface const *state;
+ if (!dev.interface(state))
+ return sol::lua_nil;
+ return sol::make_object(s, device_state_entries(*state));
});
// FIXME: turn into a wrapper - it's stupid slow to walk on every property access
// also, this mixes up things like RAM areas with stuff saved by the device itself, so there's potential for key conflicts
@@ -1414,11 +1589,67 @@ void lua_engine::initialize()
});
+ auto dipalette_type = sol().registry().new_usertype<device_palette_interface>("dipalette", sol::no_constructor);
+ dipalette_type.set_function("pen", &device_palette_interface::pen);
+ dipalette_type.set_function(
+ "pen_color",
+ [] (device_palette_interface const &pal, pen_t pen)
+ {
+ return uint32_t(pal.pen_color(pen));
+ });
+ dipalette_type.set_function("pen_contrast", &device_palette_interface::pen_contrast);
+ dipalette_type.set_function("pen_indirect", &device_palette_interface::pen_indirect);
+ dipalette_type.set_function(
+ "indirect_color",
+ [] (device_palette_interface const &pal, int index)
+ {
+ return uint32_t(pal.indirect_color(index));
+ });
+ dipalette_type["set_pen_color"] = sol::overload(
+ [] (device_palette_interface &pal, pen_t pen, uint32_t color)
+ {
+ pal.set_pen_color(pen, rgb_t(color));
+ },
+ static_cast<void (device_palette_interface::*)(pen_t, uint8_t, uint8_t, uint8_t)>(&device_palette_interface::set_pen_color));
+ dipalette_type.set_function("set_pen_red_level", &device_palette_interface::set_pen_red_level);
+ dipalette_type.set_function("set_pen_green_level", &device_palette_interface::set_pen_green_level);
+ dipalette_type.set_function("set_pen_blue_level", &device_palette_interface::set_pen_blue_level);
+ dipalette_type.set_function("set_pen_contrast", &device_palette_interface::set_pen_contrast);
+ dipalette_type.set_function("set_pen_indirect", &device_palette_interface::set_pen_indirect);
+ dipalette_type["set_indirect_color"] = sol::overload(
+ [] (device_palette_interface &pal, int index, uint32_t color)
+ {
+ pal.set_indirect_color(index, rgb_t(color));
+ },
+ [] (device_palette_interface &pal, int index, uint8_t r, uint8_t g, uint8_t b)
+ {
+ pal.set_indirect_color(index, rgb_t(r, g, b));
+ });
+ dipalette_type.set_function("set_shadow_factor", &device_palette_interface::set_shadow_factor);
+ dipalette_type.set_function("set_highlight_factor", &device_palette_interface::set_highlight_factor);
+ dipalette_type.set_function("set_shadow_mode", &device_palette_interface::set_shadow_mode);
+ dipalette_type["palette"] = sol::property(
+ [] (device_palette_interface &pal)
+ {
+ return pal.palette()
+ ? std::optional<palette_wrapper>(std::in_place, *pal.palette())
+ : std::optional<palette_wrapper>();
+ });
+ dipalette_type["entries"] = sol::property(&device_palette_interface::entries);
+ dipalette_type["indirect_entries"] = sol::property(&device_palette_interface::indirect_entries);
+ dipalette_type["black_pen"] = sol::property(&device_palette_interface::black_pen);
+ dipalette_type["white_pen"] = sol::property(&device_palette_interface::white_pen);
+ dipalette_type["shadows_enabled"] = sol::property(&device_palette_interface::shadows_enabled);
+ dipalette_type["highlights_enabled"] = sol::property(&device_palette_interface::hilights_enabled);
+ dipalette_type["device"] = sol::property(static_cast<device_t & (device_palette_interface::*)()>(&device_palette_interface::device));
+
+
auto screen_dev_type = sol().registry().new_usertype<screen_device>(
"screen_dev",
sol::no_constructor,
sol::base_classes, sol::bases<device_t>());
- screen_dev_type["draw_box"] =
+ screen_dev_type.set_function(
+ "draw_box",
[] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
float const sc_width(sdev.visible_area().width());
@@ -1433,8 +1664,9 @@ void lua_engine::initialize()
if (!bgcolor)
bgcolor = ui.colors().background_color();
ui.draw_outlined_box(sdev.container(), x1, y1, x2, y2, *fgcolor, *bgcolor);
- };
- screen_dev_type["draw_line"] =
+ });
+ screen_dev_type.set_function(
+ "draw_line",
[] (screen_device &sdev, float x1, float y1, float x2, float y2, std::optional<uint32_t> color)
{
float const sc_width(sdev.visible_area().width());
@@ -1446,8 +1678,9 @@ void lua_engine::initialize()
if (!color)
color = mame_machine_manager::instance()->ui().colors().text_color();
sdev.container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(*color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- };
- screen_dev_type["draw_text"] =
+ });
+ screen_dev_type.set_function(
+ "draw_text",
[this] (screen_device &sdev, sol::object xobj, float y, char const *msg, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
float const sc_width(sdev.visible_area().width());
@@ -1485,85 +1718,87 @@ void lua_engine::initialize()
x, y, (1.0f - x),
justify, ui::text_layout::word_wrapping::WORD,
mame_ui_manager::OPAQUE_, *fgcolor, *bgcolor);
- };
- screen_dev_type["orientation"] =
- [] (screen_device &sdev)
- {
- uint32_t flags = sdev.orientation();
- int rotation_angle = 0;
- switch (flags)
+ });
+ screen_dev_type.set_function(
+ "orientation",
+ [] (screen_device &sdev)
{
- case ORIENTATION_SWAP_XY:
- case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X:
- rotation_angle = 90;
- flags ^= ORIENTATION_FLIP_X;
- break;
- case ORIENTATION_FLIP_Y:
- case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
- rotation_angle = 180;
- flags ^= ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y;
- break;
- case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y:
- case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
- rotation_angle = 270;
- flags ^= ORIENTATION_FLIP_Y;
- break;
- }
- return std::tuple<int, bool, bool>(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y);
- };
+ uint32_t flags = sdev.orientation();
+ int rotation_angle = 0;
+ switch (flags)
+ {
+ case ORIENTATION_SWAP_XY:
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X:
+ rotation_angle = 90;
+ flags ^= ORIENTATION_FLIP_X;
+ break;
+ case ORIENTATION_FLIP_Y:
+ case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ rotation_angle = 180;
+ flags ^= ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y;
+ break;
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y:
+ case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
+ rotation_angle = 270;
+ flags ^= ORIENTATION_FLIP_Y;
+ break;
+ }
+ return std::tuple<int, bool, bool>(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y);
+ });
screen_dev_type["time_until_pos"] = sol::overload(
[] (screen_device &sdev, int vpos) { return sdev.time_until_pos(vpos).as_double(); },
[] (screen_device &sdev, int vpos, int hpos) { return sdev.time_until_pos(vpos, hpos).as_double(); });
- screen_dev_type["time_until_vblank_start"] = &screen_device::time_until_vblank_start;
- screen_dev_type["time_until_vblank_end"] = &screen_device::time_until_vblank_end;
- screen_dev_type["snapshot"] =
- [this] (screen_device &sdev, char const *filename) -> sol::object
- {
- // FIXME: this shouldn't be a member of the screen device
- // the screen is only used as a hint when configured for native snapshots and may be ignored
- std::string snapstr;
- bool is_absolute_path = false;
- if (filename)
+ screen_dev_type.set_function("time_until_vblank_start", &screen_device::time_until_vblank_start);
+ screen_dev_type.set_function("time_until_vblank_end", &screen_device::time_until_vblank_end);
+ screen_dev_type.set_function(
+ "snapshot",
+ [this] (screen_device &sdev, char const *filename) -> sol::object
{
- // a filename was specified; if it isn't absolute post-process it
- snapstr = process_snapshot_filename(machine(), filename);
- is_absolute_path = osd_is_absolute_path(snapstr);
- }
+ // FIXME: this shouldn't be a member of the screen device
+ // the screen is only used as a hint when configured for native snapshots and may be ignored
+ std::string snapstr;
+ bool is_absolute_path = false;
+ if (filename)
+ {
+ // a filename was specified; if it isn't absolute post-process it
+ snapstr = process_snapshot_filename(machine(), filename);
+ is_absolute_path = osd_is_absolute_path(snapstr);
+ }
- // open the file
- emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
- std::error_condition filerr;
- if (!snapstr.empty())
- filerr = file.open(snapstr);
- else
- filerr = machine().video().open_next(file, "png");
- if (filerr)
- return sol::make_object(sol(), filerr);
+ // open the file
+ emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ std::error_condition filerr;
+ if (!snapstr.empty())
+ filerr = file.open(snapstr);
+ else
+ filerr = machine().video().open_next(file, "png");
+ if (filerr)
+ return sol::make_object(sol(), filerr);
- // and save the snapshot
- machine().video().save_snapshot(&sdev, file);
- return sol::lua_nil;
- };
- screen_dev_type["pixel"] = [] (screen_device &sdev, s32 x, s32 y) { return sdev.pixel(x, y); };
- screen_dev_type["pixels"] =
- [] (screen_device &sdev, sol::this_state s)
- {
- // TODO: would be better if this could return a tuple of (buffer, width, height)
- const rectangle &visarea = sdev.visible_area();
- luaL_Buffer buff;
- int size = visarea.height() * visarea.width() * 4;
- u32 *ptr = (u32 *)luaL_buffinitsize(s, &buff, size);
- sdev.pixels(ptr);
- luaL_pushresultsize(&buff, size);
- return sol::make_reference(s, sol::stack_reference(s, -1));
- };
+ // and save the snapshot
+ machine().video().save_snapshot(&sdev, file);
+ return sol::lua_nil;
+ });
+ screen_dev_type.set_function("pixel", &screen_device::pixel);
+ screen_dev_type.set_function(
+ "pixels",
+ [] (screen_device &sdev, sol::this_state s)
+ {
+ const rectangle &visarea = sdev.visible_area();
+ luaL_Buffer buff;
+ int size = visarea.height() * visarea.width() * 4;
+ u32 *const ptr = reinterpret_cast<u32 *>(luaL_buffinitsize(s, &buff, size));
+ sdev.pixels(ptr);
+ luaL_pushresultsize(&buff, size);
+ return std::make_tuple(sol::make_reference(s, sol::stack_reference(s, -1)), visarea.width(), visarea.height());
+ });
screen_dev_type["screen_type"] = sol::property(&screen_device::screen_type);
screen_dev_type["width"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().width(); });
screen_dev_type["height"] = sol::property([] (screen_device &sdev) { return sdev.visible_area().height(); });
screen_dev_type["refresh"] = sol::property([] (screen_device &sdev) { return ATTOSECONDS_TO_HZ(sdev.refresh_attoseconds()); });
screen_dev_type["refresh_attoseconds"] = sol::property([] (screen_device &sdev) { return sdev.refresh_attoseconds(); });
- screen_dev_type["xofffset"] = sol::property(&screen_device::xoffset);
- screen_dev_type["yofffset"] = sol::property(&screen_device::yoffset);
+ screen_dev_type["xoffset"] = sol::property(&screen_device::xoffset);
+ screen_dev_type["yoffset"] = sol::property(&screen_device::yoffset);
screen_dev_type["xscale"] = sol::property(&screen_device::xscale);
screen_dev_type["yscale"] = sol::property(&screen_device::yscale);
screen_dev_type["pixel_period"] = sol::property([] (screen_device &sdev) { return sdev.pixel_period().as_double(); });
@@ -1571,6 +1806,7 @@ void lua_engine::initialize()
screen_dev_type["frame_period"] = sol::property([] (screen_device &sdev) { return sdev.frame_period().as_double(); });
screen_dev_type["frame_number"] = &screen_device::frame_number;
screen_dev_type["container"] = sol::property(&screen_device::container);
+ screen_dev_type["palette"] = sol::property([] (screen_device const &sdev) { return sdev.has_palette() ? &sdev.palette() : nullptr; });
auto cass_type = sol().registry().new_usertype<cassette_image_device>(
@@ -1593,11 +1829,65 @@ void lua_engine::initialize()
auto image_type = sol().registry().new_usertype<device_image_interface>("image", sol::no_constructor);
- image_type["load"] = &device_image_interface::load;
- image_type["load_software"] = static_cast<image_init_result (device_image_interface::*)(std::string_view)>(&device_image_interface::load_software);
- image_type["unload"] = &device_image_interface::unload;
- image_type["create"] = static_cast<image_init_result (device_image_interface::*)(std::string_view)>(&device_image_interface::create);
- image_type["display"] = &device_image_interface::call_display;
+ image_type.set_function("load",
+ [] (device_image_interface &di, sol::this_state s, std::string_view path) -> sol::object
+ {
+ auto [err, message] = di.load(path);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("load_software",
+ [] (device_image_interface &di, sol::this_state s, std::string_view identifier) -> sol::object
+ {
+ auto [err, message] = di.load_software(identifier);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("unload", &device_image_interface::unload);
+ image_type.set_function("create",
+ [] (device_image_interface &di, sol::this_state s, std::string_view path) -> sol::object
+ {
+ auto [err, message] = di.create(path);
+ if (!err)
+ return sol::lua_nil;
+ else if (!message.empty())
+ return sol::make_object(s, message);
+ else
+ return sol::make_object(s, err.message());
+ });
+ image_type.set_function("display", &device_image_interface::call_display);
+ image_type.set_function("add_media_change_notifier",
+ [this] (device_image_interface &di, sol::protected_function cb)
+ {
+ return di.add_media_change_notifier(
+ [this, cbfunc = sol::protected_function(m_lua_state, cb)] (device_image_interface::media_change_event ev)
+ {
+ char const *evstr(nullptr);
+ switch (ev)
+ {
+ case device_image_interface::media_change_event::LOADED:
+ evstr = "loaded";
+ break;
+ case device_image_interface::media_change_event::UNLOADED:
+ evstr = "unloaded";
+ break;
+ }
+ auto status(invoke(cbfunc, evstr));
+ if (!status.valid())
+ {
+ auto err(status.template get<sol::error>());
+ osd_printf_error("[LUA ERROR] error in media change callback: %s\n", err.what());
+ }
+ });
+ });
image_type["is_readable"] = sol::property(&device_image_interface::is_readable);
image_type["is_writeable"] = sol::property(&device_image_interface::is_writeable);
image_type["is_creatable"] = sol::property(&device_image_interface::is_creatable);
@@ -1640,6 +1930,38 @@ void lua_engine::initialize()
image_type["device"] = sol::property(static_cast<device_t & (device_image_interface::*)()>(&device_image_interface::device));
+ auto state_entry_type = sol().registry().new_usertype<device_state_entry>("state_entry", sol::no_constructor);
+ state_entry_type["value"] = sol::property(
+ [] (device_state_entry const &entry, sol::this_state s) -> sol::object
+ {
+ if (entry.is_float())
+ return sol::make_object(s, entry.dvalue());
+ else
+ return sol::make_object(s, entry.value());
+ },
+ [] (device_state_entry const &entry, sol::this_state s, sol::object value)
+ {
+ if (!entry.writeable())
+ luaL_error(s, "cannot set value of read-only device state entry");
+ else if (entry.is_float())
+ entry.set_dvalue(value.as<double>());
+ else
+ entry.set_value(value.as<u64>());
+ });
+ state_entry_type["symbol"] = sol::property(&device_state_entry::symbol);
+ state_entry_type["visible"] = sol::property(&device_state_entry::visible);
+ state_entry_type["writeable"] = sol::property(&device_state_entry::writeable);
+ state_entry_type["is_float"] = sol::property(&device_state_entry::is_float);
+ state_entry_type["datamask"] = sol::property(
+ [] (device_state_entry const &entry)
+ {
+ return entry.is_float() ? std::optional<u64>() : std::optional<u64>(entry.datamask());
+ });
+ state_entry_type["datasize"] = sol::property(&device_state_entry::datasize);
+ state_entry_type["max_length"] = sol::property(&device_state_entry::max_length);
+ state_entry_type[sol::meta_function::to_string] = &device_state_entry::to_string;
+
+
auto format_type = sol().registry().new_usertype<image_device_format>("image_format", sol::no_constructor);
format_type["name"] = sol::property(&image_device_format::name);
format_type["description"] = sol::property(&image_device_format::description);
@@ -1760,54 +2082,21 @@ void lua_engine::initialize()
auto ui_type = sol().registry().new_usertype<mame_ui_manager>("ui", sol::no_constructor);
// sol converts char32_t to a string
- ui_type["get_char_width"] = [] (mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); };
- ui_type["get_string_width"] = &mame_ui_manager::get_string_width;
- ui_type["set_aggressive_input_focus"] = [](mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); };
+ ui_type.set_function("get_char_width", [] (mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); });
+ ui_type.set_function("get_string_width", static_cast<float (mame_ui_manager::*)(std::string_view)>(&mame_ui_manager::get_string_width));
+ ui_type.set_function("set_aggressive_input_focus", [] (mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); });
ui_type["get_general_input_setting"] = sol::overload(
// TODO: overload with sequence type string - parser isn't available here
[] (mame_ui_manager &ui, ioport_type type, int player) { return ui.get_general_input_setting(type, player, SEQ_TYPE_STANDARD); },
[] (mame_ui_manager &ui, ioport_type type) { return ui.get_general_input_setting(type, 0, SEQ_TYPE_STANDARD); });
ui_type["options"] = sol::property([] (mame_ui_manager &m) { return static_cast<core_options *>(&m.options()); });
- ui_type["line_height"] = sol::property(&mame_ui_manager::get_line_height);
+ ui_type["line_height"] = sol::property([] (mame_ui_manager &m) { return m.get_line_height(); });
ui_type["menu_active"] = sol::property(&mame_ui_manager::is_menu_active);
+ ui_type["ui_active"] = sol::property(&mame_ui_manager::ui_active, &mame_ui_manager::set_ui_active);
ui_type["single_step"] = sol::property(&mame_ui_manager::single_step, &mame_ui_manager::set_single_step);
ui_type["show_fps"] = sol::property(&mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps);
ui_type["show_profiler"] = sol::property(&mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler);
-
-
-/* device_state_entry library
- *
- * manager:machine().devices[device_tag].state[state_name]
- *
- * state:name() - get device state name
- * state:is_visible() - is state visible in debugger
- * state:is_divider() - is state a divider
- *
- * state.value - get device state value
- */
-
- auto dev_state_type = sol().registry().new_usertype<device_state_entry>("dev_state", "new", sol::no_constructor);
- dev_state_type.set("name", &device_state_entry::symbol);
- dev_state_type.set("value", sol::property(
- [this](device_state_entry &entry) -> uint64_t {
- device_state_interface *state = entry.parent_state();
- if(state)
- {
- machine().save().dispatch_presave();
- return state->state_int(entry.index());
- }
- return 0;
- },
- [this](device_state_entry &entry, uint64_t val) {
- device_state_interface *state = entry.parent_state();
- if(state)
- {
- state->set_state_int(entry.index(), val);
- machine().save().dispatch_presave();
- }
- }));
- dev_state_type.set("is_visible", &device_state_entry::visible);
- dev_state_type.set("is_divider", &device_state_entry::divider);
+ ui_type["image_display_enabled"] = sol::property(&mame_ui_manager::image_display_enabled, &mame_ui_manager::set_image_display_enabled);
/* rom_entry library
@@ -1875,6 +2164,10 @@ void lua_engine::initialize()
//-------------------------------------------------
bool lua_engine::frame_hook()
{
+ std::vector<int> tasks = std::move(m_update_tasks);
+ m_update_tasks.clear();
+ resume_tasks(m_lua_state, tasks, true); // TODO: doesn't need to return anything
+
return execute_function("LUA_ON_FRAME_DONE");
}
@@ -1884,6 +2177,10 @@ bool lua_engine::frame_hook()
void lua_engine::close()
{
+ m_notifiers.reset();
+ m_menu.clear();
+ m_update_tasks.clear();
+ m_frame_tasks.clear();
m_sol_state.reset();
if (m_lua_state)
{
@@ -1893,49 +2190,46 @@ void lua_engine::close()
}
}
-void lua_engine::resume(void *ptr, int nparam)
+void lua_engine::resume(s32 param)
{
- lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, nparam);
- lua_State *L = lua_tothread(m_lua_state, -1);
- lua_pop(m_lua_state, 1);
- int stat = lua_resume(L, nullptr, 0);
- if((stat != LUA_OK) && (stat != LUA_YIELD))
- {
- osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(L, -1));
- lua_pop(L, 1);
- }
- luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam);
+ attotime const now = machine().time();
+ auto const pos = std::find_if(
+ m_waiting_tasks.begin(),
+ m_waiting_tasks.end(),
+ [&now] (auto const &x) { return now < x.first; });
+ std::vector<int> expired;
+ expired.reserve(std::distance(m_waiting_tasks.begin(), pos));
+ for (auto it = m_waiting_tasks.begin(); pos != it; ++it)
+ expired.emplace_back(it->second);
+ m_waiting_tasks.erase(m_waiting_tasks.begin(), pos);
+ if (!m_waiting_tasks.empty())
+ m_timer->reset(m_waiting_tasks.begin()->first - now);
+ resume_tasks(m_lua_state, expired, true);
}
-void lua_engine::run(sol::load_result res)
+//-------------------------------------------------
+// load_script - load script from file path
+//-------------------------------------------------
+
+sol::load_result lua_engine::load_script(std::string const &filename)
{
- if(res.valid())
- {
- auto ret = invoke(res.get<sol::protected_function>());
- if(!ret.valid())
- {
- sol::error err = ret;
- osd_printf_error("[LUA ERROR] in run: %s\n", err.what());
- }
- }
- else
- osd_printf_error("[LUA ERROR] %d loading Lua script\n", (int)res.status());
+ return sol().load_file(filename);
}
//-------------------------------------------------
-// execute - load and execute script
+// load_string - load script from string
//-------------------------------------------------
-void lua_engine::load_script(const char *filename)
+sol::load_result lua_engine::load_string(std::string const &value)
{
- run(sol().load_file(filename));
+ return sol().load(value);
}
//-------------------------------------------------
-// execute_string - execute script from string
+// make_environment - make a sandbox
//-------------------------------------------------
-void lua_engine::load_string(const char *value)
+sol::environment lua_engine::make_environment()
{
- run(sol().load(value));
+ return sol::environment(sol(), sol::create, sol().globals());
}
diff --git a/src/frontend/mame/luaengine.h b/src/frontend/mame/luaengine.h
index f61baa8c083..de94e7ba729 100644
--- a/src/frontend/mame/luaengine.h
+++ b/src/frontend/mame/luaengine.h
@@ -12,7 +12,8 @@
#pragma once
-#include <condition_variable>
+#include "notifier.h"
+
#include <functional>
#include <map>
#include <memory>
@@ -21,7 +22,12 @@
#include <tuple>
#include <vector>
+#define SOL_USING_CXX_LUA 1
+#ifdef MAME_DEBUG
+#define SOL_ALL_SAFETIES_ON 1
+#else
#define SOL_SAFE_USERTYPE 1
+#endif
#include "sol/sol.hpp"
struct lua_State;
@@ -43,8 +49,9 @@ public:
~lua_engine();
void initialize();
- void load_script(const char *filename);
- void load_string(const char *value);
+ sol::load_result load_script(std::string const &filename);
+ sol::load_result load_string(std::string const &value);
+ sol::environment make_environment();
bool frame_hook();
@@ -52,9 +59,8 @@ public:
std::pair<bool, std::optional<long> > menu_callback(const std::string &menu, int index, const std::string &event);
void set_machine(running_machine *machine);
- std::vector<std::string> &get_menu() { return m_menu; }
+ std::vector<std::string> const &get_menu() { return m_menu; }
void attach_notifiers();
- void on_frame_done();
void on_sound_update();
void on_periodic();
bool on_missing_mandatory_image(const std::string &instance_name);
@@ -117,29 +123,46 @@ public:
sol::state_view &sol() const { return *m_sol_state; }
-private:
- template<typename T, size_t SIZE> class enum_parser;
+ template <typename Func, typename... Params>
+ sol::protected_function_result invoke(Func &&func, Params&&... args)
+ {
+ auto profile = g_profiler.start(PROFILER_LUA);
- struct addr_space;
+ sol::thread th = sol::thread::create(m_lua_state);
+ sol::coroutine cr(th.state(), std::forward<Func>(func));
+ return cr(std::forward<Params>(args)...);
+ }
- struct save_item {
- void *base;
- unsigned int size;
- unsigned int count;
- unsigned int valcount;
- unsigned int blockcount;
- unsigned int stride;
- };
+ template <typename Func, typename... Params>
+ static auto invoke_direct(Func &&func, Params&&... args)
+ {
+ auto profile = g_profiler.start(PROFILER_LUA);
+ return func(std::forward<Params>(args)...);
+ }
- struct context
+private:
+ struct notifiers
{
- context() { busy = false; yield = false; }
- std::string result;
- std::condition_variable sync;
- bool busy;
- bool yield;
+ util::notifier<> on_reset;
+ util::notifier<> on_stop;
+ util::notifier<> on_pause;
+ util::notifier<> on_resume;
+ util::notifier<> on_frame;
+ util::notifier<> on_presave;
+ util::notifier<> on_postload;
};
+ template <typename T, size_t Size> class enum_parser;
+
+ class buffer_helper;
+ struct addr_space;
+ class palette_wrapper;
+ template <typename T> class bitmap_helper;
+ class tap_helper;
+ class addr_space_change_notif;
+ class symbol_table_wrapper;
+ class expression_wrapper;
+
// internal state
lua_State *m_lua_state;
std::unique_ptr<sol::state_view> m_sol_state;
@@ -147,31 +170,40 @@ private:
std::vector<std::string> m_menu;
- template <typename R, typename T, typename D>
- auto make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&), D &&dflt, const char *name, const char *desc);
+ emu_timer *m_timer;
+
+ // machine event notifiers
+ std::optional<notifiers> m_notifiers;
+
+ // deferred coroutines
+ std::vector<std::pair<attotime, int> > m_waiting_tasks;
+ std::vector<int> m_update_tasks;
+ std::vector<int> m_frame_tasks;
+
+ template <typename... T>
+ auto make_notifier_adder(util::notifier<T...> &notifier, const char *desc);
+ template <typename T, typename D, typename R, typename... A>
+ auto make_simple_callback_setter(void (T::*setter)(delegate<R (A...)> &&), D &&dflt, const char *name, const char *desc);
running_machine &machine() const { return *m_machine; }
void on_machine_prestart();
- void on_machine_start();
+ void on_machine_reset();
void on_machine_stop();
void on_machine_pause();
void on_machine_resume();
void on_machine_frame();
+ void on_machine_presave();
+ void on_machine_postload();
- void resume(void *ptr, int nparam);
+ void resume(s32 param);
void register_function(sol::function func, const char *id);
- int enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback);
+ template <typename T> size_t enumerate_functions(const char *id, T &&callback);
bool execute_function(const char *id);
sol::object call_plugin(const std::string &name, sol::object in);
void close();
- void run(sol::load_result res);
-
- template <typename TFunc, typename... TArgs>
- sol::protected_function_result invoke(TFunc &&func, TArgs&&... args);
-
void initialize_debug(sol::table &emu);
void initialize_input(sol::table &emu);
void initialize_memory(sol::table &emu);
diff --git a/src/frontend/mame/luaengine.ipp b/src/frontend/mame/luaengine.ipp
index 10d1f55f33f..e08f267538e 100644
--- a/src/frontend/mame/luaengine.ipp
+++ b/src/frontend/mame/luaengine.ipp
@@ -14,9 +14,13 @@
#include "options.h"
-#include <lua.hpp>
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <cassert>
#include <system_error>
+#include <type_traits>
@@ -38,53 +42,144 @@ struct lua_engine::tag_object_ptr_map
};
-namespace sol {
-
-class buffer
+class lua_engine::buffer_helper
{
-public:
- // sol does lua_settop(0), save userdata buffer in registry if necessary
- buffer(int size, lua_State *L)
+private:
+ class proxy
{
- ptr = luaL_buffinitsize(L, &buff, size);
- len = size;
- if(buff.b != buff.initb)
+ private:
+ buffer_helper &m_host;
+ char *m_space;
+ size_t const m_size;
+
+ public:
+ proxy(proxy const &) = delete;
+ proxy &operator=(proxy const &) = delete;
+
+ proxy(proxy &&that) : m_host(that.m_host), m_space(that.m_space), m_size(that.m_size)
{
- lua_pushvalue(L, -1);
- lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp");
+ that.m_space = nullptr;
}
- }
- ~buffer()
- {
- lua_State *L = buff.L;
- if(lua_getfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp") != LUA_TNIL)
+
+ proxy(buffer_helper &host, size_t size) : m_host(host), m_space(luaL_prepbuffsize(&host.m_buffer, size)), m_size(size)
{
- lua_pushnil(L);
- lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp");
+ m_host.m_prepared = true;
}
- else
- lua_pop(L, -1);
- luaL_pushresultsize(&buff, len);
+ ~proxy()
+ {
+ if (m_space)
+ {
+ assert(m_host.m_prepared);
+ luaL_addsize(&m_host.m_buffer, 0U);
+ m_host.m_prepared = false;
+ }
+ }
+
+ char *get()
+ {
+ return m_space;
+ }
+
+ void add(size_t size)
+ {
+ assert(m_space);
+ assert(size <= m_size);
+ assert(m_host.m_prepared);
+ m_space = nullptr;
+ luaL_addsize(&m_host.m_buffer, size);
+ m_host.m_prepared = false;
+ }
+ };
+
+ luaL_Buffer m_buffer;
+ bool m_valid;
+ bool m_prepared;
+
+public:
+ buffer_helper(buffer_helper const &) = delete;
+ buffer_helper &operator=(buffer_helper const &) = delete;
+
+ buffer_helper(lua_State *L)
+ {
+ luaL_buffinit(L, &m_buffer);
+ m_valid = true;
+ m_prepared = false;
+ }
+
+ ~buffer_helper()
+ {
+ assert(!m_prepared);
+ if (m_valid)
+ luaL_pushresult(&m_buffer);
+ }
+
+ void push()
+ {
+ assert(m_valid);
+ assert(!m_prepared);
+ luaL_pushresult(&m_buffer);
+ m_valid = false;
+ }
+
+ proxy prepare(size_t size)
+ {
+ assert(m_valid);
+ assert(!m_prepared);
+ return proxy(*this, size);
+ }
+};
+
+
+class lua_engine::palette_wrapper
+{
+public:
+ palette_wrapper(uint32_t numcolors, uint32_t numgroups) : m_palette(palette_t::alloc(numcolors, numgroups))
+ {
+ }
+
+ palette_wrapper(palette_t &pal) : m_palette(&pal)
+ {
+ m_palette->ref();
+ }
+
+ palette_wrapper(palette_wrapper const &that) : m_palette(that.m_palette)
+ {
+ m_palette->ref();
+ }
+
+ ~palette_wrapper()
+ {
+ m_palette->deref();
+ }
+
+ palette_wrapper &operator=(palette_wrapper const &that)
+ {
+ that.m_palette->ref();
+ m_palette->deref();
+ m_palette = that.m_palette;
+ return *this;
+ }
+
+ palette_t const &palette() const
+ {
+ return *m_palette;
}
- void set_len(int size) { len = size; }
- int get_len() { return len; }
- char *get_ptr() { return ptr; }
+ palette_t &palette()
+ {
+ return *m_palette;
+ }
private:
- luaL_Buffer buff;
- int len;
- char *ptr;
+ palette_t *m_palette;
};
-// don't convert core_optons to a table directly
-template <> struct is_container<core_options> : std::false_type { };
+namespace sol {
-// buffer customisation
-sol::buffer *sol_lua_get(sol::types<buffer *>, lua_State *L, int index, sol::stack::record &tracking);
-int sol_lua_push(sol::types<buffer *>, lua_State *L, buffer *value);
+// don't convert core_options to a table directly
+template <> struct is_container<core_options> : std::false_type { };
// these things should be treated as containers
@@ -265,8 +360,6 @@ int sol_lua_push(sol::types<std::error_condition>, lua_State &L, std::error_cond
// enums to automatically convert to strings
int sol_lua_push(sol::types<map_handler_type>, lua_State *L, map_handler_type &&value);
-int sol_lua_push(sol::types<image_init_result>, lua_State *L, image_init_result &&value);
-int sol_lua_push(sol::types<image_verify_result>, lua_State *L, image_verify_result &&value);
int sol_lua_push(sol::types<endianness_t>, lua_State *L, endianness_t &&value);
@@ -366,7 +459,10 @@ protected:
result = sol::stack::push(L, i.ix + 1);
else
result = T::push_key(L, i.it, i.ix);
- result += sol::stack::push_reference(L, T::unwrap(i.it));
+ if constexpr (std::is_reference_v<decltype(T::unwrap(i.it))>)
+ result += sol::stack::push_reference(L, std::ref(T::unwrap(i.it)));
+ else
+ result += sol::stack::push_reference(L, T::unwrap(i.it));
++i;
return result;
}
@@ -387,9 +483,16 @@ public:
T &self(immutable_sequence_helper::get_self(L));
std::ptrdiff_t const index(sol::stack::unqualified_get<std::ptrdiff_t>(L, 2));
if ((0 >= index) || (self.items().size() < index))
+ {
return sol::stack::push(L, sol::lua_nil);
+ }
else
- return sol::stack::push_reference(L, T::unwrap(std::next(self.items().begin(), index - 1)));
+ {
+ if constexpr (std::is_reference_v<decltype(T::unwrap(std::next(self.items().begin(), index - 1)))>)
+ return sol::stack::push_reference(L, std::ref(T::unwrap(std::next(self.items().begin(), index - 1))));
+ else
+ return sol::stack::push_reference(L, T::unwrap(std::next(self.items().begin(), index - 1)));
+ }
}
static int index_of(lua_State *L)
@@ -444,13 +547,13 @@ struct lua_engine::addr_space
};
-template <typename T, size_t SIZE>
+template <typename T, size_t Size>
class lua_engine::enum_parser
{
public:
constexpr enum_parser(std::initializer_list<std::pair<std::string_view, T> > values)
{
- if (values.size() != SIZE)
+ if (values.size() != Size)
throw false && "size template argument incorrectly specified";
std::copy(values.begin(), values.end(), m_map.begin());
}
@@ -467,49 +570,84 @@ public:
}
private:
- std::array<std::pair<std::string_view, T>, SIZE> m_map;
+ std::array<std::pair<std::string_view, T>, Size> m_map;
};
//-------------------------------------------------
+// make_notifier_adder - make a function for
+// subscribing to a notifier
+//-------------------------------------------------
+
+template <typename... T>
+auto lua_engine::make_notifier_adder(util::notifier<T...> &notifier, const char *desc)
+{
+ return
+ [this, &notifier, desc] (sol::protected_function cb)
+ {
+ return notifier.subscribe(
+ delegate<void (T...)>(
+ [this, desc, cbfunc = sol::protected_function(m_lua_state, cb)] (T... args)
+ {
+ auto status(invoke(cbfunc, std::forward<T>(args)...));
+ if (!status.valid())
+ {
+ auto err(status.template get<sol::error>());
+ osd_printf_error("[LUA ERROR] error in %s callback: %s\n", desc, err.what());
+ }
+ }));
+ };
+}
+
+
+//-------------------------------------------------
// make_simple_callback_setter - make a callback
// setter for simple cases
//-------------------------------------------------
-template <typename R, typename T, typename D>
-auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&), D &&dflt, const char *name, const char *desc)
+template <typename T, typename D, typename R, typename... A>
+auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R (A...)> &&), D &&dflt, const char *name, const char *desc)
{
return
[this, setter, dflt, name, desc] (T &self, sol::object cb)
{
if (cb == sol::lua_nil)
{
- (self.*setter)(delegate<R ()>());
+ (self.*setter)(delegate<R (A...)>());
}
else if (cb.is<sol::protected_function>())
{
- (self.*setter)(delegate<R ()>(
- [this, dflt, desc, cbfunc = cb.as<sol::protected_function>()] () -> R
+ (self.*setter)(delegate<R (A...)>(
+ [dflt, desc, cbfunc = sol::protected_function(m_lua_state, cb)] (A... args) -> R
{
- if constexpr (std::is_same_v<R, void>)
- {
- (void)dflt;
- (void)desc;
- invoke(cbfunc);
- }
- else
+ auto status(invoke_direct(cbfunc, std::forward<A>(args)...));
+ if (status.valid())
{
- auto result(invoke(cbfunc).get<sol::optional<R> >());
- if (result)
+ if constexpr (std::is_same_v<R, void>)
{
- return *result;
+ std::ignore = dflt;
}
else
{
- osd_printf_error("[LUA ERROR] invalid return from %s callback\n", desc);
- return dflt();
+ auto result(status.template get<std::optional<R> >());
+ if (result)
+ {
+ return *result;
+ }
+ else
+ {
+ osd_printf_error("[LUA ERROR] invalid return from %s callback\n", desc);
+ return dflt();
+ }
}
}
+ else
+ {
+ auto err(status.template get<sol::error>());
+ osd_printf_error("[LUA ERROR] error in %s callback: %s\n", desc, err.what());
+ if constexpr (!std::is_same_v<R, void>)
+ return dflt();
+ }
}));
}
else
@@ -519,18 +657,4 @@ auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&
};
}
-
-//-------------------------------------------------
-// invoke - invokes a function, wrapping profiler
-//-------------------------------------------------
-
-template <typename TFunc, typename... TArgs>
-inline sol::protected_function_result lua_engine::invoke(TFunc &&func, TArgs &&... args)
-{
- g_profiler.start(PROFILER_LUA);
- sol::protected_function_result result = func(std::forward<TArgs>(args)...);
- g_profiler.stop();
- return result;
-}
-
#endif // MAME_FRONTEND_MAME_LUAENGINE_IPP
diff --git a/src/frontend/mame/luaengine_debug.cpp b/src/frontend/mame/luaengine_debug.cpp
index 8349cbd88fc..8cdf29246c0 100644
--- a/src/frontend/mame/luaengine_debug.cpp
+++ b/src/frontend/mame/luaengine_debug.cpp
@@ -14,6 +14,7 @@
#include "debug/debugcon.h"
#include "debug/debugcpu.h"
#include "debug/debugvw.h"
+#include "debug/express.h"
#include "debug/points.h"
#include "debug/textbuf.h"
#include "debugger.h"
@@ -83,6 +84,136 @@ sol::object do_watchpoint_enable(device_debug &dev, sol::this_state s, sol::obje
} // anonymous namespace
+class lua_engine::symbol_table_wrapper
+{
+public:
+ symbol_table_wrapper(symbol_table_wrapper const &) = delete;
+
+ symbol_table_wrapper(lua_engine &host, running_machine &machine, std::shared_ptr<symbol_table_wrapper> const &parent, device_t *device)
+ : m_host(host)
+ , m_table(machine, parent ? &parent->table() : nullptr, device)
+ , m_parent(parent)
+ {
+ }
+
+ symbol_table &table() { return m_table; }
+ symbol_table const &table() const { return m_table; }
+ std::shared_ptr<symbol_table_wrapper> const &parent() { return m_parent; }
+
+ symbol_entry &add(char const *name) { return m_table.add(name, symbol_table::READ_WRITE); }
+ symbol_entry &add(char const *name, u64 value) { return m_table.add(name, value); }
+ symbol_entry &add(char const *name, sol::protected_function getter, std::optional<sol::protected_function> setter, std::optional<char const *> format)
+ {
+ symbol_table::setter_func setfun;
+ if (setter)
+ {
+ setfun =
+ [this, cbfunc = sol::protected_function(m_host.m_lua_state, *setter)] (u64 value)
+ {
+ auto status = m_host.invoke(cbfunc, value);
+ if (!status.valid())
+ {
+ sol::error err = status;
+ osd_printf_error("[LUA EROR] in symbol value setter callback: %s\n", err.what());
+ }
+ };
+ }
+ return m_table.add(
+ name,
+ [this, cbfunc = sol::protected_function(m_host.m_lua_state, getter)] () -> u64
+ {
+ auto status = m_host.invoke(cbfunc);
+ if (status.valid())
+ {
+ auto result = status.get<std::optional<u64> >();
+ if (result)
+ return *result;
+
+ osd_printf_error("[LUA EROR] invalid return from symbol value getter callback\n");
+ }
+ else
+ {
+ sol::error err = status;
+ osd_printf_error("[LUA EROR] in symbol value getter callback: %s\n", err.what());
+ }
+ return 0;
+ },
+ std::move(setfun),
+ (format && *format) ? *format : "");
+ }
+ symbol_entry *find(char const *name) const { return m_table.find(name); }
+ symbol_entry *find_deep(char const *name) { return m_table.find_deep(name); }
+
+ u64 value(const char *symbol) { return m_table.value(symbol); }
+ void set_value(const char *symbol, u64 value) { m_table.set_value(symbol, value); }
+
+ u64 read_memory(addr_space &space, offs_t address, int size, bool translate) { return m_table.read_memory(space.space, address, size, translate); }
+ void write_memory(addr_space &space, offs_t address, u64 data, int size, bool translate) { m_table.write_memory(space.space, address, data, size, translate); }
+
+private:
+ lua_engine &m_host;
+ symbol_table m_table;
+ std::shared_ptr<symbol_table_wrapper> const m_parent;
+};
+
+
+class lua_engine::expression_wrapper
+{
+public:
+ expression_wrapper(expression_wrapper const &) = delete;
+ expression_wrapper &operator=(expression_wrapper const &) = delete;
+
+ expression_wrapper(std::shared_ptr<symbol_table_wrapper> const &symtable)
+ : m_expression(symtable->table())
+ , m_symbols(symtable)
+ {
+ }
+
+ void set_default_base(int base) { m_expression.set_default_base(base); }
+
+ void parse(sol::this_state s, char const *string)
+ {
+ try
+ {
+ m_expression.parse(string);
+ }
+ catch (expression_error const &err)
+ {
+ sol::stack::push(s, err);
+ lua_error(s);
+ }
+ }
+
+ u64 execute(sol::this_state s)
+ {
+ try
+ {
+ return m_expression.execute();
+ }
+ catch (expression_error const &err)
+ {
+ sol::stack::push(s, err);
+ lua_error(s);
+ return 0; // unreachable - lua_error doesn't return
+ }
+ }
+
+ bool is_empty() const { return m_expression.is_empty(); }
+ char const *original_string() const { return m_expression.original_string(); }
+ std::shared_ptr<symbol_table_wrapper> const &symbols() { return m_symbols; }
+
+ void set_symbols(std::shared_ptr<symbol_table_wrapper> const &symtable)
+ {
+ m_expression.set_symbols(symtable->table());
+ m_symbols = symtable;
+ }
+
+private:
+ parsed_expression m_expression;
+ std::shared_ptr<symbol_table_wrapper> m_symbols;
+};
+
+
void lua_engine::initialize_debug(sol::table &emu)
{
@@ -94,9 +225,160 @@ void lua_engine::initialize_debug(sol::table &emu)
{ "wr", read_or_write::READWRITE }
};
+ static const enum_parser<expression_space, 15> s_expression_space_parser =
+ {
+ { "p", EXPSPACE_PROGRAM_LOGICAL }, { "lp", EXPSPACE_PROGRAM_LOGICAL }, { "pp", EXPSPACE_PROGRAM_PHYSICAL },
+ { "d", EXPSPACE_DATA_LOGICAL }, { "ld", EXPSPACE_DATA_LOGICAL }, { "pd", EXPSPACE_DATA_PHYSICAL },
+ { "i", EXPSPACE_IO_LOGICAL }, { "li", EXPSPACE_IO_LOGICAL }, { "pi", EXPSPACE_IO_PHYSICAL },
+ { "3", EXPSPACE_OPCODE_LOGICAL }, { "l3", EXPSPACE_OPCODE_LOGICAL }, { "p3", EXPSPACE_OPCODE_PHYSICAL },
+ { "r", EXPSPACE_PRGDIRECT },
+ { "o", EXPSPACE_OPDIRECT },
+ { "m", EXPSPACE_REGION }
+ };
+
+ auto expression_error_type = emu.new_usertype<expression_error>(
+ "expression_error",
+ sol::no_constructor);
+ expression_error_type["code"] = sol::property(
+ [] (expression_error const &err) { return int(err.code()); });
+ expression_error_type["offset"] = sol::property(&expression_error::offset);
+ expression_error_type[sol::meta_function::to_string] = &expression_error::code_string;
+
+ auto symbol_table_type = emu.new_usertype<symbol_table_wrapper>(
+ "symbol_table",
+ sol::call_constructor, sol::factories(
+ [this] (running_machine &machine)
+ { return std::make_shared<symbol_table_wrapper>(*this, machine, nullptr, nullptr); },
+ [this] (std::shared_ptr<symbol_table_wrapper> const &parent, device_t *device)
+ { return std::make_shared<symbol_table_wrapper>(*this, parent->table().machine(), parent, device); },
+ [this] (std::shared_ptr<symbol_table_wrapper> const &parent)
+ { return std::make_shared<symbol_table_wrapper>(*this, parent->table().machine(), parent, nullptr); },
+ [this] (device_t &device)
+ { return std::make_shared<symbol_table_wrapper>(*this, device.machine(), nullptr, &device); }));
+ symbol_table_type.set_function("set_memory_modified_func",
+ [this] (symbol_table_wrapper &st, sol::object cb)
+ {
+ if (cb == sol::lua_nil)
+ st.table().set_memory_modified_func(nullptr);
+ else if (cb.is<sol::protected_function>())
+ st.table().set_memory_modified_func([this, cbfunc = sol::protected_function(m_lua_state, cb)] () { invoke(cbfunc); });
+ else
+ osd_printf_error("[LUA ERROR] must call set_memory_modified_func with function or nil\n");
+ });
+ symbol_table_type["add"] = sol::overload(
+ static_cast<symbol_entry &(symbol_table_wrapper::*)(char const *)>(&symbol_table_wrapper::add),
+ static_cast<symbol_entry &(symbol_table_wrapper::*)(char const *, u64)>(&symbol_table_wrapper::add),
+ static_cast<symbol_entry &(symbol_table_wrapper::*)(char const *, sol::protected_function, std::optional<sol::protected_function>, std::optional<char const *>)>(&symbol_table_wrapper::add),
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, sol::lua_nil_t, char const *format) -> symbol_entry &
+ {
+ return st.add(name, getter, std::nullopt, format);
+ },
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, std::optional<sol::protected_function> setter) -> symbol_entry &
+ {
+ return st.add(name, getter, setter, nullptr);
+ },
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter, char const *format) -> symbol_entry &
+ {
+ return st.add(name, getter, std::nullopt, format);
+ },
+ [] (symbol_table_wrapper &st, char const *name, sol::protected_function getter) -> symbol_entry &
+ {
+ return st.add(name, getter, std::nullopt, nullptr);
+ },
+ [this] (symbol_table_wrapper &st, char const *name, int minparams, int maxparams, sol::protected_function execute) -> symbol_entry &
+ {
+ return st.table().add(
+ name,
+ minparams,
+ maxparams,
+ [this, cb = sol::protected_function(m_lua_state, execute)] (int numparams, u64 const *paramlist) -> u64
+ {
+ // TODO: C++20 will make this obsolete
+ class helper
+ {
+ private:
+ u64 const *b, *e;
+ public:
+ helper(int n, u64 const *p) : b(p), e(p + n) { }
+ auto begin() const { return b; }
+ auto end() const { return e; }
+ };
+
+ auto status(invoke(cb, sol::as_args(helper(numparams, paramlist))));
+ if (status.valid())
+ {
+ auto result = status.get<std::optional<u64> >();
+ if (result)
+ return *result;
+
+ osd_printf_error("[LUA EROR] invalid return from symbol execute callback\n");
+ }
+ else
+ {
+ sol::error err = status;
+ osd_printf_error("[LUA EROR] in symbol execute callback: %s\n", err.what());
+ }
+ return 0;
+ });
+ });
+ symbol_table_type.set_function("find", &symbol_table_wrapper::find);
+ symbol_table_type.set_function("find_deep", &symbol_table_wrapper::find_deep);
+ symbol_table_type.set_function("value", &symbol_table_wrapper::value);
+ symbol_table_type.set_function("set_value", &symbol_table_wrapper::set_value);
+ symbol_table_type.set_function("memory_value",
+ [] (symbol_table_wrapper &st, char const *name, char const *space, u32 offset, int size, bool disable_se)
+ {
+ expression_space const es = s_expression_space_parser(space);
+ return st.table().memory_value(name, es, offset, size, disable_se);
+ });
+ symbol_table_type.set_function("set_memory_value",
+ [] (symbol_table_wrapper &st, char const *name, char const *space, u32 offset, int size, u64 value, bool disable_se)
+ {
+ expression_space const es = s_expression_space_parser(space);
+ st.table().set_memory_value(name, es, offset, size, value, disable_se);
+ });
+ symbol_table_type.set_function("read_memory", &symbol_table_wrapper::read_memory);
+ symbol_table_type.set_function("write_memory", &symbol_table_wrapper::write_memory);
+ symbol_table_type["entries"] = sol::property([] (symbol_table_wrapper const &st) { return standard_tag_object_ptr_map<symbol_entry>(st.table().entries()); });
+ symbol_table_type["parent"] = sol::property(&symbol_table_wrapper::parent);
+
+
+ auto parsed_expression_type = emu.new_usertype<expression_wrapper>(
+ "parsed_expression",
+ sol::call_constructor, sol::initializers(
+ [] (expression_wrapper &wrapper, std::shared_ptr<symbol_table_wrapper> const &symbols)
+ {
+ new (&wrapper) expression_wrapper(symbols);
+ },
+ [] (expression_wrapper &wrapper, sol::this_state s, std::shared_ptr<symbol_table_wrapper> const &symbols, char const *expression, int base)
+ {
+ new (&wrapper) expression_wrapper(symbols);
+ wrapper.set_default_base(base);
+ wrapper.parse(s, expression);
+ },
+ [] (expression_wrapper &wrapper, sol::this_state s, std::shared_ptr<symbol_table_wrapper> const &symbols, char const *expression)
+ {
+ new (&wrapper) expression_wrapper(symbols);
+ wrapper.parse(s, expression);
+ }));
+ parsed_expression_type.set_function("set_default_base", &expression_wrapper::set_default_base);
+ parsed_expression_type.set_function("parse", &expression_wrapper::parse);
+ parsed_expression_type.set_function("execute", &expression_wrapper::execute);
+ parsed_expression_type["is_empty"] = sol::property(&expression_wrapper::is_empty);
+ parsed_expression_type["original_string"] = sol::property(&expression_wrapper::original_string);
+ parsed_expression_type["symbols"] = sol::property(&expression_wrapper::symbols, &expression_wrapper::set_symbols);
+
+
+ auto symbol_entry_type = sol().registry().new_usertype<symbol_entry>("symbol_entry", sol::no_constructor);
+ symbol_entry_type["name"] = sol::property(&symbol_entry::name);
+ symbol_entry_type["format"] = sol::property(&symbol_entry::format);
+ symbol_entry_type["is_function"] = sol::property(&symbol_entry::is_function);
+ symbol_entry_type["is_lval"] = sol::property(&symbol_entry::is_lval);
+ symbol_entry_type["value"] = sol::property(&symbol_entry::value, &symbol_entry::set_value);
+
auto debugger_type = sol().registry().new_usertype<debugger_manager>("debugger", sol::no_constructor);
- debugger_type["command"] = [] (debugger_manager &debug, std::string const &cmd) { debug.console().execute_command(cmd, false); };
+ debugger_type.set_function("command", [] (debugger_manager &debug, std::string const &cmd) { debug.console().execute_command(cmd, false); });
debugger_type["consolelog"] = sol::property([] (debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); });
debugger_type["errorlog"] = sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); });
debugger_type["visible_cpu"] = sol::property(
@@ -130,23 +412,23 @@ void lua_engine::initialize_debug(sol::table &emu)
auto device_debug_type = sol().registry().new_usertype<device_debug>("device_debug", sol::no_constructor);
- device_debug_type["step"] =
- [] (device_debug &dev, sol::object num)
- {
- int steps = 1;
- if (num.is<int>())
- steps = num.as<int>();
- dev.single_step(steps);
- };
- device_debug_type["go"] = &device_debug::go;
- device_debug_type["bpset"] =
- [] (device_debug &dev, offs_t address, char const *cond, char const *act)
- {
- int result(dev.breakpoint_set(address, cond, act));
- dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
- dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
- return result;
- };
+ device_debug_type.set_function("step",
+ [] (device_debug &dev, sol::object num)
+ {
+ int steps = 1;
+ if (num.is<int>())
+ steps = num.as<int>();
+ dev.single_step(steps);
+ });
+ device_debug_type.set_function("go", &device_debug::go);
+ device_debug_type.set_function("bpset",
+ [] (device_debug &dev, offs_t address, char const *cond, char const *act)
+ {
+ int result(dev.breakpoint_set(address, cond, act));
+ dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ return result;
+ });
device_debug_type["bpclear"] = sol::overload(
[] (device_debug &dev, int index)
{
@@ -164,24 +446,24 @@ void lua_engine::initialize_debug(sol::table &emu)
dev.device().machine().debug_view().update_all(DVT_DISASSEMBLY);
dev.device().machine().debug_view().update_all(DVT_BREAK_POINTS);
});
- device_debug_type["bpenable"] = &do_breakpoint_enable<true>;
- device_debug_type["bpdisable"] = &do_breakpoint_enable<false>;
- device_debug_type["bplist"] =
- [this] (device_debug &dev)
- {
- sol::table table = sol().create_table();
- for (auto const &bpp : dev.breakpoint_list())
- table[bpp.second->index()] = sol::make_reference(sol(), *bpp.second);
- return table;
- };
- device_debug_type["wpset"] =
- [] (device_debug &dev, addr_space &sp, std::string const &type, offs_t addr, offs_t len, char const *cond, char const *act)
- {
- read_or_write wptype = s_read_or_write_parser(type);
- int result(dev.watchpoint_set(sp.space, wptype, addr, len, cond, act));
- dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
- return result;
- };
+ device_debug_type.set_function("bpenable", &do_breakpoint_enable<true>);
+ device_debug_type.set_function("bpdisable", &do_breakpoint_enable<false>);
+ device_debug_type.set_function("bplist",
+ [this] (device_debug &dev)
+ {
+ sol::table table = sol().create_table();
+ for (auto const &bpp : dev.breakpoint_list())
+ table[bpp.second->index()] = sol::make_reference(sol(), bpp.second.get());
+ return table;
+ });
+ device_debug_type.set_function("wpset",
+ [] (device_debug &dev, addr_space &sp, std::string const &type, offs_t addr, offs_t len, char const *cond, char const *act)
+ {
+ read_or_write const wptype = s_read_or_write_parser(type);
+ int result(dev.watchpoint_set(sp.space, wptype, addr, len, cond, act));
+ dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ return result;
+ });
device_debug_type["wpclear"] = sol::overload(
[] (device_debug &dev, int index)
{
@@ -195,21 +477,31 @@ void lua_engine::initialize_debug(sol::table &emu)
dev.watchpoint_clear_all();
dev.device().machine().debug_view().update_all(DVT_WATCH_POINTS);
});
- device_debug_type["wpenable"] = &do_watchpoint_enable<true>;
- device_debug_type["wpdisable"] = &do_watchpoint_enable<false>;
- device_debug_type["wplist"] =
- [this] (device_debug &dev, addr_space &sp)
- {
- sol::table table = sol().create_table();
- for (auto &wpp : dev.watchpoint_vector(sp.space.spacenum()))
- table[wpp->index()] = sol::make_reference(sol(), *wpp);
- return table;
- };
+ device_debug_type.set_function("wpenable", &do_watchpoint_enable<true>);
+ device_debug_type.set_function("wpdisable", &do_watchpoint_enable<false>);
+ device_debug_type.set_function("wplist",
+ [this] (device_debug &dev, addr_space &sp)
+ {
+ sol::table table = sol().create_table();
+ for (auto &wpp : dev.watchpoint_vector(sp.space.spacenum()))
+ table[wpp->index()] = sol::make_reference(sol(), wpp.get());
+ return table;
+ });
auto breakpoint_type = sol().registry().new_usertype<debug_breakpoint>("breakpoint", sol::no_constructor);
breakpoint_type["index"] = sol::property(&debug_breakpoint::index);
- breakpoint_type["enabled"] = sol::property(&debug_breakpoint::enabled);
+ breakpoint_type["enabled"] = sol::property(
+ &debug_breakpoint::enabled,
+ [] (debug_breakpoint &bp, bool val)
+ {
+ if (bp.enabled() != val)
+ {
+ bp.setEnabled(val);
+ bp.debugInterface()->device().machine().debug_view().update_all(DVT_DISASSEMBLY);
+ bp.debugInterface()->device().machine().debug_view().update_all(DVT_BREAK_POINTS);
+ }
+ });
breakpoint_type["address"] = sol::property(&debug_breakpoint::address);
breakpoint_type["condition"] = sol::property(&debug_breakpoint::condition);
breakpoint_type["action"] = sol::property(&debug_breakpoint::action);
@@ -217,7 +509,16 @@ void lua_engine::initialize_debug(sol::table &emu)
auto watchpoint_type = sol().registry().new_usertype<debug_watchpoint>("watchpoint", sol::no_constructor);
watchpoint_type["index"] = sol::property(&debug_watchpoint::index);
- watchpoint_type["enabled"] = sol::property(&debug_watchpoint::enabled);
+ watchpoint_type["enabled"] = sol::property(
+ &debug_watchpoint::enabled,
+ [] (debug_watchpoint &wp, bool val)
+ {
+ if (wp.enabled() != val)
+ {
+ wp.setEnabled(val);
+ wp.debugInterface()->device().machine().debug_view().update_all(DVT_WATCH_POINTS);
+ }
+ });
watchpoint_type["type"] = sol::property(
[] (debug_watchpoint &wp) -> char const *
{
diff --git a/src/frontend/mame/luaengine_input.cpp b/src/frontend/mame/luaengine_input.cpp
index f32858ccce3..04db2a13e84 100644
--- a/src/frontend/mame/luaengine_input.cpp
+++ b/src/frontend/mame/luaengine_input.cpp
@@ -144,29 +144,50 @@ void lua_engine::initialize_input(sol::table &emu)
ioport_manager_type["count_players"] = &ioport_manager::count_players;
ioport_manager_type["type_pressed"] = sol::overload(
&ioport_manager::type_pressed,
- [] (ioport_manager &im, ioport_type type) { return im.type_pressed(type, 0); });
+ [] (ioport_manager &im, ioport_type type) { return im.type_pressed(type, 0); },
+ [] (ioport_manager &im, input_type_entry const &type) { return im.type_pressed(type.type(), type.player()); });
ioport_manager_type["type_name"] = sol::overload(
&ioport_manager::type_name,
[] (ioport_manager &im, ioport_type type) { return im.type_name(type, 0); });
ioport_manager_type["type_group"] = sol::overload(
&ioport_manager::type_group,
[] (ioport_manager &im, ioport_type type) { return im.type_group(type, 0); });
- ioport_manager_type["type_seq"] =
+ ioport_manager_type["type_seq"] = sol::overload(
[] (ioport_manager &im, ioport_type type, std::optional<int> player, std::optional<char const *> seq_type_string)
{
if (!player)
player = 0;
input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD;
return im.type_seq(type, *player, seq_type);
- };
- ioport_manager_type["set_type_seq"] =
+ },
+ [] (ioport_manager &im, ioport_type type, std::optional<int> player)
+ {
+ if (!player)
+ player = 0;
+ return im.type_seq(type, *player);
+ },
+ [] (ioport_manager &im, input_type_entry const &type, std::optional<char const *> seq_type_string)
+ {
+ input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD;
+ return im.type_seq(type.type(), type.player(), seq_type);
+ },
+ [] (ioport_manager &im, input_type_entry const &type)
+ {
+ return im.type_seq(type.type(), type.player());
+ });
+ ioport_manager_type["set_type_seq"] = sol::overload(
[] (ioport_manager &im, ioport_type type, std::optional<int> player, std::optional<char const *> seq_type_string, input_seq const &seq)
{
if (!player)
player = 0;
input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD;
im.set_type_seq(type, *player, seq_type, seq);
- };
+ },
+ [] (ioport_manager &im, input_type_entry const &type, std::optional<char const *> seq_type_string, input_seq const &seq)
+ {
+ input_seq_type seq_type = seq_type_string ? s_seq_type_parser(*seq_type_string) : SEQ_TYPE_STANDARD;
+ im.set_type_seq(type.type(), type.player(), seq_type, seq);
+ });
ioport_manager_type["token_to_input_type"] =
[] (ioport_manager &im, std::string const &string)
{
@@ -177,6 +198,7 @@ void lua_engine::initialize_input(sol::table &emu)
ioport_manager_type["input_type_to_token"] = sol::overload(
&ioport_manager::input_type_to_token,
[] (ioport_manager &im, ioport_type type) { return im.input_type_to_token(type, 0); });
+ ioport_manager_type["types"] = sol::property(&ioport_manager::types);
ioport_manager_type["ports"] = sol::property([] (ioport_manager &im) { return tag_object_ptr_map<ioport_list>(im.ports()); });
@@ -245,6 +267,7 @@ void lua_engine::initialize_input(sol::table &emu)
auto ioport_field_type = sol().registry().new_usertype<ioport_field>("ioport_field", sol::no_constructor);
ioport_field_type["set_value"] = &ioport_field::set_value;
+ ioport_field_type["clear_value"] = &ioport_field::clear_value;
ioport_field_type["set_input_seq"] =
[] (ioport_field &f, std::string const &seq_type_string, const input_seq &seq)
{
@@ -293,17 +316,31 @@ void lua_engine::initialize_input(sol::table &emu)
ioport_field_type["type"] = sol::property(&ioport_field::type);
ioport_field_type["name"] = sol::property(&ioport_field::name);
ioport_field_type["default_name"] = sol::property(
- [] (ioport_field &f)
+ [] (ioport_field const &f)
{
return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player());
});
ioport_field_type["player"] = sol::property(&ioport_field::player, &ioport_field::set_player);
ioport_field_type["mask"] = sol::property(&ioport_field::mask);
ioport_field_type["defvalue"] = sol::property(&ioport_field::defvalue);
- ioport_field_type["sensitivity"] = sol::property(&ioport_field::sensitivity);
+ ioport_field_type["minvalue"] = sol::property(
+ [] (ioport_field const &f)
+ {
+ return f.is_analog() ? std::make_optional(f.minval()) : std::nullopt;
+ });
+ ioport_field_type["maxvalue"] = sol::property(
+ [] (ioport_field const &f)
+ {
+ return f.is_analog() ? std::make_optional(f.maxval()) : std::nullopt;
+ });
+ ioport_field_type["sensitivity"] = sol::property(
+ [] (ioport_field const &f)
+ {
+ return f.is_analog() ? std::make_optional(f.sensitivity()) : std::nullopt;
+ });
ioport_field_type["way"] = sol::property(&ioport_field::way);
ioport_field_type["type_class"] = sol::property(
- [] (ioport_field &f)
+ [] (ioport_field const &f)
{
switch (f.type_class())
{
@@ -331,7 +368,7 @@ void lua_engine::initialize_input(sol::table &emu)
ioport_field_type["crosshair_scale"] = sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale);
ioport_field_type["crosshair_offset"] = sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset);
ioport_field_type["user_value"] = sol::property(
- [] (ioport_field &f)
+ [] (ioport_field const &f)
{
ioport_field::user_settings settings;
f.get_user_settings(settings);
@@ -359,6 +396,15 @@ void lua_engine::initialize_input(sol::table &emu)
ioport_field_live_type["name"] = &ioport_field_live::name;
+ auto input_type_entry_type = sol().registry().new_usertype<input_type_entry>("input_type_entry", sol::no_constructor);
+ input_type_entry_type["type"] = sol::property(&input_type_entry::type);
+ input_type_entry_type["group"] = sol::property(&input_type_entry::group);
+ input_type_entry_type["player"] = sol::property(&input_type_entry::player);
+ input_type_entry_type["token"] = sol::property(&input_type_entry::token);
+ input_type_entry_type["name"] = sol::property(&input_type_entry::name);
+ input_type_entry_type["is_analog"] = sol::property([] (input_type_entry const &type) { return ioport_manager::type_is_analog(type.type()); });
+
+
auto input_type = sol().registry().new_usertype<input_manager>("input", sol::no_constructor);
input_type["code_value"] = &input_manager::code_value;
input_type["code_pressed"] = &input_manager::code_pressed;
@@ -448,7 +494,7 @@ void lua_engine::initialize_input(sol::table &emu)
[this] (input_device &dev)
{
sol::table result = sol().create_table();
- for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++)
+ for (input_item_id id = ITEM_ID_FIRST_VALID; id <= dev.maxitem(); id++)
{
input_device_item *item = dev.item(id);
if (item)
@@ -466,16 +512,9 @@ void lua_engine::initialize_input(sol::table &emu)
auto uiinput_type = sol().registry().new_usertype<ui_input_manager>("uiinput", sol::no_constructor);
- uiinput_type["find_mouse"] =
- [] (ui_input_manager &ui)
- {
- int32_t x, y;
- bool button;
- render_target *rt = ui.find_mouse(&x, &y, &button);
- return std::make_tuple(x, y, button, rt);
- };
- uiinput_type["pressed"] = &ui_input_manager::pressed;
- uiinput_type["pressed_repeat"] = &ui_input_manager::pressed_repeat;
+ uiinput_type.set_function("reset", &ui_input_manager::reset);
+ uiinput_type.set_function("pressed", &ui_input_manager::pressed);
+ uiinput_type.set_function("pressed_repeat", &ui_input_manager::pressed_repeat);
uiinput_type["presses_enabled"] = sol::property(&ui_input_manager::presses_enabled, &ui_input_manager::set_presses_enabled);
}
diff --git a/src/frontend/mame/luaengine_mem.cpp b/src/frontend/mame/luaengine_mem.cpp
index f95ebdb37c2..ff2f406828b 100644
--- a/src/frontend/mame/luaengine_mem.cpp
+++ b/src/frontend/mame/luaengine_mem.cpp
@@ -11,6 +11,8 @@
#include "emu.h"
#include "luaengine.ipp"
+#include <cstring>
+
namespace {
@@ -26,7 +28,7 @@ T region_read(memory_region &region, offs_t address)
const offs_t lowmask = region.bytewidth() - 1;
for (int i = 0; i < sizeof(T); i++)
{
- int addr = region.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
+ int addr = (region.endianness() == ENDIANNESS_LITTLE) ? (address + sizeof(T) - 1 - i) : (address + i);
if (addr < region.bytes())
{
if constexpr (sizeof(T) > 1)
@@ -52,7 +54,7 @@ void region_write(memory_region &region, offs_t address, T val)
const offs_t lowmask = region.bytewidth() - 1;
for (int i = 0; i < sizeof(T); i++)
{
- int addr = region.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
+ int addr = (region.endianness() == ENDIANNESS_BIG) ? (address + sizeof(T) - 1 - i) : (address + i);
if (addr < region.bytes())
{
if (region.endianness() == ENDIANNESS_BIG)
@@ -130,7 +132,7 @@ void share_write(memory_share &share, offs_t address, T val)
int sol_lua_push(sol::types<map_handler_type>, lua_State *L, map_handler_type &&value)
{
const char *typestr;
- switch(value)
+ switch (value)
{
case AMH_NONE:
typestr = "none";
@@ -184,6 +186,135 @@ int sol_lua_push(sol::types<endianness_t>, lua_State *L, endianness_t &&value)
//-------------------------------------------------
+// tap_helper - class for managing address space
+// taps
+//-------------------------------------------------
+
+class lua_engine::tap_helper
+{
+public:
+ tap_helper(tap_helper const &) = delete;
+ tap_helper(tap_helper &&) = delete;
+
+ tap_helper(
+ lua_engine &host,
+ address_space &space,
+ read_or_write mode,
+ offs_t start,
+ offs_t end,
+ std::string &&name,
+ sol::protected_function &&callback)
+ : m_callback(host.m_lua_state, std::move(callback))
+ , m_space(space)
+ , m_handler()
+ , m_name(std::move(name))
+ , m_start(start)
+ , m_end(end)
+ , m_mode(mode)
+ , m_installing(0U)
+ {
+ reinstall();
+ }
+
+ ~tap_helper()
+ {
+ remove();
+ }
+
+ offs_t start() const noexcept { return m_start; }
+ offs_t end() const noexcept { return m_end; }
+ std::string const &name() const noexcept { return m_name; }
+
+ void reinstall()
+ {
+ switch (m_space.data_width())
+ {
+ case 8: do_install<u8>(); break;
+ case 16: do_install<u16>(); break;
+ case 32: do_install<u32>(); break;
+ case 64: do_install<u64>(); break;
+ }
+ }
+
+ void remove()
+ {
+ ++m_installing;
+ try
+ {
+ m_handler.remove();
+ }
+ catch (...)
+ {
+ --m_installing;
+ throw;
+ }
+ --m_installing;
+ }
+
+private:
+ template <typename T>
+ void do_install()
+ {
+ if (m_installing)
+ return;
+ ++m_installing;
+ try
+ {
+ m_handler.remove();
+
+ switch (m_mode)
+ {
+ case read_or_write::READ:
+ m_handler = m_space.install_read_tap(
+ m_start,
+ m_end,
+ m_name,
+ [this] (offs_t offset, T &data, T mem_mask)
+ {
+ auto result = invoke_direct(m_callback, offset, data, mem_mask).template get<std::optional<T> >();
+ if (result)
+ data = *result;
+ },
+ &m_handler);
+ break;
+ case read_or_write::WRITE:
+ m_handler = m_space.install_write_tap(
+ m_start,
+ m_end,
+ m_name,
+ [this] (offs_t offset, T &data, T mem_mask)
+ {
+ auto result = invoke_direct(m_callback, offset, data, mem_mask).template get<std::optional<T> >();
+ if (result)
+ data = *result;
+ },
+ &m_handler);
+ break;
+ case read_or_write::READWRITE:
+ // won't ever get here, but compilers complain about unhandled enum value
+ break;
+ }
+ }
+ catch (...)
+ {
+ --m_installing;
+ throw;
+ }
+ --m_installing;
+ };
+
+ sol::protected_function m_callback;
+ address_space &m_space;
+ memory_passthrough_handler m_handler;
+ std::string m_name;
+ offs_t const m_start;
+ offs_t const m_end;
+ read_or_write const m_mode;
+ unsigned m_installing;
+};
+
+
+//-------------------------------------------------
// mem_read - templated memory readers for <sign>,<size>
// -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000)
//-------------------------------------------------
@@ -266,32 +397,33 @@ void lua_engine::addr_space::mem_write(offs_t address, T val)
template <typename T>
T lua_engine::addr_space::log_mem_read(offs_t address)
{
- if (!dev.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
+ address_space *tspace;
+ if (!dev.translate(space.spacenum(), device_memory_interface::TR_READ, address, tspace))
return 0;
T mem_content = 0;
switch (sizeof(mem_content) * 8)
{
case 8:
- mem_content = space.read_byte(address);
+ mem_content = tspace->read_byte(address);
break;
case 16:
if (WORD_ALIGNED(address))
- mem_content = space.read_word(address);
+ mem_content = tspace->read_word(address);
else
- mem_content = space.read_word_unaligned(address);
+ mem_content = tspace->read_word_unaligned(address);
break;
case 32:
if (DWORD_ALIGNED(address))
- mem_content = space.read_dword(address);
+ mem_content = tspace->read_dword(address);
else
- mem_content = space.read_dword_unaligned(address);
+ mem_content = tspace->read_dword_unaligned(address);
break;
case 64:
if (QWORD_ALIGNED(address))
- mem_content = space.read_qword(address);
+ mem_content = tspace->read_qword(address);
else
- mem_content = space.read_qword_unaligned(address);
+ mem_content = tspace->read_qword_unaligned(address);
break;
default:
break;
@@ -308,31 +440,32 @@ T lua_engine::addr_space::log_mem_read(offs_t address)
template <typename T>
void lua_engine::addr_space::log_mem_write(offs_t address, T val)
{
- if (!dev.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address))
+ address_space *tspace;
+ if (!dev.translate(space.spacenum(), device_memory_interface::TR_WRITE, address, tspace))
return;
switch (sizeof(val) * 8)
{
case 8:
- space.write_byte(address, val);
+ tspace->write_byte(address, val);
break;
case 16:
if (WORD_ALIGNED(address))
- space.write_word(address, val);
+ tspace->write_word(address, val);
else
- space.write_word_unaligned(address, val);
+ tspace->write_word_unaligned(address, val);
break;
case 32:
if (DWORD_ALIGNED(address))
- space.write_dword(address, val);
+ tspace->write_dword(address, val);
else
- space.write_dword_unaligned(address, val);
+ tspace->write_dword_unaligned(address, val);
break;
case 64:
if (QWORD_ALIGNED(address))
- space.write_qword(address, val);
+ tspace->write_qword(address, val);
else
- space.write_qword_unaligned(address, val);
+ tspace->write_qword_unaligned(address, val);
break;
default:
break;
@@ -400,113 +533,151 @@ void lua_engine::initialize_memory(sol::table &emu)
{
auto addr_space_type = sol().registry().new_usertype<addr_space>("addr_space", sol::no_constructor);
- addr_space_type["read_i8"] = &addr_space::mem_read<s8>;
- addr_space_type["read_u8"] = &addr_space::mem_read<u8>;
- addr_space_type["read_i16"] = &addr_space::mem_read<s16>;
- addr_space_type["read_u16"] = &addr_space::mem_read<u16>;
- addr_space_type["read_i32"] = &addr_space::mem_read<s32>;
- addr_space_type["read_u32"] = &addr_space::mem_read<u32>;
- addr_space_type["read_i64"] = &addr_space::mem_read<s64>;
- addr_space_type["read_u64"] = &addr_space::mem_read<u64>;
- addr_space_type["write_i8"] = &addr_space::mem_write<s8>;
- addr_space_type["write_u8"] = &addr_space::mem_write<u8>;
- addr_space_type["write_i16"] = &addr_space::mem_write<s16>;
- addr_space_type["write_u16"] = &addr_space::mem_write<u16>;
- addr_space_type["write_i32"] = &addr_space::mem_write<s32>;
- addr_space_type["write_u32"] = &addr_space::mem_write<u32>;
- addr_space_type["write_i64"] = &addr_space::mem_write<s64>;
- addr_space_type["write_u64"] = &addr_space::mem_write<u64>;
- addr_space_type["readv_i8"] = &addr_space::log_mem_read<s8>;
- addr_space_type["readv_u8"] = &addr_space::log_mem_read<u8>;
- addr_space_type["readv_i16"] = &addr_space::log_mem_read<s16>;
- addr_space_type["readv_u16"] = &addr_space::log_mem_read<u16>;
- addr_space_type["readv_i32"] = &addr_space::log_mem_read<s32>;
- addr_space_type["readv_u32"] = &addr_space::log_mem_read<u32>;
- addr_space_type["readv_i64"] = &addr_space::log_mem_read<s64>;
- addr_space_type["readv_u64"] = &addr_space::log_mem_read<u64>;
- addr_space_type["writev_i8"] = &addr_space::log_mem_write<s8>;
- addr_space_type["writev_u8"] = &addr_space::log_mem_write<u8>;
- addr_space_type["writev_i16"] = &addr_space::log_mem_write<s16>;
- addr_space_type["writev_u16"] = &addr_space::log_mem_write<u16>;
- addr_space_type["writev_i32"] = &addr_space::log_mem_write<s32>;
- addr_space_type["writev_u32"] = &addr_space::log_mem_write<u32>;
- addr_space_type["writev_i64"] = &addr_space::log_mem_write<s64>;
- addr_space_type["writev_u64"] = &addr_space::log_mem_write<u64>;
- addr_space_type["read_direct_i8"] = &addr_space::direct_mem_read<s8>;
- addr_space_type["read_direct_u8"] = &addr_space::direct_mem_read<u8>;
- addr_space_type["read_direct_i16"] = &addr_space::direct_mem_read<s16>;
- addr_space_type["read_direct_u16"] = &addr_space::direct_mem_read<u16>;
- addr_space_type["read_direct_i32"] = &addr_space::direct_mem_read<s32>;
- addr_space_type["read_direct_u32"] = &addr_space::direct_mem_read<u32>;
- addr_space_type["read_direct_i64"] = &addr_space::direct_mem_read<s64>;
- addr_space_type["read_direct_u64"] = &addr_space::direct_mem_read<u64>;
- addr_space_type["write_direct_i8"] = &addr_space::direct_mem_write<s8>;
- addr_space_type["write_direct_u8"] = &addr_space::direct_mem_write<u8>;
- addr_space_type["write_direct_i16"] = &addr_space::direct_mem_write<s16>;
- addr_space_type["write_direct_u16"] = &addr_space::direct_mem_write<u16>;
- addr_space_type["write_direct_i32"] = &addr_space::direct_mem_write<s32>;
- addr_space_type["write_direct_u32"] = &addr_space::direct_mem_write<u32>;
- addr_space_type["write_direct_i64"] = &addr_space::direct_mem_write<s64>;
- addr_space_type["write_direct_u64"] = &addr_space::direct_mem_write<u64>;
- addr_space_type["read_range"] =
- [] (addr_space &sp, sol::this_state s, u64 first, u64 last, int width, sol::object opt_step) -> sol::object
- {
- lua_State *L = s;
- luaL_Buffer buff;
- offs_t space_size = sp.space.addrmask();
- u64 step = 1;
- if (opt_step.is<u64>())
- {
- step = opt_step.as<u64>();
- if (step < 1 || step > last - first)
- {
- luaL_error(L, "Invalid step");
- return sol::lua_nil;
- }
- }
- if (first > space_size || last > space_size || last < first)
+ addr_space_type.set_function(sol::meta_function::to_string,
+ [] (addr_space const &sp)
{
- luaL_error(L, "Invalid offset");
- return sol::lua_nil;
- }
- int byte_count = width / 8 * (last - first + 1) / step;
- switch (width)
+ device_t &d(sp.dev.device());
+ return util::string_format("%s(%s):%s", d.shortname(), d.tag(), sp.space.name());
+ });
+ addr_space_type.set_function("read_i8", &addr_space::mem_read<s8>);
+ addr_space_type.set_function("read_u8", &addr_space::mem_read<u8>);
+ addr_space_type.set_function("read_i16", &addr_space::mem_read<s16>);
+ addr_space_type.set_function("read_u16", &addr_space::mem_read<u16>);
+ addr_space_type.set_function("read_i32", &addr_space::mem_read<s32>);
+ addr_space_type.set_function("read_u32", &addr_space::mem_read<u32>);
+ addr_space_type.set_function("read_i64", &addr_space::mem_read<s64>);
+ addr_space_type.set_function("read_u64", &addr_space::mem_read<u64>);
+ addr_space_type.set_function("write_i8", &addr_space::mem_write<s8>);
+ addr_space_type.set_function("write_u8", &addr_space::mem_write<u8>);
+ addr_space_type.set_function("write_i16", &addr_space::mem_write<s16>);
+ addr_space_type.set_function("write_u16", &addr_space::mem_write<u16>);
+ addr_space_type.set_function("write_i32", &addr_space::mem_write<s32>);
+ addr_space_type.set_function("write_u32", &addr_space::mem_write<u32>);
+ addr_space_type.set_function("write_i64", &addr_space::mem_write<s64>);
+ addr_space_type.set_function("write_u64", &addr_space::mem_write<u64>);
+ addr_space_type.set_function("readv_i8", &addr_space::log_mem_read<s8>);
+ addr_space_type.set_function("readv_u8", &addr_space::log_mem_read<u8>);
+ addr_space_type.set_function("readv_i16", &addr_space::log_mem_read<s16>);
+ addr_space_type.set_function("readv_u16", &addr_space::log_mem_read<u16>);
+ addr_space_type.set_function("readv_i32", &addr_space::log_mem_read<s32>);
+ addr_space_type.set_function("readv_u32", &addr_space::log_mem_read<u32>);
+ addr_space_type.set_function("readv_i64", &addr_space::log_mem_read<s64>);
+ addr_space_type.set_function("readv_u64", &addr_space::log_mem_read<u64>);
+ addr_space_type.set_function("writev_i8", &addr_space::log_mem_write<s8>);
+ addr_space_type.set_function("writev_u8", &addr_space::log_mem_write<u8>);
+ addr_space_type.set_function("writev_i16", &addr_space::log_mem_write<s16>);
+ addr_space_type.set_function("writev_u16", &addr_space::log_mem_write<u16>);
+ addr_space_type.set_function("writev_i32", &addr_space::log_mem_write<s32>);
+ addr_space_type.set_function("writev_u32", &addr_space::log_mem_write<u32>);
+ addr_space_type.set_function("writev_i64", &addr_space::log_mem_write<s64>);
+ addr_space_type.set_function("writev_u64", &addr_space::log_mem_write<u64>);
+ addr_space_type.set_function("read_direct_i8", &addr_space::direct_mem_read<s8>);
+ addr_space_type.set_function("read_direct_u8", &addr_space::direct_mem_read<u8>);
+ addr_space_type.set_function("read_direct_i16", &addr_space::direct_mem_read<s16>);
+ addr_space_type.set_function("read_direct_u16", &addr_space::direct_mem_read<u16>);
+ addr_space_type.set_function("read_direct_i32", &addr_space::direct_mem_read<s32>);
+ addr_space_type.set_function("read_direct_u32", &addr_space::direct_mem_read<u32>);
+ addr_space_type.set_function("read_direct_i64", &addr_space::direct_mem_read<s64>);
+ addr_space_type.set_function("read_direct_u64", &addr_space::direct_mem_read<u64>);
+ addr_space_type.set_function("write_direct_i8", &addr_space::direct_mem_write<s8>);
+ addr_space_type.set_function("write_direct_u8", &addr_space::direct_mem_write<u8>);
+ addr_space_type.set_function("write_direct_i16", &addr_space::direct_mem_write<s16>);
+ addr_space_type.set_function("write_direct_u16", &addr_space::direct_mem_write<u16>);
+ addr_space_type.set_function("write_direct_i32", &addr_space::direct_mem_write<s32>);
+ addr_space_type.set_function("write_direct_u32", &addr_space::direct_mem_write<u32>);
+ addr_space_type.set_function("write_direct_i64", &addr_space::direct_mem_write<s64>);
+ addr_space_type.set_function("write_direct_u64", &addr_space::direct_mem_write<u64>);
+ addr_space_type.set_function("read_range",
+ [] (addr_space &sp, sol::this_state s, u64 first, u64 last, int width, sol::object opt_step) -> sol::object
{
- case 8:
+ u64 step = 1;
+ if (opt_step.is<u64>())
{
- u8 *dest = (u8 *)luaL_buffinitsize(L, &buff, byte_count);
- for ( ; first <= last; first += step)
- *dest++ = sp.mem_read<u8>(first);
- break;
+ step = opt_step.as<u64>();
+ if ((step < 1) || (step > last - first))
+ {
+ luaL_error(s, "Invalid step");
+ return sol::lua_nil;
+ }
}
- case 16:
- {
- u16 *dest = (u16 *)luaL_buffinitsize(L, &buff, byte_count);
- for ( ; first <= last; first += step)
- *dest++ = sp.mem_read<u16>(first);
- break;
- }
- case 32:
+
+ offs_t space_size = sp.space.addrmask();
+ if ((first > space_size) || (last > space_size) || (last < first))
{
- u32 *dest = (u32 *)luaL_buffinitsize(L, &buff, byte_count);
- for(; first <= last; first += step)
- *dest++ = sp.mem_read<u32>(first);
- break;
+ luaL_error(s, "Invalid offset");
+ return sol::lua_nil;
}
- case 64:
+
+ luaL_Buffer buff;
+ int byte_count = width / 8 * (last - first + 1) / step;
+ switch (width)
{
- u64 *dest = (u64 *)luaL_buffinitsize(L, &buff, byte_count);
- for(; first <= last; first += step)
- *dest++ = sp.mem_read<u64>(first);
- break;
+ case 8:
+ {
+ u8 *dest = (u8 *)luaL_buffinitsize(s, &buff, byte_count);
+ for ( ; first <= last; first += step)
+ *dest++ = sp.mem_read<u8>(first);
+ break;
+ }
+ case 16:
+ {
+ u16 *dest = (u16 *)luaL_buffinitsize(s, &buff, byte_count);
+ for ( ; first <= last; first += step)
+ *dest++ = sp.mem_read<u16>(first);
+ break;
+ }
+ case 32:
+ {
+ u32 *dest = (u32 *)luaL_buffinitsize(s, &buff, byte_count);
+ for( ; first <= last; first += step)
+ *dest++ = sp.mem_read<u32>(first);
+ break;
+ }
+ case 64:
+ {
+ u64 *dest = (u64 *)luaL_buffinitsize(s, &buff, byte_count);
+ for( ; first <= last; first += step)
+ *dest++ = sp.mem_read<u64>(first);
+ break;
+ }
+ default:
+ luaL_error(s, "Invalid width. Must be 8/16/32/64");
+ return sol::lua_nil;
}
- default:
- luaL_error(L, "Invalid width. Must be 8/16/32/64");
- return sol::lua_nil;
- }
- luaL_pushresultsize(&buff, byte_count);
- return sol::make_reference(L, sol::stack_reference(L, -1));
- };
+ luaL_pushresultsize(&buff, byte_count);
+ return sol::make_reference(s, sol::stack_reference(s, -1));
+ });
+ addr_space_type.set_function("add_change_notifier",
+ [this] (addr_space &sp, sol::protected_function &&cb)
+ {
+ return sp.space.add_change_notifier(
+ [this, callback = std::move(cb)] (read_or_write mode)
+ {
+ char const *modestr = "";
+ switch (mode)
+ {
+ case read_or_write::READ: modestr = "r"; break;
+ case read_or_write::WRITE: modestr = "w"; break;
+ case read_or_write::READWRITE: modestr = "rw"; break;
+ }
+ auto status = invoke(callback, modestr);
+ if (!status.valid())
+ {
+ sol::error err = status;
+ osd_printf_error("[LUA ERROR] in address space change notifier: %s\n", err.what());
+ }
+ });
+ });
+ addr_space_type.set_function("install_read_tap",
+ [this] (addr_space &sp, offs_t start, offs_t end, std::string &&name, sol::protected_function &&cb)
+ {
+ return std::make_unique<tap_helper>(*this, sp.space, read_or_write::READ, start, end, std::move(name), std::move(cb));
+ });
+ addr_space_type.set_function("install_write_tap",
+ [this] (addr_space &sp, offs_t start, offs_t end, std::string &&name, sol::protected_function &&cb)
+ {
+ return std::make_unique<tap_helper>(*this, sp.space, read_or_write::WRITE, start, end, std::move(name), std::move(cb));
+ });
addr_space_type["name"] = sol::property([] (addr_space &sp) { return sp.space.name(); });
addr_space_type["shift"] = sol::property([] (addr_space &sp) { return sp.space.addr_shift(); });
addr_space_type["index"] = sol::property([] (addr_space &sp) { return sp.space.spacenum(); });
@@ -516,6 +687,14 @@ void lua_engine::initialize_memory(sol::table &emu)
addr_space_type["map"] = sol::property([] (addr_space &sp) { return sp.space.map(); });
+ auto tap_type = sol().registry().new_usertype<tap_helper>("mempassthrough", sol::no_constructor);
+ tap_type.set_function("reinstall", &tap_helper::reinstall);
+ tap_type.set_function("remove", &tap_helper::remove);
+ tap_type["addrstart"] = sol::property(&tap_helper::start);
+ tap_type["addrend"] = sol::property(&tap_helper::end);
+ tap_type["name"] = sol::property(&tap_helper::name);
+
+
auto addrmap_type = sol().registry().new_usertype<address_map>("addrmap", sol::no_constructor);
addrmap_type["spacenum"] = sol::readonly(&address_map::m_spacenum);
addrmap_type["device"] = sol::readonly(&address_map::m_device);
@@ -557,22 +736,37 @@ void lua_engine::initialize_memory(sol::table &emu)
auto region_type = sol().registry().new_usertype<memory_region>("region", sol::no_constructor);
- region_type["read_i8"] = &region_read<s8>;
- region_type["read_u8"] = &region_read<u8>;
- region_type["read_i16"] = &region_read<s16>;
- region_type["read_u16"] = &region_read<u16>;
- region_type["read_i32"] = &region_read<s32>;
- region_type["read_u32"] = &region_read<u32>;
- region_type["read_i64"] = &region_read<s64>;
- region_type["read_u64"] = &region_read<u64>;
- region_type["write_i8"] = &region_write<s8>;
- region_type["write_u8"] = &region_write<u8>;
- region_type["write_i16"] = &region_write<s16>;
- region_type["write_u16"] = &region_write<u16>;
- region_type["write_i32"] = &region_write<s32>;
- region_type["write_u32"] = &region_write<u32>;
- region_type["write_i64"] = &region_write<s64>;
- region_type["write_u64"] = &region_write<u64>;
+ region_type.set_function(
+ "read",
+ [] (memory_region &region, sol::this_state s, offs_t offset, offs_t length)
+ {
+ // TODO: should this do something special if the offset isn't a multiple of the byte width?
+ buffer_helper buf(s);
+ const offs_t limit = std::min<offs_t>(region.bytes(), offset + length);
+ const offs_t copyable = (limit > offset) ? (limit - offset) : 0;
+ auto space = buf.prepare(copyable);
+ if (copyable)
+ std::memcpy(space.get(), &region.as_u8(offset), copyable);
+ space.add(copyable);
+ buf.push();
+ return sol::make_reference(s, sol::stack_reference(s, -1));
+ });
+ region_type.set_function("read_i8", &region_read<s8>);
+ region_type.set_function("read_u8", &region_read<u8>);
+ region_type.set_function("read_i16", &region_read<s16>);
+ region_type.set_function("read_u16", &region_read<u16>);
+ region_type.set_function("read_i32", &region_read<s32>);
+ region_type.set_function("read_u32", &region_read<u32>);
+ region_type.set_function("read_i64", &region_read<s64>);
+ region_type.set_function("read_u64", &region_read<u64>);
+ region_type.set_function("write_i8", &region_write<s8>);
+ region_type.set_function("write_u8", &region_write<u8>);
+ region_type.set_function("write_i16", &region_write<s16>);
+ region_type.set_function("write_u16", &region_write<u16>);
+ region_type.set_function("write_i32", &region_write<s32>);
+ region_type.set_function("write_u32", &region_write<u32>);
+ region_type.set_function("write_i64", &region_write<s64>);
+ region_type.set_function("write_u64", &region_write<u64>);
region_type["tag"] = sol::property(&memory_region::name);
region_type["size"] = sol::property(&memory_region::bytes);
region_type["length"] = sol::property([] (memory_region &r) { return r.bytes() / r.bytewidth(); });
@@ -582,22 +776,22 @@ void lua_engine::initialize_memory(sol::table &emu)
auto share_type = sol().registry().new_usertype<memory_share>("share", sol::no_constructor);
- share_type["read_i8"] = &share_read<s8>;
- share_type["read_u8"] = &share_read<u8>;
- share_type["read_i16"] = &share_read<s16>;
- share_type["read_u16"] = &share_read<u16>;
- share_type["read_i32"] = &share_read<s32>;
- share_type["read_u32"] = &share_read<u32>;
- share_type["read_i64"] = &share_read<s64>;
- share_type["read_u64"] = &share_read<u64>;
- share_type["write_i8"] = &share_write<s8>;
- share_type["write_u8"] = &share_write<u8>;
- share_type["write_i16"] = &share_write<s16>;
- share_type["write_u16"] = &share_write<u16>;
- share_type["write_i32"] = &share_write<s32>;
- share_type["write_u32"] = &share_write<u32>;
- share_type["write_i64"] = &share_write<s64>;
- share_type["write_u64"] = &share_write<u64>;
+ share_type.set_function("read_i8", &share_read<s8>);
+ share_type.set_function("read_u8", &share_read<u8>);
+ share_type.set_function("read_i16", &share_read<s16>);
+ share_type.set_function("read_u16", &share_read<u16>);
+ share_type.set_function("read_i32", &share_read<s32>);
+ share_type.set_function("read_u32", &share_read<u32>);
+ share_type.set_function("read_i64", &share_read<s64>);
+ share_type.set_function("read_u64", &share_read<u64>);
+ share_type.set_function("write_i8", &share_write<s8>);
+ share_type.set_function("write_u8", &share_write<u8>);
+ share_type.set_function("write_i16", &share_write<s16>);
+ share_type.set_function("write_u16", &share_write<u16>);
+ share_type.set_function("write_i32", &share_write<s32>);
+ share_type.set_function("write_u32", &share_write<u32>);
+ share_type.set_function("write_i64", &share_write<s64>);
+ share_type.set_function("write_u64", &share_write<u64>);
share_type["tag"] = sol::property(&memory_share::name);
share_type["size"] = sol::property(&memory_share::bytes);
share_type["length"] = sol::property([] (memory_share &s) { return s.bytes() / s.bytewidth(); });
diff --git a/src/frontend/mame/luaengine_render.cpp b/src/frontend/mame/luaengine_render.cpp
index 21a68dc65cb..8f931f91dac 100644
--- a/src/frontend/mame/luaengine_render.cpp
+++ b/src/frontend/mame/luaengine_render.cpp
@@ -16,12 +16,104 @@
#include "render.h"
#include "rendlay.h"
+#include "rendutil.h"
+#include "interface/uievents.h"
+#include "ioprocs.h"
+
+#include <algorithm>
+#include <atomic>
#include <iterator>
namespace {
+class render_texture_helper
+{
+public:
+ render_texture_helper(render_texture_helper const &) = delete;
+
+ render_texture_helper(render_texture_helper &&that)
+ : texture(that.texture)
+ , bitmap(that.bitmap)
+ , manager(that.manager)
+ , storage_lock_count(that.storage_lock_count)
+ , palette_lock_count(that.palette_lock_count)
+ {
+ that.texture = nullptr;
+ that.bitmap.reset();
+ }
+
+ template <typename T>
+ render_texture_helper(sol::this_state s, render_manager &m, std::shared_ptr<T> const &b, texture_format f)
+ : texture(nullptr)
+ , bitmap(b)
+ , manager(m)
+ , storage_lock_count(b->storage_lock_count)
+ , palette_lock_count(b->palette_lock_count)
+ {
+ if (bitmap)
+ {
+ texture = manager.texture_alloc();
+ if (texture)
+ {
+ ++storage_lock_count;
+ ++palette_lock_count;
+ texture->set_bitmap(*bitmap, bitmap->cliprect(), f);
+ }
+ else
+ {
+ luaL_error(s, "Error allocating texture");
+ }
+ }
+ }
+
+ ~render_texture_helper()
+ {
+ free();
+ }
+
+ bool valid() const
+ {
+ return texture && bitmap;
+ }
+
+ void free()
+ {
+ if (texture)
+ {
+ manager.texture_free(texture);
+ texture = nullptr;
+ }
+ if (bitmap)
+ {
+ assert(storage_lock_count);
+ assert(palette_lock_count);
+ --storage_lock_count;
+ --palette_lock_count;
+ bitmap.reset();
+ }
+ }
+
+ render_texture *texture;
+ std::shared_ptr<bitmap_t> bitmap;
+
+private:
+ render_manager &manager;
+ std::atomic<unsigned> &storage_lock_count;
+ std::atomic<unsigned> &palette_lock_count;
+};
+
+
+struct layout_file_elements
+{
+ layout_file_elements(layout_file &f) : file(f) { }
+ layout_file::element_map &items() { return file.elements(); }
+
+ layout_file &file;
+};
+
+
struct layout_file_views
{
layout_file_views(layout_file &f) : file(f) { }
@@ -54,17 +146,161 @@ struct render_target_view_names
int count;
};
+
+template <typename T>
+auto get_bitmap_pixels(T const &bitmap, sol::this_state s, rectangle const &bounds)
+{
+ if (!bitmap.cliprect().contains(bounds))
+ luaL_error(s, "Bounds exceed source clipping rectangle");
+ luaL_Buffer buff;
+ size_t const size(bounds.width() * bounds.height() * sizeof(typename T::pixel_t));
+ auto ptr = reinterpret_cast<typename T::pixel_t *>(luaL_buffinitsize(s, &buff, size));
+ for (auto y = bounds.top(); bounds.bottom() >= y; ++y, ptr += bounds.width())
+ std::copy_n(&bitmap.pix(y, bounds.left()), bounds.width(), ptr);
+ luaL_pushresultsize(&buff, size);
+ return std::make_tuple(sol::make_reference(s, sol::stack_reference(s, -1)), bounds.width(), bounds.height());
+}
+
+
+template <typename T>
+auto make_bitmap_specific_type(sol::table registry, char const *name)
+{
+ auto result = registry.new_usertype<T>(
+ name,
+ sol::no_constructor,
+ sol::base_classes, sol::bases<bitmap_t>());
+ result.set_function("pix", [] (T &bitmap, int32_t x, int32_t y) { return bitmap.pix(y, x); });
+ result["pixels"] = sol::overload(
+ [] (T const &bitmap, sol::this_state s)
+ {
+ return get_bitmap_pixels(bitmap, s, bitmap.cliprect());
+ },
+ [] (T const &bitmap, sol::this_state s, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ return get_bitmap_pixels(bitmap, s, rectangle(minx, maxx, miny, maxy));
+ });
+ result["fill"] = sol::overload(
+ static_cast<void (T::*)(typename T::pixel_t)>(&T::fill),
+ [] (T &bitmap, typename T::pixel_t color, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ bitmap.fill(color, rectangle(minx, maxx, miny, maxy));
+ });
+ result.set_function(
+ "plot",
+ [] (T &bitmap, int32_t x, int32_t y, typename T::pixel_t color)
+ {
+ if (bitmap.cliprect().contains(x, y))
+ bitmap.pix(y, x) = color;
+ });
+ result.set_function("plot_box", &T::plot_box);
+ result["bpp"] = sol::property(&T::bpp);
+ return result;
+}
+
} // anonymous namespace
namespace sol {
+template <> struct is_container<layout_file_elements> : std::true_type { };
template <> struct is_container<layout_file_views> : std::true_type { };
template <> struct is_container<layout_view_items> : std::true_type { };
template <> struct is_container<render_target_view_names> : std::true_type { };
template <>
+struct usertype_container<layout_file_elements> : lua_engine::immutable_collection_helper<layout_file_elements, layout_file::element_map>
+{
+private:
+ template <bool Indexed>
+ static int next_pairs(lua_State *L)
+ {
+ usertype_container::indexed_iterator &i(stack::unqualified_get<user<usertype_container::indexed_iterator> >(L, 1));
+ if (i.src.end() == i.it)
+ return stack::push(L, lua_nil);
+ int result;
+ if constexpr (Indexed)
+ result = stack::push(L, i.ix + 1);
+ else
+ result = stack::push(L, i.it->first);
+ result += stack::push_reference(L, i.it->second);
+ ++i;
+ return result;
+ }
+
+ template <bool Indexed>
+ static int start_pairs(lua_State *L)
+ {
+ layout_file_elements &self(usertype_container::get_self(L));
+ stack::push(L, next_pairs<Indexed>);
+ stack::push<user<usertype_container::indexed_iterator> >(L, self.items(), self.items().begin());
+ stack::push(L, lua_nil);
+ return 3;
+ }
+
+public:
+ static int at(lua_State *L)
+ {
+ layout_file_elements &self(usertype_container::get_self(L));
+ std::ptrdiff_t const index(stack::unqualified_get<std::ptrdiff_t>(L, 2));
+ if ((0 >= index) || (self.items().size() < index))
+ return stack::push(L, lua_nil);
+ auto const found(std::next(self.items().begin(), index - 1));
+ return stack::push_reference(L, found->second);
+ }
+
+ static int get(lua_State *L)
+ {
+ layout_file_elements &self(usertype_container::get_self(L));
+ char const *const tag(stack::unqualified_get<char const *>(L));
+ auto const found(self.items().find(tag));
+ if (self.items().end() == found)
+ return stack::push(L, lua_nil);
+ else
+ return stack::push_reference(L, found->second);
+ }
+
+ static int index_get(lua_State *L)
+ {
+ return get(L);
+ }
+
+ static int index_of(lua_State *L)
+ {
+ layout_file_elements &self(usertype_container::get_self(L));
+ auto &obj(stack::unqualified_get<layout_element>(L, 2));
+ auto it(self.items().begin());
+ std::ptrdiff_t ix(0);
+ while ((self.items().end() != it) && (&it->second != &obj))
+ {
+ ++it;
+ ++ix;
+ }
+ if (self.items().end() == it)
+ return stack::push(L, lua_nil);
+ else
+ return stack::push(L, ix + 1);
+ }
+
+ static int size(lua_State *L)
+ {
+ layout_file_elements &self(usertype_container::get_self(L));
+ return stack::push(L, self.items().size());
+ }
+
+ static int empty(lua_State *L)
+ {
+ layout_file_elements &self(usertype_container::get_self(L));
+ return stack::push(L, self.items().empty());
+ }
+
+ static int next(lua_State *L) { return stack::push(L, next_pairs<false>); }
+ static int pairs(lua_State *L) { return start_pairs<false>(L); }
+ static int ipairs(lua_State *L) { return start_pairs<true>(L); }
+};
+
+
+template <>
struct usertype_container<layout_file_views> : lua_engine::immutable_sequence_helper<layout_file_views, layout_file::view_list>
{
public:
@@ -215,6 +451,258 @@ public:
//-------------------------------------------------
+// sol_lua_push - automatically convert
+// osd::ui_event_handler::pointer to a string
+//-------------------------------------------------
+
+int sol_lua_push(sol::types<osd::ui_event_handler::pointer>, lua_State *L, osd::ui_event_handler::pointer &&value)
+{
+ const char *typestr = "invalid";
+ switch (value)
+ {
+ case osd::ui_event_handler::pointer::UNKNOWN:
+ typestr = "unknown";
+ break;
+ case osd::ui_event_handler::pointer::MOUSE:
+ typestr = "mouse";
+ break;
+ case osd::ui_event_handler::pointer::PEN:
+ typestr = "pen";
+ break;
+ case osd::ui_event_handler::pointer::TOUCH:
+ typestr = "touch";
+ break;
+ }
+ return sol::stack::push(L, typestr);
+}
+
+
+template <typename T>
+class lua_engine::bitmap_helper : public T
+{
+public:
+ using ptr = std::shared_ptr<bitmap_helper>;
+
+ bitmap_helper(bitmap_helper const &) = delete;
+ bitmap_helper(bitmap_helper &&) = delete;
+ bitmap_helper &operator=(bitmap_helper const &) = delete;
+ bitmap_helper &operator=(bitmap_helper &&) = delete;
+
+ bitmap_helper(sol::this_state s, int width, int height, int xslop, int yslop)
+ : T(width, height, xslop, yslop)
+ , storage_lock_count(0)
+ , palette_lock_count(0)
+ , storage()
+ {
+ if ((0 < width) && (0 < height) && !this->valid())
+ luaL_error(s, "Error allocating bitmap storage");
+ }
+
+ bitmap_helper(ptr const &source, rectangle const &subrect)
+ : T(*source, subrect)
+ , storage_lock_count(0)
+ , palette_lock_count(0)
+ , storage(source->storage ? source->storage : source)
+ {
+ ++storage->storage_lock_count;
+ this->set_palette(source->palette());
+ }
+
+ ~bitmap_helper()
+ {
+ assert(!storage_lock_count);
+ assert(!palette_lock_count);
+ release_storage();
+ }
+
+ void reset(sol::this_state s)
+ {
+ if (storage_lock_count)
+ luaL_error(s, "Cannot reset bitmap while in use");
+ palette_t *const p(this->palette());
+ if (p)
+ p->ref();
+ T::reset();
+ this->set_palette(p);
+ if (p)
+ p->deref();
+ release_storage();
+ }
+
+ void allocate(sol::this_state s, int width, int height, int xslop, int yslop)
+ {
+ if (storage_lock_count)
+ luaL_error(s, "Cannot reallocate bitmap while in use");
+ palette_t *const p(this->palette());
+ if (p)
+ p->ref();
+ T::allocate(width, height, xslop, yslop);
+ this->set_palette(p);
+ if (p)
+ p->deref();
+ release_storage();
+ if ((0 < width) && (0 < height) && !this->valid())
+ luaL_error(s, "Error allocating bitmap storage");
+ }
+
+ void resize(sol::this_state s, int width, int height, int xslop, int yslop)
+ {
+ if (storage_lock_count)
+ luaL_error(s, "Cannot resize bitmap while in use");
+ T::resize(width, height, xslop, yslop);
+ release_storage();
+ if ((0 < width) && (0 < height) && !this->valid())
+ luaL_error(s, "Error allocating bitmap storage");
+ }
+
+ void wrap(sol::this_state s, ptr const &source, rectangle const &subrect)
+ {
+ if (source.get() == this)
+ luaL_error(s, "Bitmap cannot wrap itself");
+ if (storage_lock_count)
+ luaL_error(s, "Cannot free bitmap storage while in use");
+ if (!source->cliprect().contains(subrect))
+ luaL_error(s, "Bounds exceed source clipping rectangle");
+ palette_t *const p(this->palette());
+ if (p)
+ p->ref();
+ T::wrap(*source, subrect);
+ this->set_palette(p);
+ if (p)
+ p->deref();
+ release_storage();
+ storage = source->storage ? source->storage : source;
+ ++storage->storage_lock_count;
+ }
+
+ std::atomic<unsigned> storage_lock_count;
+ std::atomic<unsigned> palette_lock_count;
+
+ template <typename B>
+ static auto make_type(sol::table &registry, char const *name)
+ {
+ auto result = registry.new_usertype<bitmap_helper>(
+ name,
+ sol::call_constructor, sol::factories(
+ [] (sol::this_state s)
+ {
+ return std::make_shared<bitmap_helper>(s, 0, 0, 0, 0);
+ },
+ [] (sol::this_state s, int width, int height)
+ {
+ return std::make_shared<bitmap_helper>(s, width, height, 0, 0);
+ },
+ [] (sol::this_state s, int width, int height, int xslop, int yslop)
+ {
+ return std::make_shared<bitmap_helper>(s, width, height, xslop, yslop);
+ },
+ [] (ptr const &source)
+ {
+ return std::make_shared<bitmap_helper>(source, source->cliprect());
+ },
+ [] (sol::this_state s, ptr const &source, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ rectangle const subrect(minx, maxx, miny, maxy);
+ if (!source->cliprect().contains(subrect))
+ luaL_error(s, "Bounds exceed source clipping rectangle");
+ return std::make_shared<bitmap_helper>(source, subrect);
+ }),
+ sol::base_classes, sol::bases<T, B, bitmap_t>());
+ add_bitmap_members(result);
+ return result;
+ }
+
+ template <typename B>
+ static auto make_indexed_type(sol::table &registry, char const *name)
+ {
+ auto result = registry.new_usertype<bitmap_helper>(
+ name,
+ sol::call_constructor, sol::factories(
+ [] (sol::this_state s, palette_wrapper &p)
+ {
+ ptr result = std::make_shared<bitmap_helper>(s, 0, 0, 0, 0);
+ result->set_palette(&p.palette());
+ return result;
+ },
+ [] (sol::this_state s, palette_wrapper &p, int width, int height)
+ {
+ ptr result = std::make_shared<bitmap_helper>(s, width, height, 0, 0);
+ result->set_palette(&p.palette());
+ return result;
+ },
+ [] (sol::this_state s, palette_wrapper &p, int width, int height, int xslop, int yslop)
+ {
+ ptr result = std::make_shared<bitmap_helper>(s, width, height, xslop, yslop);
+ result->set_palette(&p.palette());
+ return result;
+ },
+ [] (ptr const &source)
+ {
+ return std::make_shared<bitmap_helper>(source, source->cliprect());
+ },
+ [] (sol::this_state s, ptr const &source, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ rectangle const subrect(minx, maxx, miny, maxy);
+ if (!source->cliprect().contains(subrect))
+ luaL_error(s, "Bounds exceed source clipping rectangle");
+ return std::make_shared<bitmap_helper>(source, subrect);
+ }),
+ sol::base_classes, sol::bases<B, bitmap_t>());
+ result["palette"] = sol::property(
+ [] (bitmap_helper const &b)
+ {
+ return b.palette()
+ ? std::optional<palette_wrapper>(std::in_place, *b.palette())
+ : std::optional<palette_wrapper>();
+ },
+ [] (bitmap_helper &b, sol::this_state s, palette_wrapper &p)
+ {
+ if (b.palette_lock_count)
+ luaL_error(s, "Cannot set palette while in use");
+ b.set_palette(&p.palette());
+ });
+ add_bitmap_members(result);
+ return result;
+ }
+
+private:
+ void release_storage()
+ {
+ if (storage)
+ {
+ assert(storage->storage_lock_count);
+ --storage->storage_lock_count;
+ storage.reset();
+ }
+ }
+
+ template <typename U>
+ static void add_bitmap_members(U &type)
+ {
+ type.set_function("reset", &bitmap_helper::reset);
+ type["allocate"] = sol::overload(
+ &bitmap_helper::allocate,
+ [] (bitmap_helper &bitmap, sol::this_state s, int width, int height) { bitmap.allocate(s, width, height, 0, 0); });
+ type["resize"] = sol::overload(
+ &bitmap_helper::resize,
+ [] (bitmap_helper &bitmap, sol::this_state s, int width, int height) { bitmap.resize(s, width, height, 0, 0); });
+ type["wrap"] = sol::overload(
+ [] (bitmap_helper &bitmap, sol::this_state s, ptr const &source)
+ {
+ bitmap.wrap(s, source, source->cliprect());
+ },
+ [] (bitmap_helper &bitmap, sol::this_state s, ptr const &source, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ bitmap.wrap(s, source, rectangle(minx, maxx, miny, maxy));
+ });
+ type["locked"] = sol::property([] (bitmap_helper const &b) { return bool(b.storage_lock_count); });
+ }
+
+ ptr storage;
+};
+
+
+//-------------------------------------------------
// initialize_render - register render user types
//-------------------------------------------------
@@ -226,9 +714,9 @@ void lua_engine::initialize_render(sol::table &emu)
sol::call_constructor, sol::initializers(
[] (render_bounds &b) { new (&b) render_bounds{ 0.0F, 0.0F, 1.0F, 1.0F }; },
[] (render_bounds &b, float x0, float y0, float x1, float y1) { new (&b) render_bounds{ x0, y0, x1, y1 }; }));
- bounds_type["includes"] = &render_bounds::includes;
- bounds_type["set_xy"] = &render_bounds::set_xy;
- bounds_type["set_wh"] = &render_bounds::set_wh;
+ bounds_type.set_function("includes", &render_bounds::includes);
+ bounds_type.set_function("set_xy", &render_bounds::set_xy);
+ bounds_type.set_function("set_wh", &render_bounds::set_wh);
bounds_type["x0"] = &render_bounds::x0;
bounds_type["y0"] = &render_bounds::y0;
bounds_type["x1"] = &render_bounds::x1;
@@ -243,33 +731,262 @@ void lua_engine::initialize_render(sol::table &emu)
sol::call_constructor, sol::initializers(
[] (render_color &c) { new (&c) render_color{ 1.0F, 1.0F, 1.0F, 1.0F }; },
[] (render_color &c, float a, float r, float g, float b) { new (&c) render_color{ a, r, g, b }; }));
- color_type["set"] = &render_color::set;
+ color_type.set_function("set", &render_color::set);
color_type["a"] = &render_color::a;
color_type["r"] = &render_color::r;
color_type["g"] = &render_color::g;
color_type["b"] = &render_color::b;
+ auto palette_type = emu.new_usertype<palette_wrapper>(
+ "palette",
+ sol::call_constructor, sol::initializers(
+ [] (palette_wrapper &pal, uint32_t colors) { new (&pal) palette_wrapper(colors, 1); },
+ [] (palette_wrapper &pal, uint64_t colors, uint32_t groups) { new (&pal) palette_wrapper(colors, groups); }));
+ palette_type.set_function(
+ "entry_color",
+ [] (palette_wrapper const &pal, uint32_t index) { return uint32_t(pal.palette().entry_color(index)); });
+ palette_type.set_function(
+ "entry_contrast",
+ [] (palette_wrapper const &pal, uint32_t index) { return pal.palette().entry_contrast(index); });
+ palette_type.set_function(
+ "entry_adjusted_color",
+ [] (palette_wrapper const &pal, uint32_t index, std::optional<uint32_t> group)
+ {
+ if (group)
+ {
+ if ((pal.palette().num_colors() <= index) || (pal.palette().num_groups() <= *group))
+ return uint32_t(rgb_t::black());
+ index += *group * pal.palette().num_colors();
+ }
+ return uint32_t(pal.palette().entry_adjusted_color(index));
+ });
+ palette_type["entry_set_color"] = sol::overload(
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t index, uint32_t color)
+ {
+ if (pal.palette().num_colors() <= index)
+ luaL_error(s, "Color index out of range");
+ pal.palette().entry_set_color(index, rgb_t(color));
+ },
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t index, uint8_t red, uint8_t green, uint8_t blue)
+ {
+ if (pal.palette().num_colors() <= index)
+ luaL_error(s, "Color index out of range");
+ pal.palette().entry_set_color(index, rgb_t(red, green, blue));
+ });
+ palette_type.set_function(
+ "entry_set_red_level",
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t index, uint8_t level)
+ {
+ if (pal.palette().num_colors() <= index)
+ luaL_error(s, "Color index out of range");
+ pal.palette().entry_set_red_level(index, level);
+ });
+ palette_type.set_function(
+ "entry_set_green_level",
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t index, uint8_t level)
+ {
+ if (pal.palette().num_colors() <= index)
+ luaL_error(s, "Color index out of range");
+ pal.palette().entry_set_green_level(index, level);
+ });
+ palette_type.set_function(
+ "entry_set_blue_level",
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t index, uint8_t level)
+ {
+ if (pal.palette().num_colors() <= index)
+ luaL_error(s, "Color index out of range");
+ pal.palette().entry_set_blue_level(index, level);
+ });
+ palette_type.set_function(
+ "entry_set_contrast",
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t index, float contrast)
+ {
+ if (pal.palette().num_colors() <= index)
+ luaL_error(s, "Color index out of range");
+ pal.palette().entry_set_contrast(index, contrast);
+ });
+ palette_type.set_function(
+ "group_set_brightness",
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t group, float brightness)
+ {
+ if (pal.palette().num_colors() <= group)
+ luaL_error(s, "Group index out of range");
+ pal.palette().group_set_brightness(group, brightness);
+ });
+ palette_type.set_function(
+ "group_set_contrast",
+ [] (palette_wrapper &pal, sol::this_state s, uint32_t group, float contrast)
+ {
+ if (pal.palette().num_colors() <= group)
+ luaL_error(s, "Group index out of range");
+ pal.palette().group_set_contrast(group, contrast);
+ });
+ palette_type["colors"] = sol::property([] (palette_wrapper const &pal) { return pal.palette().num_colors(); });
+ palette_type["groups"] = sol::property([] (palette_wrapper const &pal) { return pal.palette().num_groups(); });
+ palette_type["max_index"] = sol::property([] (palette_wrapper const &pal) { return pal.palette().max_index(); });
+ palette_type["black_entry"] = sol::property([] (palette_wrapper const &pal) { return pal.palette().black_entry(); });
+ palette_type["white_entry"] = sol::property([] (palette_wrapper const &pal) { return pal.palette().white_entry(); });
+ palette_type["brightness"] = sol::property([] (palette_wrapper &pal, float brightness) { pal.palette().set_brightness(brightness); });
+ palette_type["contrast"] = sol::property([] (palette_wrapper &pal, float contrast) { pal.palette().set_contrast(contrast); });
+ palette_type["gamma"] = sol::property([] (palette_wrapper &pal, float gamma) { pal.palette().set_gamma(gamma); });
+
+
+ auto bitmap_type = sol().registry().new_usertype<bitmap_t>("bitmap", sol::no_constructor);
+ bitmap_type.set_function(
+ "cliprect",
+ [] (bitmap_t const &bitmap)
+ {
+ rectangle const &result(bitmap.cliprect());
+ return std::make_tuple(result.left(), result.top(), result.right(), result.bottom());
+ });
+ bitmap_type["fill"] = sol::overload(
+ static_cast<void (bitmap_t::*)(uint64_t)>(&bitmap_t::fill),
+ [] (bitmap_t &bitmap, uint64_t color, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy)
+ {
+ bitmap.fill(color, rectangle(minx, maxx, miny, maxy));
+ });
+ bitmap_type.set_function("plot_box", &bitmap_t::plot_box);
+ bitmap_type["width"] = sol::property(&bitmap_t::width);
+ bitmap_type["height"] = sol::property(&bitmap_t::height);
+ bitmap_type["rowpixels"] = sol::property(&bitmap_t::rowpixels);
+ bitmap_type["rowbytes"] = sol::property(&bitmap_t::rowbytes);
+ bitmap_type["bpp"] = sol::property(&bitmap_t::bpp);
+ bitmap_type["valid"] = sol::property(&bitmap_t::valid);
+
+ make_bitmap_specific_type<bitmap8_t>(sol().registry(), "bitmap8");
+ make_bitmap_specific_type<bitmap16_t>(sol().registry(), "bitmap16");
+ make_bitmap_specific_type<bitmap32_t>(sol().registry(), "bitmap32");
+ make_bitmap_specific_type<bitmap64_t>(sol().registry(), "bitmap64");
+
+ bitmap_helper<bitmap_ind8>::make_indexed_type<bitmap8_t>(emu, "bitmap_ind8");
+ bitmap_helper<bitmap_ind16>::make_indexed_type<bitmap16_t>(emu, "bitmap_ind16");
+ bitmap_helper<bitmap_ind32>::make_indexed_type<bitmap32_t>(emu, "bitmap_ind32");
+ bitmap_helper<bitmap_ind64>::make_indexed_type<bitmap64_t>(emu, "bitmap_ind64");
+
+ bitmap_helper<bitmap_yuy16>::make_type<bitmap16_t>(emu, "bitmap_yuy16");
+ bitmap_helper<bitmap_rgb32>::make_type<bitmap32_t>(emu, "bitmap_rgb32");
+
+ // ARGB32 bitmaps get extra functionality
+ auto bitmap_argb32_type = emu.new_usertype<bitmap_argb32>(
+ "bitmap_argb32_t",
+ sol::no_constructor,
+ sol::base_classes, sol::bases<bitmap32_t, bitmap_t>());
+ bitmap_argb32_type.set_function(
+ "resample",
+ [] (bitmap_argb32 &bitmap, bitmap_argb32 &dest, std::optional<render_color> color)
+ {
+ render_resample_argb_bitmap_hq(dest, bitmap, color ? *color : render_color{ 1.0F, 1.0F, 1.0F, 1.0F });
+ });
+
+
+ auto bitmap_argb32_helper_type = bitmap_helper<bitmap_argb32>::make_type<bitmap32_t>(emu, "bitmap_argb32");
+ bitmap_argb32_helper_type.set_function(
+ "load",
+ [] (sol::this_state s, std::string_view data)
+ {
+ auto stream(util::ram_read(data.data(), data.size()));
+ if (!stream)
+ luaL_error(s, "Error allocating stream wrapper");
+ auto b = std::make_shared<bitmap_helper<bitmap_argb32> >(s, 0, 0, 0, 0);
+ switch (render_detect_image(*stream))
+ {
+ case RENDUTIL_IMGFORMAT_PNG:
+ render_load_png(*b, *stream);
+ if (!b->valid())
+ luaL_error(s, "Invalid or unsupported PNG data");
+ break;
+ case RENDUTIL_IMGFORMAT_JPEG:
+ render_load_jpeg(*b, *stream);
+ if (!b->valid())
+ luaL_error(s, "Invalid or unsupported PNG data");
+ break;
+ case RENDUTIL_IMGFORMAT_MSDIB:
+ render_load_msdib(*b, *stream);
+ if (!b->valid())
+ luaL_error(s, "Invalid or unsupported Microsoft DIB data");
+ break;
+ default:
+ luaL_error(s, "Unsupported bitmap data format");
+ }
+ return b;
+ });
+
+ auto render_texture_type = emu.new_usertype<render_texture_helper>("render_texture", sol::no_constructor);
+ render_texture_type.set_function("free", &render_texture_helper::free);
+ render_texture_type["valid"] = sol::property(&render_texture_helper::valid);
+
+
+ auto layout_element_type = sol().registry().new_usertype<layout_element>("layout_element", sol::no_constructor);
+ layout_element_type.set_function("invalidate", &layout_element::invalidate);
+ layout_element_type.set_function(
+ "set_draw_callback",
+ make_simple_callback_setter(
+ &layout_element::set_draw_callback,
+ nullptr,
+ "set_draw_callback",
+ "draw"));
+ layout_element_type["default_state"] = sol::property(
+ [] (layout_element const &e) -> std::optional<int>
+ {
+ if (0 <= e.default_state())
+ return e.default_state();
+ else
+ return std::nullopt;
+ });
+
+
auto layout_view_type = sol().registry().new_usertype<layout_view>("layout_view", sol::no_constructor);
- layout_view_type["has_screen"] = &layout_view::has_screen;
- layout_view_type["set_prepare_items_callback"] =
- make_simple_callback_setter<void>(
- &layout_view::set_prepare_items_callback,
- nullptr,
- "set_prepare_items_callback",
- nullptr);
- layout_view_type["set_preload_callback"] =
- make_simple_callback_setter<void>(
- &layout_view::set_preload_callback,
- nullptr,
- "set_preload_callback",
- nullptr);
- layout_view_type["set_recomputed_callback"] =
- make_simple_callback_setter<void>(
- &layout_view::set_recomputed_callback,
- nullptr,
- "set_recomputed_callback",
- nullptr);
+ layout_view_type.set_function("has_screen", &layout_view::has_screen);
+ layout_view_type.set_function(
+ "set_prepare_items_callback",
+ make_simple_callback_setter(
+ &layout_view::set_prepare_items_callback,
+ nullptr,
+ "set_prepare_items_callback",
+ "prepare items"));
+ layout_view_type.set_function(
+ "set_preload_callback",
+ make_simple_callback_setter(
+ &layout_view::set_preload_callback,
+ nullptr,
+ "set_preload_callback",
+ "preload"));
+ layout_view_type.set_function(
+ "set_recomputed_callback",
+ make_simple_callback_setter(
+ &layout_view::set_recomputed_callback,
+ nullptr,
+ "set_recomputed_callback",
+ "recomputed"));
+ layout_view_type.set_function(
+ "set_pointer_updated_callback",
+ make_simple_callback_setter(
+ &layout_view::set_pointer_updated_callback,
+ nullptr,
+ "set_pointer_updated_callback",
+ "pointer updated"));
+ layout_view_type.set_function(
+ "set_pointer_left_callback",
+ make_simple_callback_setter(
+ &layout_view::set_pointer_left_callback,
+ nullptr,
+ "set_pointer_left_callback",
+ "pointer left"));
+ layout_view_type.set_function(
+ "set_pointer_aborted_callback",
+ make_simple_callback_setter(
+ &layout_view::set_pointer_aborted_callback,
+ nullptr,
+ "set_pointer_aborted_callback",
+ "pointer aborted"));
+ layout_view_type.set_function(
+ "set_forget_pointers_callback",
+ make_simple_callback_setter(
+ &layout_view::set_forget_pointers_callback,
+ nullptr,
+ "set_forget_pointers_callback",
+ "forget pointers"));
layout_view_type["items"] = sol::property([] (layout_view &v) { return layout_view_items(v); });
layout_view_type["name"] = sol::property(&layout_view::name);
layout_view_type["unqualified_name"] = sol::property(&layout_view::unqualified_name);
@@ -277,58 +994,68 @@ void lua_engine::initialize_render(sol::table &emu)
layout_view_type["effective_aspect"] = sol::property(&layout_view::effective_aspect);
layout_view_type["bounds"] = sol::property(&layout_view::bounds);
layout_view_type["has_art"] = sol::property(&layout_view::has_art);
+ layout_view_type["show_pointers"] = sol::property(&layout_view::show_pointers, &layout_view::set_show_pointers);
+ layout_view_type["hide_inactive_pointers"] = sol::property(&layout_view::hide_inactive_pointers, &layout_view::set_hide_inactive_pointers);
auto layout_view_item_type = sol().registry().new_usertype<layout_view_item>("layout_item", sol::no_constructor);
- layout_view_item_type["set_state"] = &layout_view_item::set_state;
- layout_view_item_type["set_element_state_callback"] =
- make_simple_callback_setter<int>(
- &layout_view_item::set_element_state_callback,
- [] () { return 0; },
- "set_element_state_callback",
- "element state");
- layout_view_item_type["set_animation_state_callback"] =
- make_simple_callback_setter<int>(
- &layout_view_item::set_animation_state_callback,
- [] () { return 0; },
- "set_animation_state_callback",
- "animation state");
- layout_view_item_type["set_bounds_callback"] =
- make_simple_callback_setter<render_bounds>(
- &layout_view_item::set_bounds_callback,
- [] () { return render_bounds{ 0.0f, 0.0f, 1.0f, 1.0f }; },
- "set_bounds_callback",
- "bounds");
- layout_view_item_type["set_color_callback"] =
- make_simple_callback_setter<render_color>(
- &layout_view_item::set_color_callback,
- [] () { return render_color{ 1.0f, 1.0f, 1.0f, 1.0f }; },
- "set_color_callback",
- "color");
- layout_view_item_type["set_scroll_size_x_callback"] =
- make_simple_callback_setter<float>(
- &layout_view_item::set_scroll_size_x_callback,
- [] () { return 1.0f; },
- "set_scroll_size_x_callback",
- "horizontal scroll window size");
- layout_view_item_type["set_scroll_size_y_callback"] =
- make_simple_callback_setter<float>(
- &layout_view_item::set_scroll_size_y_callback,
- [] () { return 1.0f; },
- "set_scroll_size_y_callback",
- "vertical scroll window size");
- layout_view_item_type["set_scroll_pos_x_callback"] =
- make_simple_callback_setter<float>(
- &layout_view_item::set_scroll_pos_x_callback,
- [] () { return 1.0f; },
- "set_scroll_pos_x_callback",
- "horizontal scroll position");
- layout_view_item_type["set_scroll_pos_y_callback"] =
- make_simple_callback_setter<float>(
- &layout_view_item::set_scroll_pos_y_callback,
- [] () { return 1.0f; },
- "set_scroll_pos_y_callback",
- "vertical scroll position");
+ layout_view_item_type.set_function("set_state", &layout_view_item::set_state);
+ layout_view_item_type.set_function(
+ "set_element_state_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_element_state_callback,
+ [] () { return 0; },
+ "set_element_state_callback",
+ "element state"));
+ layout_view_item_type.set_function(
+ "set_animation_state_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_animation_state_callback,
+ [] () { return 0; },
+ "set_animation_state_callback",
+ "animation state"));
+ layout_view_item_type.set_function(
+ "set_bounds_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_bounds_callback,
+ [] () { return render_bounds{ 0.0f, 0.0f, 1.0f, 1.0f }; },
+ "set_bounds_callback",
+ "bounds"));
+ layout_view_item_type.set_function(
+ "set_color_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_color_callback,
+ [] () { return render_color{ 1.0f, 1.0f, 1.0f, 1.0f }; },
+ "set_color_callback",
+ "color"));
+ layout_view_item_type.set_function(
+ "set_scroll_size_x_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_scroll_size_x_callback,
+ [] () { return 1.0f; },
+ "set_scroll_size_x_callback",
+ "horizontal scroll window size"));
+ layout_view_item_type.set_function(
+ "set_scroll_size_y_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_scroll_size_y_callback,
+ [] () { return 1.0f; },
+ "set_scroll_size_y_callback",
+ "vertical scroll window size"));
+ layout_view_item_type.set_function(
+ "set_scroll_pos_x_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_scroll_pos_x_callback,
+ [] () { return 1.0f; },
+ "set_scroll_pos_x_callback",
+ "horizontal scroll position"));
+ layout_view_item_type.set_function(
+ "set_scroll_pos_y_callback",
+ make_simple_callback_setter(
+ &layout_view_item::set_scroll_pos_y_callback,
+ [] () { return 1.0f; },
+ "set_scroll_pos_y_callback",
+ "vertical scroll position"));
layout_view_item_type["id"] = sol::property(
[] (layout_view_item &i, sol::this_state s) -> sol::object
{
@@ -337,6 +1064,7 @@ void lua_engine::initialize_render(sol::table &emu)
else
return sol::make_object(s, i.id());
});
+ layout_view_item_type["element"] = sol::property(&layout_view_item::element);
layout_view_item_type["bounds_animated"] = sol::property(&layout_view_item::bounds_animated);
layout_view_item_type["color_animated"] = sol::property(&layout_view_item::color_animated);
layout_view_item_type["bounds"] = sol::property(&layout_view_item::bounds);
@@ -363,16 +1091,18 @@ void lua_engine::initialize_render(sol::table &emu)
auto layout_file_type = sol().registry().new_usertype<layout_file>("layout_file", sol::no_constructor);
layout_file_type["set_resolve_tags_callback"] =
- make_simple_callback_setter<void>(
+ make_simple_callback_setter(
&layout_file::set_resolve_tags_callback,
nullptr,
"set_resolve_tags_callback",
- nullptr);
+ "resolve tags");
layout_file_type["device"] = sol::property(&layout_file::device);
+ layout_file_type["elements"] = sol::property([] (layout_file &f) { return layout_file_elements(f); });
layout_file_type["views"] = sol::property([] (layout_file &f) { return layout_file_views(f); });
auto target_type = sol().registry().new_usertype<render_target>("target", sol::no_constructor);
+ target_type["ui_container"] = sol::property(&render_target::ui_container);
target_type["index"] = sol::property([] (render_target const &t) { return t.index() + 1; });
target_type["width"] = sol::property(&render_target::width);
target_type["height"] = sol::property(&render_target::height);
@@ -392,7 +1122,8 @@ void lua_engine::initialize_render(sol::table &emu)
auto render_container_type = sol().registry().new_usertype<render_container>("render_container", sol::no_constructor);
- render_container_type["draw_box"] =
+ render_container_type.set_function(
+ "draw_box",
[] (render_container &ctnr, float x1, float y1, float x2, float y2, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
x1 = std::clamp(x1, 0.0f, 1.0f);
@@ -405,8 +1136,9 @@ void lua_engine::initialize_render(sol::table &emu)
if (!bgcolor)
bgcolor = ui.colors().background_color();
ui.draw_outlined_box(ctnr, x1, y1, x2, y2, *fgcolor, *bgcolor);
- };
- render_container_type["draw_line"] =
+ });
+ render_container_type.set_function(
+ "draw_line",
[] (render_container &ctnr, float x1, float y1, float x2, float y2, std::optional<uint32_t> color)
{
x1 = std::clamp(x1, 0.0f, 1.0f);
@@ -416,9 +1148,16 @@ void lua_engine::initialize_render(sol::table &emu)
if (!color)
color = mame_machine_manager::instance()->ui().colors().text_color();
ctnr.add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(*color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- };
- render_container_type["draw_text"] =
- [this] (render_container &ctnr, sol::object xobj, float y, char const *msg, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
+ });
+ render_container_type.set_function(
+ "draw_quad",
+ [] (render_container &cntr, render_texture_helper const &tex, float x1, float y1, float x2, float y2, std::optional<uint32_t> color)
+ {
+ cntr.add_quad(x1, y1, x2, y2, color ? *color : uint32_t(0xffffffff), tex.texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ });
+ render_container_type.set_function(
+ "draw_text",
+ [] (render_container &ctnr, sol::this_state s, sol::object xobj, float y, char const *msg, std::optional<uint32_t> fgcolor, std::optional<uint32_t> bgcolor)
{
auto justify = ui::text_layout::text_justify::LEFT;
float x = 0;
@@ -438,7 +1177,7 @@ void lua_engine::initialize_render(sol::table &emu)
}
else
{
- luaL_error(m_lua_state, "Error in param 1 to draw_text");
+ luaL_error(s, "Error in param 1 to draw_text");
return;
}
y = std::clamp(y, 0.0f, 1.0f);
@@ -453,7 +1192,7 @@ void lua_engine::initialize_render(sol::table &emu)
x, y, (1.0f - x),
justify, ui::text_layout::word_wrapping::WORD,
mame_ui_manager::OPAQUE_, *fgcolor, *bgcolor);
- };
+ });
render_container_type["user_settings"] = sol::property(&render_container::get_user_settings, &render_container::set_user_settings);
render_container_type["orientation"] = sol::property(
&render_container::orientation,
@@ -510,6 +1249,23 @@ void lua_engine::initialize_render(sol::table &emu)
auto render_type = sol().registry().new_usertype<render_manager>("render", sol::no_constructor);
+ render_type["texture_alloc"] = sol::overload(
+ [] (render_manager &manager, sol::this_state s, bitmap_helper<bitmap_ind16>::ptr const &bitmap)
+ {
+ return render_texture_helper(s, manager, bitmap, TEXFORMAT_PALETTE16);
+ },
+ [] (render_manager &manager, sol::this_state s, bitmap_helper<bitmap_yuy16>::ptr const &bitmap)
+ {
+ return render_texture_helper(s, manager, bitmap, TEXFORMAT_YUY16);
+ },
+ [] (render_manager &manager, sol::this_state s, bitmap_helper<bitmap_rgb32>::ptr const &bitmap)
+ {
+ return render_texture_helper(s, manager, bitmap, TEXFORMAT_RGB32);
+ },
+ [] (render_manager &manager, sol::this_state s, bitmap_helper<bitmap_argb32>::ptr const &bitmap)
+ {
+ return render_texture_helper(s, manager, bitmap, TEXFORMAT_ARGB32);
+ });
render_type["max_update_rate"] = sol::property(&render_manager::max_update_rate);
render_type["ui_target"] = sol::property(&render_manager::ui_target);
render_type["ui_container"] = sol::property(&render_manager::ui_container);
diff --git a/src/frontend/mame/mame.cpp b/src/frontend/mame/mame.cpp
index c7bc0879909..a44bd19814f 100644
--- a/src/frontend/mame/mame.cpp
+++ b/src/frontend/mame/mame.cpp
@@ -19,6 +19,7 @@
#include "cheat.h"
#include "clifront.h"
#include "emuopts.h"
+#include "fileio.h"
#include "luaengine.h"
#include "mameopts.h"
#include "pluginopts.h"
@@ -73,6 +74,7 @@ mame_machine_manager::mame_machine_manager(emu_options &options,osd_interface &o
mame_machine_manager::~mame_machine_manager()
{
+ m_autoboot_script.reset();
m_lua.reset();
s_manager = nullptr;
}
@@ -141,10 +143,7 @@ void mame_machine_manager::start_luaengine()
std::string pluginpath;
while (iter.next(pluginpath))
{
- // user may specify environment variables; subsitute them
- osd_subst_env(pluginpath, pluginpath);
-
- // and then scan the directory recursively
+ // scan the directory recursively
m_plugins->scan_directory(pluginpath, true);
}
@@ -201,9 +200,29 @@ void mame_machine_manager::start_luaengine()
std::error_condition const filerr = file.open("boot.lua");
if (!filerr)
{
- std::string exppath;
- osd_subst_env(exppath, std::string(file.fullpath()));
- m_lua->load_script(exppath.c_str());
+ const std::string exppath = file.fullpath();
+ auto &l(*lua());
+ auto load_result = l.load_script(exppath);
+ if (!load_result.valid())
+ {
+ sol::error err = load_result;
+ sol::load_status status = load_result.status();
+ fatalerror("Error plugin bootstrap script %s: %s error\n%s\n",
+ exppath,
+ sol::to_string(status),
+ err.what());
+ }
+ sol::protected_function func = load_result;
+ sol::protected_function_result call_result = l.invoke(func);
+ if (!call_result.valid())
+ {
+ sol::error err = call_result;
+ sol::call_status status = call_result.status();
+ fatalerror("Error running plugin bootstrap script %s: %s error\n%s\n",
+ options().autoboot_script(),
+ sol::to_string(status),
+ err.what());
+ }
}
}
}
@@ -245,7 +264,7 @@ int mame_machine_manager::execute()
m_options.revert(OPTION_PRIORITY_INI);
std::ostringstream errors;
- mame_options::parse_standard_inis(m_options, errors);
+ mame_options::parse_standard_inis(m_options, errors, system);
}
// otherwise, perform validity checks before anything else
@@ -279,7 +298,10 @@ int mame_machine_manager::execute()
else
{
if (machine.exit_pending())
+ {
m_options.set_system_name("");
+ m_options.set_value(OPTION_BIOS, "", OPTION_PRIORITY_CMDLINE);
+ }
}
if (machine.exit_pending() && (!started_empty || is_empty))
@@ -294,21 +316,35 @@ int mame_machine_manager::execute()
TIMER_CALLBACK_MEMBER(mame_machine_manager::autoboot_callback)
{
- if (strlen(options().autoboot_script())!=0) {
- mame_machine_manager::instance()->lua()->load_script(options().autoboot_script());
+ if (*options().autoboot_script())
+ {
+ assert(m_autoboot_script);
+ sol::protected_function func = *m_autoboot_script;
+ sol::protected_function_result result = lua()->invoke(func);
+ if (!result.valid())
+ {
+ sol::error err = result;
+ sol::call_status status = result.status();
+ fatalerror("Error running autoboot script %s: %s error\n%s\n",
+ options().autoboot_script(),
+ sol::to_string(status),
+ err.what());
+ }
}
- else if (strlen(options().autoboot_command())!=0) {
- std::string cmd = std::string(options().autoboot_command());
+ else if (*options().autoboot_command())
+ {
+ std::string cmd(options().autoboot_command());
strreplace(cmd, "'", "\\'");
std::string val = std::string("emu.keypost('").append(cmd).append("')");
- mame_machine_manager::instance()->lua()->load_string(val.c_str());
+ auto &l(*lua());
+ l.invoke(l.load_string(val).get<sol::protected_function>());
}
}
void mame_machine_manager::reset()
{
// setup autoboot if needed
- m_autoboot_timer->adjust(attotime(options().autoboot_delay(),0),0);
+ m_autoboot_timer->adjust(attotime(options().autoboot_delay(), 0), 0);
}
ui_manager* mame_machine_manager::create_ui(running_machine& machine)
@@ -318,8 +354,6 @@ ui_manager* mame_machine_manager::create_ui(running_machine& machine)
machine.add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&mame_machine_manager::reset, this));
- m_ui->set_startup_text("Initializing...", true);
-
return m_ui.get();
}
@@ -336,7 +370,7 @@ void mame_machine_manager::before_load_settings(running_machine& machine)
m_lua->on_machine_before_load_settings();
}
-void mame_machine_manager::create_custom(running_machine& machine)
+void mame_machine_manager::create_custom(running_machine &machine)
{
// start the inifile manager
m_inifile = std::make_unique<inifile_manager>(m_ui->options());
@@ -346,6 +380,25 @@ void mame_machine_manager::create_custom(running_machine& machine)
// start favorite manager
m_favorite = std::make_unique<favorite_manager>(m_ui->options());
+
+ // attempt to load the autoboot script if configured
+ m_autoboot_script.reset();
+ if (*options().autoboot_script())
+ {
+ auto result = lua()->load_script(options().autoboot_script());
+ if (!result.valid())
+ {
+ sol::error err = result;
+ sol::load_status status = result.status();
+ fatalerror("Error loading autoboot script %s: %s error\n%s\n",
+ options().autoboot_script(),
+ sol::to_string(status),
+ err.what());
+ }
+ m_autoboot_script.reset(new sol::load_result(std::move(result)));
+ sol::protected_function func = *m_autoboot_script;
+ sol::set_environment(lua()->make_environment(), func);
+ }
}
void mame_machine_manager::load_cheatfiles(running_machine& machine)
@@ -406,9 +459,9 @@ int emulator_info::start_frontend(emu_options &options, osd_interface &osd, int
return start_frontend(options, osd, args);
}
-void emulator_info::draw_user_interface(running_machine& machine)
+bool emulator_info::draw_user_interface(running_machine& machine)
{
- mame_machine_manager::instance()->ui().update_and_render(machine.render().ui_container());
+ return mame_machine_manager::instance()->ui().update_and_render(machine.render().ui_container());
}
void emulator_info::periodic_check()
diff --git a/src/frontend/mame/mame.h b/src/frontend/mame/mame.h
index 5fb5a2f4a81..80bd0419a22 100644
--- a/src/frontend/mame/mame.h
+++ b/src/frontend/mame/mame.h
@@ -6,12 +6,20 @@
Controls execution of the core MAME system.
***************************************************************************/
-
#ifndef MAME_FRONTEND_MAME_MAME_H
#define MAME_FRONTEND_MAME_MAME_H
#pragma once
+#include "main.h"
+
+
+namespace sol {
+
+struct load_result;
+
+} // namespace sol
+
class plugin_options;
class osd_interface;
@@ -71,19 +79,20 @@ private:
mame_machine_manager &operator=(mame_machine_manager const &) = delete;
mame_machine_manager &operator=(mame_machine_manager &&) = delete;
- std::unique_ptr<plugin_options> m_plugins; // pointer to plugin options
- std::unique_ptr<lua_engine> m_lua;
+ std::unique_ptr<plugin_options> m_plugins; // pointer to plugin options
+ std::unique_ptr<lua_engine> m_lua;
- const game_driver * m_new_driver_pending; // pointer to the next pending driver
+ const game_driver * m_new_driver_pending; // pointer to the next pending driver
bool m_firstrun;
- static mame_machine_manager *s_manager;
- emu_timer *m_autoboot_timer; // autoboot timer
- std::unique_ptr<mame_ui_manager> m_ui; // internal data from ui.cpp
- std::unique_ptr<cheat_manager> m_cheat; // internal data from cheat.cpp
- std::unique_ptr<inifile_manager> m_inifile; // internal data from inifile.c for INIs
- std::unique_ptr<favorite_manager> m_favorite; // internal data from inifile.c for favorites
+ emu_timer * m_autoboot_timer; // auto-boot timer
+ std::unique_ptr<sol::load_result> m_autoboot_script; // auto-boot script
+ std::unique_ptr<mame_ui_manager> m_ui; // internal data from ui.cpp
+ std::unique_ptr<cheat_manager> m_cheat; // internal data from cheat.cpp
+ std::unique_ptr<inifile_manager> m_inifile; // internal data from inifile.c for INIs
+ std::unique_ptr<favorite_manager> m_favorite; // internal data from inifile.c for favorites
+ static mame_machine_manager *s_manager;
};
//**************************************************************************
diff --git a/src/frontend/mame/mameopts.cpp b/src/frontend/mame/mameopts.cpp
index a1d370ddb74..cd754fcb0f3 100644
--- a/src/frontend/mame/mameopts.cpp
+++ b/src/frontend/mame/mameopts.cpp
@@ -11,12 +11,19 @@
#include "emu.h"
#include "mameopts.h"
+#include "clifront.h"
+
+// emu
#include "drivenum.h"
+#include "fileio.h"
+#include "hashfile.h"
+#include "main.h"
#include "screen.h"
#include "softlist_dev.h"
+
+// lib/util
+#include "path.h"
#include "zippath.h"
-#include "hashfile.h"
-#include "clifront.h"
#include <cctype>
#include <stack>
@@ -43,50 +50,35 @@ void mame_options::parse_standard_inis(emu_options &options, std::ostream &error
if (!cursystem)
return;
- // parse "vertical.ini" or "horizont.ini"
- if (cursystem->flags & ORIENTATION_SWAP_XY)
- parse_one_ini(options, "vertical", OPTION_PRIORITY_ORIENTATION_INI, &error_stream);
- else
- parse_one_ini(options, "horizont", OPTION_PRIORITY_ORIENTATION_INI, &error_stream);
-
- switch (cursystem->flags & machine_flags::MASK_TYPE)
+ if (&GAME_NAME(___empty) != cursystem) // hacky - this thing isn't a real system
{
- case machine_flags::TYPE_ARCADE:
- parse_one_ini(options, "arcade", OPTION_PRIORITY_SYSTYPE_INI, &error_stream);
- break;
- case machine_flags::TYPE_CONSOLE:
- parse_one_ini(options ,"console", OPTION_PRIORITY_SYSTYPE_INI, &error_stream);
- break;
- case machine_flags::TYPE_COMPUTER:
- parse_one_ini(options, "computer", OPTION_PRIORITY_SYSTYPE_INI, &error_stream);
- break;
- case machine_flags::TYPE_OTHER:
- parse_one_ini(options, "othersys", OPTION_PRIORITY_SYSTYPE_INI, &error_stream);
- break;
- default:
- break;
- }
-
- machine_config config(*cursystem, options);
- for (const screen_device &device : screen_device_enumerator(config.root_device()))
- {
- // parse "raster.ini" for raster games
- if (device.screen_type() == SCREEN_TYPE_RASTER)
- {
- parse_one_ini(options, "raster", OPTION_PRIORITY_SCREEN_INI, &error_stream);
- break;
- }
- // parse "vector.ini" for vector games
- if (device.screen_type() == SCREEN_TYPE_VECTOR)
- {
- parse_one_ini(options, "vector", OPTION_PRIORITY_SCREEN_INI, &error_stream);
- break;
- }
- // parse "lcd.ini" for lcd games
- if (device.screen_type() == SCREEN_TYPE_LCD)
+ // parse "vertical.ini" or "horizont.ini"
+ if (cursystem->flags & ORIENTATION_SWAP_XY)
+ parse_one_ini(options, "vertical", OPTION_PRIORITY_ORIENTATION_INI, &error_stream);
+ else
+ parse_one_ini(options, "horizont", OPTION_PRIORITY_ORIENTATION_INI, &error_stream);
+
+ machine_config config(*cursystem, options);
+ for (const screen_device &device : screen_device_enumerator(config.root_device()))
{
- parse_one_ini(options, "lcd", OPTION_PRIORITY_SCREEN_INI, &error_stream);
- break;
+ // parse "raster.ini" for raster games
+ if (device.screen_type() == SCREEN_TYPE_RASTER)
+ {
+ parse_one_ini(options, "raster", OPTION_PRIORITY_SCREEN_INI, &error_stream);
+ break;
+ }
+ // parse "vector.ini" for vector games
+ if (device.screen_type() == SCREEN_TYPE_VECTOR)
+ {
+ parse_one_ini(options, "vector", OPTION_PRIORITY_SCREEN_INI, &error_stream);
+ break;
+ }
+ // parse "lcd.ini" for lcd games
+ if (device.screen_type() == SCREEN_TYPE_LCD)
+ {
+ parse_one_ini(options, "lcd", OPTION_PRIORITY_SCREEN_INI, &error_stream);
+ break;
+ }
}
}
diff --git a/src/frontend/mame/mameopts.h b/src/frontend/mame/mameopts.h
index 2b178fb2ead..bc66245e6c1 100644
--- a/src/frontend/mame/mameopts.h
+++ b/src/frontend/mame/mameopts.h
@@ -31,7 +31,6 @@ enum
OPTION_PRIORITY_MAME_INI = OPTION_PRIORITY_NORMAL + 1,
OPTION_PRIORITY_DEBUG_INI,
OPTION_PRIORITY_ORIENTATION_INI,
- OPTION_PRIORITY_SYSTYPE_INI,
OPTION_PRIORITY_SCREEN_INI,
OPTION_PRIORITY_SOURCE_INI,
OPTION_PRIORITY_GPARENT_INI,
diff --git a/src/frontend/mame/media_ident.cpp b/src/frontend/mame/media_ident.cpp
index b1a18ed7789..d09858198ea 100644
--- a/src/frontend/mame/media_ident.cpp
+++ b/src/frontend/mame/media_ident.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- media_ident.c
+ media_ident.cpp
Media identify.
@@ -14,6 +14,7 @@
#include "softlist_dev.h"
#include "jedparse.h"
+#include "path.h"
#include "unzip.h"
@@ -133,7 +134,7 @@ void media_identifier::collect_files(std::vector<file_info> &info, char const *p
}
}
}
- else if (core_filename_ends_with(path, ".7z") || core_filename_ends_with(path, ".zip"))
+ else if (core_filename_ends_with(path, ".7z") || core_filename_ends_with(path, ".zip") || core_filename_ends_with(path, ".imz"))
{
// first attempt to examine it as a valid zip/7z file
util::archive_file::ptr archive;
@@ -260,33 +261,27 @@ void media_identifier::digest_file(std::vector<file_info> &info, char const *pat
// load the file and process if it opens and has a valid length
util::core_file::ptr file;
- if (util::core_file::open(path, OPEN_FLAG_READ, file) || !file)
+ std::error_condition err = util::core_file::open(path, OPEN_FLAG_READ, file);
+ if (err || !file)
{
- osd_printf_error("%s: error opening file\n", path);
+ osd_printf_error("%s: error opening file (%s)\n", path, err ? err.message() : std::string("could not allocate pointer"));
return;
}
std::uint64_t length;
- if (file->length(length))
+ err = file->length(length);
+ if (err)
{
- osd_printf_error("%s: error getting file length\n", path);
+ osd_printf_error("%s: error getting file length (%s)\n", path, err.message());
return;
}
util::hash_collection hashes;
- hashes.begin(util::hash_collection::HASH_TYPES_CRC_SHA1);
- std::uint8_t buf[1024];
- for (std::uint64_t remaining = length; remaining; )
+ std::size_t actual;
+ err = hashes.compute(*file, 0U, length, actual, util::hash_collection::HASH_TYPES_CRC_SHA1);
+ if (err)
{
- std::size_t const block = std::min<std::uint64_t>(remaining, sizeof(buf));
- std::size_t actual;
- if (file->read(buf, block, actual) || !actual)
- {
- osd_printf_error("%s: error reading file\n", path);
- return;
- }
- remaining -= actual;
- hashes.buffer(buf, actual);
+ osd_printf_error("%s: error reading file (%s)\n", path, err.message());
+ return;
}
- hashes.end();
info.emplace_back(path, length, std::move(hashes), file_flavour::RAW);
m_total++;
}
diff --git a/src/frontend/mame/pluginopts.cpp b/src/frontend/mame/pluginopts.cpp
index 51fa39c2ac9..d06bb4ce49b 100644
--- a/src/frontend/mame/pluginopts.cpp
+++ b/src/frontend/mame/pluginopts.cpp
@@ -10,7 +10,9 @@
#include "emu.h"
#include "pluginopts.h"
+
#include "options.h"
+#include "path.h"
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
@@ -47,13 +49,12 @@ void plugin_options::scan_directory(const std::string &path, bool recursive)
{
if (entry->type == osd::directory::entry::entry_type::FILE && !strcmp(entry->name, "plugin.json"))
{
- std::string curfile = std::string(path).append(PATH_SEPARATOR).append(entry->name);
- load_plugin(curfile);
+ load_plugin(util::path_concat(path, entry->name));
}
else if (entry->type == osd::directory::entry::entry_type::DIR)
{
if (recursive && strcmp(entry->name, ".") && strcmp(entry->name, ".."))
- scan_directory(path + PATH_SEPARATOR + entry->name, recursive);
+ scan_directory(util::path_concat(path, entry->name), recursive);
}
}
}
@@ -131,7 +132,7 @@ static core_options create_core_options(const plugin_options &plugin_opts)
// the data back
static const options_entry s_option_entries[] =
{
- { nullptr, nullptr, OPTION_HEADER, "PLUGINS OPTIONS" },
+ { nullptr, nullptr, core_options::option_type::HEADER, "PLUGINS OPTIONS" },
{ nullptr }
};
@@ -141,11 +142,14 @@ static core_options create_core_options(const plugin_options &plugin_opts)
// create an entry for each option
for (const plugin_options::plugin &p : plugin_opts.plugins())
{
- opts.add_entry(
- { p.m_name },
- nullptr,
- core_options::option_type::BOOLEAN,
- p.m_start ? "1" : "0");
+ if (p.m_type != "library")
+ {
+ opts.add_entry(
+ { p.m_name },
+ nullptr,
+ core_options::option_type::BOOLEAN,
+ p.m_start ? "1" : "0");
+ }
}
return opts;
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 6dfe4165788..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,11 +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(_("menu-analoginput", "Analog Input Adjustments"));
}
@@ -65,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);
@@ -122,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;
}
}
@@ -159,179 +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;
- switch (ev->iptkey)
+ // 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:
{
- // 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)
{
- 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)
- {
- 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);
+ // clamp to range
+ newval = std::clamp(newval, data.min, data.max);
- // if things changed, update
- if (newval != data.cur)
- {
- ioport_field::user_settings settings;
-
- // 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())
@@ -366,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;
}
@@ -394,10 +660,14 @@ void menu_analog::populate(float &customtop, float &custombottom)
&data);
}
+ // display a message if there are toggle inputs enabled
+ if (!prev_owner)
+ 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));
}
@@ -449,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 aaad0967f00..180c3005b73 100644
--- a/src/frontend/mame/ui/auditmenu.cpp
+++ b/src/frontend/mame/ui/auditmenu.cpp
@@ -17,6 +17,8 @@
#include "audit.h"
#include "drivenum.h"
+#include "fileio.h"
+#include "main.h"
#include "uiinput.h"
#include "util/corestr.h"
@@ -55,6 +57,8 @@ menu_audit::menu_audit(mame_ui_manager &mui, render_container &container)
, m_phase(phase::CONFIRMATION)
, m_fast(true)
{
+ set_heading(_("Audit Media"));
+
std::string filename(emulator_info::get_configname());
filename += "_avail.ini";
m_prompt = util::string_format(_("Results will be saved to %1$s"), filename);
@@ -71,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)
{
@@ -80,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;
@@ -95,7 +107,7 @@ void menu_audit::custom_render(void *selectedref, float top, float bottom, float
std::size_t const total(m_fast ? m_unavailable : m_availablesorted.size());
std::ostringstream text;
util::stream_format(text,
- _("Auditing ROMs for machine %2$u of %3$u...\n%1$s"),
+ _("Auditing media for system %2$u of %3$u...\n%1$s"),
system ? std::string_view(system->description) : std::string_view(),
(std::min)(audited + 1, total),
total);
@@ -104,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;
@@ -115,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 ROMs for %1$u machines marked unavailable"), m_unavailable), 0, ITEMREF_START_FAST);
- item_append(util::string_format(_("Audit ROMs for all %1$u machines"), m_availablesorted.size()), 0, ITEMREF_START_FULL);
+ 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)
{
@@ -151,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;
@@ -174,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 7181445e240..979203f0133 100644
--- a/src/frontend/mame/ui/barcode.cpp
+++ b/src/frontend/mame/ui/barcode.cpp
@@ -37,6 +37,7 @@ namespace ui {
menu_barcode_reader::menu_barcode_reader(mame_ui_manager &mui, render_container &container, barcode_reader_device *device)
: menu_device_control<barcode_reader_device>(mui, container, device)
{
+ set_heading(_("Barcode Reader"));
set_process_flags(PROCESS_LR_REPEAT);
}
@@ -53,34 +54,19 @@ 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(current_display_name(), current_display_flags(), ITEMREF_SELECT_READER);
+ 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);
item_append(menu_item_type::SEPARATOR);
-
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
}
@@ -89,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_SPECIAL:
- if (get_selection_ref() == ITEMREF_NEW_BARCODE)
+ 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))
{
- 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;
+
+ case IPT_SPECIAL:
+ if (get_selection_ref() == ITEMREF_NEW_BARCODE)
+ {
+ if (input_character(m_barcode_buffer, ev->unichar, uchar_is_digit))
+ {
+ ev->item->set_subtext(m_barcode_buffer);
+ return true;
+ }
+ }
+ 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 aeed0024b69..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())
@@ -167,103 +169,117 @@ void menu_confswitch::populate(float &customtop, float &custombottom)
}
item_append(menu_item_type::SEPARATOR);
- item_append(_("Reset Machine"), 0, (void *)1);
+ item_append(_("Reset System"), 0, (void *)1);
}
-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;
}
@@ -311,6 +327,7 @@ menu_settings_dip_switches::menu_settings_dip_switches(mame_ui_manager &mui, ren
, m_first_nub(0.0f)
, m_clickable_height(0.0f)
{
+ set_heading(_("DIP Switches"));
}
@@ -319,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())
@@ -337,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)
{
@@ -377,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(),
@@ -431,13 +457,16 @@ void menu_settings_dip_switches::custom_render(void *selectedref, float top, flo
}
-bool menu_settings_dip_switches::custom_mouse_down()
+std::tuple<int, bool, bool> menu_settings_dip_switches::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- if (!m_visible_switch_groups || (get_mouse_x() < m_first_nub))
- return false;
+ if (!m_visible_switch_groups || !(uievt.pointer_pressed & 0x01) || (uievt.pointer_buttons & ~u32(0x01)))
+ return std::make_tuple(IPT_INVALID, false, false);
+
+ auto const [cx, y] = pointer_location();
+ if (cx < m_first_nub)
+ return std::make_tuple(IPT_INVALID, false, false);
- float const x(get_mouse_x() - m_first_nub);
- float const y(get_mouse_y());
+ float const x(cx - m_first_nub);
for (unsigned n = 0U, line = 0U; (switch_groups().size() > n) && (m_visible_switch_groups > line); ++n)
{
switch_group_descriptor const &group(switch_groups()[n]);
@@ -458,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);
}
}
}
@@ -467,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);
}
}
@@ -504,11 +531,11 @@ void menu_settings_dip_switches::populate(float &customtop, float &custombottom)
menu_settings_machine_config::menu_settings_machine_config(mame_ui_manager &mui, render_container &container) : menu_confswitch(mui, container, IPT_CONFIG)
{
+ set_heading(_("Machine Configuration"));
}
menu_settings_machine_config::~menu_settings_machine_config()
{
}
-
} // namespace ui
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 02da9310207..9cfd7284d9b 100644
--- a/src/frontend/mame/ui/custui.cpp
+++ b/src/frontend/mame/ui/custui.cpp
@@ -17,10 +17,12 @@
#include "drivenum.h"
#include "emuopts.h"
+#include "fileio.h"
#include "uiinput.h"
#include "corestr.h"
#include "osdepend.h"
+#include "path.h"
#include <algorithm>
#include <iterator>
@@ -76,8 +78,11 @@ 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"));
+
find_languages();
find_sysnames();
}
@@ -88,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());
@@ -105,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(), 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(), 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(), 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);
@@ -216,25 +225,10 @@ 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);
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
-}
-
-//-------------------------------------------------
-// perform our special rendering
-//-------------------------------------------------
-
-void menu_custom_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
-{
- char const *const text[] = { _("UI Customization Settings") };
- draw_text_box(
- std::begin(text), std::end(text),
- 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_GREEN_COLOR, 1.0f);
}
//-------------------------------------------------
@@ -262,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)
@@ -271,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
@@ -304,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)
@@ -313,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
@@ -339,10 +333,12 @@ 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)
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading(_("UI Fonts"));
std::string name(mui.machine().options().ui_font());
list();
@@ -384,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);
@@ -410,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(), 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;
@@ -525,32 +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 = customtop = 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)
{
- // top text
- char const *const toptext[] = { _("UI Fonts Settings") };
- draw_text_box(
- std::begin(toptext), std::end(toptext),
- 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_GREEN_COLOR, 1.0f);
-
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));
}
}
@@ -561,6 +568,8 @@ void menu_font_ui::custom_render(void *selectedref, float top, float bottom, flo
menu_colors_ui::menu_colors_ui(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("UI Colors"));
+
SET_COLOR_UI(m_color_table, UI_BACKGROUND_COLOR);
SET_COLOR_UI(m_color_table, UI_BORDER_COLOR);
SET_COLOR_UI(m_color_table, UI_CLONE_COLOR);
@@ -600,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)
@@ -612,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);
@@ -642,45 +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 = customtop = 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)
{
- // top text
- char const *const toptext[] = { _("UI Color Settings") };
- draw_text_box(
- std::begin(toptext), std::end(toptext),
- 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_GREEN_COLOR, 1.0f);
-
- // bottom text
// 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];
@@ -692,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(
@@ -746,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(
@@ -755,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(
@@ -784,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());
}
//-------------------------------------------------
@@ -809,97 +814,98 @@ menu_rgb_ui::menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_
, m_search()
, m_key_active(false)
, m_lock_ref(0)
- , m_title(std::move(title))
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading(std::move(title));
}
//-------------------------------------------------
// 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("_");
@@ -940,86 +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);
-
- custombottom = customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
//-------------------------------------------------
-// perform our special rendering
+// recompute metrics
//-------------------------------------------------
-void menu_rgb_ui::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_rgb_ui::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
- float width, maxwidth = origx2 - origx1;
+ menu::recompute_metrics(width, height, aspect);
- // top text
- ui().draw_text_full(
- container(),
- m_title,
- 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);
- const float lr_border = ui().box_lr_border() * machine().render().ui_aspect(&container());
- width += 2 * lr_border;
- maxwidth = std::max(maxwidth, width);
-
- // compute our bounds
- float x1 = 0.5f - 0.5f * maxwidth;
- float x2 = x1 + maxwidth;
- float y1 = origy1 - top;
- float y2 = origy1 - ui().box_tb_border();
-
- // draw a box
- ui().draw_outlined_box(container(), x1, y1, x2, y2, UI_GREEN_COLOR);
+ set_custom_space(0.0f, line_height() + 3.0f * tb_border());
+}
- // take off the borders
- x1 += lr_border;
- x2 -= lr_border;
- y1 += ui().box_tb_border();
+//-------------------------------------------------
+// perform our special rendering
+//-------------------------------------------------
- // draw the text within it
- ui().draw_text_full(
- container(),
- m_title,
- 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());
+void menu_rgb_ui::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+{
+ float maxwidth = origx2 - origx1;
std::string sampletxt(_("Color preview:"));
- 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
- x1 = 0.5f - 0.5f * maxwidth;
- x2 = x1 + maxwidth;
- y1 = origy2 + ui().box_tb_border();
- y2 = origy2 + bottom;
+ float x1 = 0.5f - 0.5f * maxwidth;
+ float x2 = x1 + maxwidth;
+ 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));
@@ -1030,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)
{
@@ -1073,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;
}
}
@@ -1107,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)
@@ -1119,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 168934b5356..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 {
@@ -30,12 +32,11 @@ public:
menu_custom_ui(mame_ui_manager &mui, render_container &container, std::function<void ()> &&handler);
protected:
- virtual void custom_render(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 find_languages();
void find_sysnames();
@@ -45,6 +46,7 @@ private:
std::vector<std::string> m_sysnames;
std::size_t m_currlang;
std::size_t m_currsysnames;
+ u8 m_currpanels;
};
//-------------------------------------------------
@@ -57,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();
@@ -72,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;
@@ -91,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:
@@ -122,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();
@@ -139,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
@@ -151,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;
};
//-------------------------------------------------
@@ -173,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 df8ac0a7e67..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(reset_options::SELECT_FIRST);
+ // 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 9885e69fb85..8d841c7d131 100644
--- a/src/frontend/mame/ui/devopt.cpp
+++ b/src/frontend/mame/ui/devopt.cpp
@@ -13,6 +13,11 @@
#include "ui/ui.h"
#include "romload.h"
+#include "screen.h"
+
+#include "util/unicode.h"
+
+#include <locale>
namespace ui {
@@ -43,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()));
@@ -52,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
@@ -90,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));
}
@@ -99,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);
@@ -120,17 +132,23 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
}
else
{
- std::string hz(std::to_string(float(screen.frame_period().as_hz())));
- size_t last = hz.find_last_not_of('0');
- size_t dpos = hz.find_last_of('.');
- hz = hz.substr(0, last + (last != dpos ? 1 : 0));
+ const u32 rate = u32(screen.frame_period().as_hz() * 1'000'000 + 0.5);
+ const bool valid = rate >= 1'000'000;
+ std::string hz(valid ? std::to_string(rate) : "?");
+ if (valid)
+ {
+ size_t dpos = hz.length() - 6;
+ 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();
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(),
@@ -166,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));
}
@@ -175,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);
@@ -252,12 +270,12 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
if (setting.value() == field.defvalue())
{
def = true;
- util::stream_format(dips_opt, _(" %1$s [default: %2$s]\n"), field.name(), setting.name());
+ util::stream_format(dips_opt, _(" %1$s [default: %2$s]\n"), field.specific_name(), setting.name());
break;
}
}
if (!def)
- util::stream_format(dips_opt, _(" %1$s\n"), field.name());
+ util::stream_format(dips_opt, _(" %1$s\n"), field.specific_name());
}
else if (field.type() == IPT_CONFIG)
{
@@ -268,12 +286,12 @@ void menu_device_config::populate_text(std::optional<text_layout> &layout, float
if (setting.value() == field.defvalue())
{
def = true;
- util::stream_format(confs_opt, _(" %1$s [default: %2$s]\n"), field.name(), setting.name());
+ util::stream_format(confs_opt, _(" %1$s [default: %2$s]\n"), field.specific_name(), setting.name());
break;
}
}
if (!def)
- util::stream_format(confs_opt, _(" %1$s\n"), field.name());
+ util::stream_format(confs_opt, _(" %1$s\n"), field.specific_name());
}
}
@@ -346,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 3b92204ae3e..083dfbeba79 100644
--- a/src/frontend/mame/ui/dirmenu.cpp
+++ b/src/frontend/mame/ui/dirmenu.cpp
@@ -9,511 +9,611 @@
*********************************************************************/
#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"
#include "mame.h"
-#include "corestr.h"
+#include "util/corestr.h"
+#include "util/path.h"
+
+#include <locale>
namespace ui {
-static int ADDING = 1;
-static int CHANGE = 2;
+namespace {
struct folders_entry
{
const char *name;
const char *option;
- const int action;
};
-static const folders_entry s_folders[] =
+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 }
};
/**************************************************
- MENU DIRECTORY
+ MENU REMOVE FOLDER
**************************************************/
-//-------------------------------------------------
-// ctor / dtor
-//-------------------------------------------------
-
-menu_directory::menu_directory(mame_ui_manager &mui, render_container &container) : menu(mui, container)
-{
-}
-menu_directory::~menu_directory()
+class menu_remove_folder : public menu
{
- ui().save_ui_options();
- ui_globals::reset = true;
-}
+public:
+ menu_remove_folder(mame_ui_manager &mui, render_container &container, int ref);
-//-------------------------------------------------
-// handle
-//-------------------------------------------------
+private:
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
-void 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());
-}
+ std::string m_searchpath;
+ int const m_ref;
+ std::vector<std::string> m_folders;
+};
//-------------------------------------------------
-// populate
+// ctor / dtor
//-------------------------------------------------
-void menu_directory::populate(float &customtop, float &custombottom)
+menu_remove_folder::menu_remove_folder(mame_ui_manager &mui, render_container &container, int ref)
+ : menu(mui, container)
+ , m_ref(ref)
{
- for (auto & elem : s_folders)
- item_append(_("path-option", elem.name), 0, (void *)(uintptr_t)elem.action);
+ set_heading(util::string_format(_("Remove %1$s Folder"), _("path-option", f_folders[m_ref].name)));
- item_append(menu_item_type::SEPARATOR);
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
+ if (mui.options().exists(f_folders[m_ref].option))
+ m_searchpath.assign(mui.options().value(f_folders[m_ref].option));
+ else
+ m_searchpath.assign(mui.machine().options().value(f_folders[m_ref].option));
+
+ path_iterator path(m_searchpath);
+ std::string curpath;
+ while (path.next(curpath))
+ m_folders.push_back(curpath);
}
//-------------------------------------------------
-// perform our special rendering
+// handle
//-------------------------------------------------
-void menu_directory::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+bool menu_remove_folder::handle(event const *ev)
{
- char const *const toptext[] = { _("Folders Setup") };
- draw_text_box(
- std::begin(toptext), std::end(toptext),
- 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_GREEN_COLOR, 1.0f);
-}
+ // process the menu
+ if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT)
+ {
+ std::string tmppath, error_string;
+ m_folders.erase(m_folders.begin() + selected_index());
+ for (int x = 0; x < m_folders.size(); ++x)
+ {
+ tmppath.append(m_folders[x]);
+ if (x < m_folders.size() - 1)
+ tmppath.append(";");
+ }
-/**************************************************
- MENU DISPLAY PATH
-**************************************************/
-//-------------------------------------------------
-// ctor / dtor
-//-------------------------------------------------
+ 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);
-menu_display_actual::menu_display_actual(mame_ui_manager &mui, render_container &container, int ref)
- : menu(mui, container), m_ref(ref)
-{
-}
+ reset_parent(reset_options::REMEMBER_REF);
+ stack_pop();
+ }
-menu_display_actual::~menu_display_actual()
-{
+ return false;
}
//-------------------------------------------------
-// handle
+// populate menu
//-------------------------------------------------
-void menu_display_actual::handle(event const *ev)
+void menu_remove_folder::populate()
{
- // process the menu
- if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT)
- switch ((uintptr_t)ev->itemref)
- {
- case REMOVE:
- menu::stack_push<menu_remove_folder>(ui(), container(), m_ref);
- break;
+ int folders_count = 0;
+ for (auto & elem : m_folders)
+ item_append(elem, 0, (void *)(uintptr_t)++folders_count);
- case ADD_CHANGE:
- menu::stack_push<menu_add_change_folder>(ui(), container(), m_ref);
- break;
- }
+ item_append(menu_item_type::SEPARATOR);
}
-//-------------------------------------------------
-// populate
-//-------------------------------------------------
-void menu_display_actual::populate(float &customtop, float &custombottom)
-{
- m_heading[0] = string_format(_("Current %1$s Folders"), _("path-option", s_folders[m_ref].name));
- if (ui().options().exists(s_folders[m_ref].option))
- m_searchpath.assign(ui().options().value(s_folders[m_ref].option));
- else
- m_searchpath.assign(machine().options().value(s_folders[m_ref].option));
+/**************************************************
+ MENU ADD FOLDER
+**************************************************/
- path_iterator path(m_searchpath);
- std::string curpath;
- m_folders.clear();
- while (path.next(curpath, nullptr))
- m_folders.push_back(curpath);
+class menu_add_change_folder : public menu
+{
+public:
+ menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref, bool multipath);
- item_append((s_folders[m_ref].action == CHANGE) ? _("Change Folder") : _("Add Folder"), 0, (void *)ADD_CHANGE);
+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;
- if (m_folders.size() > 1)
- item_append(_("Remove Folder"), 0, (void *)REMOVE);
+ virtual bool custom_ui_back() override { return !m_search.empty(); }
- item_append(menu_item_type::SEPARATOR);
- customtop = (m_folders.size() + 1) * ui().get_line_height() + 6.0f * ui().box_tb_border();
-}
+private:
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
-//-------------------------------------------------
-// perform our special rendering
-//-------------------------------------------------
+ void update_search();
-void menu_display_actual::custom_render(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(),
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), ui().colors().background_color(), 1.0f));
- 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()),
- text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
- ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
-}
+ int const m_ref;
+ bool const m_multipath;
+ std::string m_current_path;
+ std::string m_search;
+ std::vector<std::string> m_folders;
+};
-/**************************************************
-MENU ADD FOLDER
-**************************************************/
//-------------------------------------------------
-// ctor / dtor
+// ctor
//-------------------------------------------------
-menu_add_change_folder::menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref) : menu(mui, container)
+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_multipath(multipath)
{
- m_ref = ref;
- m_change = (s_folders[ref].action == CHANGE);
- m_search.clear();
-
// configure the starting path
osd_get_full_path(m_current_path, ".");
std::string searchpath;
- if (mui.options().exists(s_folders[m_ref].option))
- searchpath = mui.options().value(s_folders[m_ref].option);
+ if (mui.options().exists(f_folders[m_ref].option))
+ searchpath = mui.options().value(f_folders[m_ref].option);
else
- searchpath = mui.machine().options().value(s_folders[m_ref].option);
+ searchpath = mui.machine().options().value(f_folders[m_ref].option);
path_iterator path(searchpath);
std::string curpath;
- while (path.next(curpath, nullptr))
+ while (path.next(curpath))
m_folders.push_back(curpath);
}
-menu_add_change_folder::~menu_add_change_folder()
-{
-}
-
//-------------------------------------------------
// 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)
- {
- int index = (uintptr_t)ev->itemref - 1;
- const menu_item &pitem = item(index);
+ assert(ev->item);
+ menu_item const &pitem = *ev->item;
- // go up to the parent path
- if (pitem.text() == "..")
- {
- size_t first_sep = m_current_path.find_first_of(PATH_SEPARATOR[0]);
- size_t last_sep = m_current_path.find_last_of(PATH_SEPARATOR[0]);
- if (first_sep != last_sep)
- m_current_path.erase(++last_sep);
- }
+ // 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]")
- {
- if (m_current_path[m_current_path.length() - 1] == PATH_SEPARATOR[0])
- m_current_path.append(pitem.text());
- else
- m_current_path.append(PATH_SEPARATOR).append(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(s_folders[m_ref].option))
- ui().options().set_value(s_folders[m_ref].option, m_current_path, OPTION_PRIORITY_CMDLINE);
- else if (machine().options().value(s_folders[m_ref].option) != m_current_path)
- {
- machine().options().set_value(s_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(s_folders[m_ref].option))
- ui().options().set_value(s_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
- else if (machine().options().value(s_folders[m_ref].option) != tmppath)
- {
- machine().options().set_value(s_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()
{
- // open a path
- file_enumerator path(m_current_path.c_str());
- const osd::directory::entry *dirent;
int folders_count = 0;
// add the drives
for (std::string const &volume_name : osd_get_volume_names())
item_append(volume_name, "[DRIVE]", 0, (void *)(uintptr_t)++folders_count);
- // add the directories
+ // get subdirectories
+ std::vector<std::string> dirnames;
+ file_enumerator path(m_current_path);
+ const osd::directory::entry *dirent;
while ((dirent = path.next()) != nullptr)
{
- if (dirent->type == osd::directory::entry::entry_type::DIR && strcmp(dirent->name, ".") != 0)
- item_append(dirent->name, "[DIR]", 0, (void *)(uintptr_t)++folders_count);
+ if ((osd::directory::entry::entry_type::DIR == dirent->type) && strcmp(dirent->name, "."))
+ dirnames.emplace_back(dirent->name);
}
+ // sort
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
+ std::sort(
+ dirnames.begin(),
+ dirnames.end(),
+ [&coll] (std::string const &x, std::string const &y)
+ {
+ std::wstring const xw = wstring_from_utf8(x);
+ std::wstring const yw = wstring_from_utf8(y);
+ return coll.compare(xw.data(), xw.data() + xw.size(), yw.data(), yw.data() + yw.size()) < 0;
+ });
+
+ // add to menu
+ for (std::string const &name : dirnames)
+ 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_"),
- _("path-option", s_folders[m_ref].name),
+ 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());
}
-/**************************************************
- MENU REMOVE FOLDER
-**************************************************/
//-------------------------------------------------
-// ctor / dtor
+// update search
//-------------------------------------------------
-menu_remove_folder::menu_remove_folder(mame_ui_manager &mui, render_container &container, int ref) : menu(mui, container)
+void menu_add_change_folder::update_search()
{
- m_ref = ref;
- if (mui.options().exists(s_folders[m_ref].option))
- m_searchpath.assign(mui.options().value(s_folders[m_ref].option));
- else
- m_searchpath.assign(mui.machine().options().value(s_folders[m_ref].option));
+ // check for entries which matches our search buffer
+ const int cur_selected = selected_index();
+ int entry, bestmatch = 0;
- path_iterator path(m_searchpath);
- std::string curpath;
- while (path.next(curpath, nullptr))
- m_folders.push_back(curpath);
+ // 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();
}
-menu_remove_folder::~menu_remove_folder()
+
+/**************************************************
+ MENU DISPLAY PATH
+**************************************************/
+
+class menu_display_actual : public menu
{
+public:
+ menu_display_actual(mame_ui_manager &mui, render_container &container, int selectedref)
+ : menu(mui, container)
+ , 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 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
+ {
+ ADD_CHANGE = 1,
+ REMOVE,
+ };
+
+ 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;
+};
+
+//-------------------------------------------------
+// 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_remove_folder::handle(event const *ev)
+bool menu_display_actual::handle(event const *ev)
{
- // process the menu
if (ev && ev->itemref && ev->iptkey == IPT_UI_SELECT)
{
- std::string tmppath, error_string;
- m_folders.erase(m_folders.begin() + selected_index());
- for (int x = 0; x < m_folders.size(); ++x)
+ switch ((uintptr_t)ev->itemref)
{
- tmppath.append(m_folders[x]);
- if (x < m_folders.size() - 1)
- tmppath.append(";");
- }
+ case REMOVE:
+ menu::stack_push<menu_remove_folder>(ui(), container(), m_ref);
+ break;
- if (ui().options().exists(s_folders[m_ref].option))
- ui().options().set_value(s_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
- else if (machine().options().value(s_folders[m_ref].option) != tmppath)
- {
- machine().options().set_value(s_folders[m_ref].option, tmppath, OPTION_PRIORITY_CMDLINE);
+ case ADD_CHANGE:
+ menu::stack_push<menu_add_change_folder>(ui(), container(), m_ref, m_multipath);
+ break;
}
-
- reset_parent(reset_options::REMEMBER_REF);
- stack_pop();
}
+
+ return false;
}
//-------------------------------------------------
-// populate menu
+// populate
//-------------------------------------------------
-void menu_remove_folder::populate(float &customtop, float &custombottom)
+void menu_display_actual::populate()
{
- int folders_count = 0;
- for (auto & elem : m_folders)
- item_append(elem, 0, (void *)(uintptr_t)++folders_count);
+ 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());
+
+ m_folders.clear();
+ 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(menu_item_type::SEPARATOR);
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
+
+ set_custom_space((m_folders.size() + 1) * line_height() + 6.0f * tb_border(), 0.0f);
+}
+
+//-------------------------------------------------
+// recompute metrics
+//-------------------------------------------------
+
+void menu_display_actual::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ set_custom_space((m_folders.size() + 1) * line_height() + 6.0f * tb_border(), 0.0f);
}
//-------------------------------------------------
// perform our special rendering
//-------------------------------------------------
-void menu_remove_folder::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)
{
- std::string const toptext[] = {string_format(_("Remove %1$s Folder"), _("path-option", s_folders[m_ref].name)) };
+ float const maxwidth(draw_text_box(
+ std::begin(m_folders), std::end(m_folders),
+ 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()));
draw_text_box(
- std::begin(toptext), std::end(toptext),
- origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(),
+ std::begin(m_heading), std::end(m_heading),
+ 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
+
+
+/**************************************************
+ MENU DIRECTORY
+**************************************************/
+//-------------------------------------------------
+// ctor / dtor
+//-------------------------------------------------
+
+menu_directory::menu_directory(mame_ui_manager &mui, render_container &container) : menu(mui, container)
+{
+ set_heading(_("Configure Folders"));
+}
+
+menu_directory::~menu_directory()
+{
+ ui().save_ui_options();
+ ui_globals::reset = true;
+}
+
+//-------------------------------------------------
+// handle
+//-------------------------------------------------
+
+bool menu_directory::handle(event const *ev)
+{
+ 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()
+{
+ for (auto & elem : f_folders)
+ item_append(_("path-option", elem.name), 0, this); // need a non-null reference pointer - value is immaterial
+
+ item_append(menu_item_type::SEPARATOR);
}
} // namespace ui
diff --git a/src/frontend/mame/ui/dirmenu.h b/src/frontend/mame/ui/dirmenu.h
index 556427425e0..54eb155267e 100644
--- a/src/frontend/mame/ui/dirmenu.h
+++ b/src/frontend/mame/ui/dirmenu.h
@@ -8,11 +8,11 @@
***************************************************************************/
-#pragma once
-
#ifndef MAME_FRONTEND_UI_DIRMENU_H
#define MAME_FRONTEND_UI_DIRMENU_H
+#pragma once
+
#include "ui/menu.h"
#include <string>
@@ -30,88 +30,9 @@ public:
menu_directory(mame_ui_manager &mui, render_container &container);
virtual ~menu_directory() override;
-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;
-};
-
-//-------------------------------------------------
-// class directory specific menu
-//-------------------------------------------------
-
-class menu_display_actual : public menu
-{
-public:
- menu_display_actual(mame_ui_manager &mui, render_container &container, int selectedref);
- virtual ~menu_display_actual() override;
-
-protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
-
-private:
- enum
- {
- ADD_CHANGE = 1,
- REMOVE,
- };
-
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
-
- std::string m_heading[1], m_searchpath;
- std::vector<std::string> m_folders;
- int m_ref;
-};
-
-//-------------------------------------------------
-// class remove folder menu
-//-------------------------------------------------
-
-class menu_remove_folder : public menu
-{
-public:
- menu_remove_folder(mame_ui_manager &mui, render_container &container, int ref);
- virtual ~menu_remove_folder() override;
-
-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;
-
- std::string m_searchpath;
- int m_ref;
- std::vector<std::string> m_folders;
-};
-
-//-------------------------------------------------
-// class add / change folder menu
-//-------------------------------------------------
-
-class menu_add_change_folder : public menu
-{
-public:
- menu_add_change_folder(mame_ui_manager &mui, render_container &container, int ref);
- virtual ~menu_add_change_folder() 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(); }
-
private:
- virtual void populate(float &customtop, float &custombottom) override;
- virtual void handle(event const *ev) override;
-
- int m_ref;
- std::string m_current_path;
- std::string m_search;
- bool m_change;
- std::vector<std::string> m_folders;
+ 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 875fe1cd197..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;
}
@@ -231,7 +280,7 @@ SELECT FORMAT MENU
// ctor
//-------------------------------------------------
-menu_select_format::menu_select_format(mame_ui_manager &mui, render_container &container, const std::vector<floppy_image_format_t *> &formats, int ext_match, floppy_image_format_t **result)
+menu_select_format::menu_select_format(mame_ui_manager &mui, render_container &container, const std::vector<const floppy_image_format_t *> &formats, int ext_match, const floppy_image_format_t **result)
: menu(mui, container)
{
m_formats = formats;
@@ -253,16 +302,16 @@ 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++)
{
- floppy_image_format_t *fmt = m_formats[i];
+ const floppy_image_format_t *fmt = m_formats[i];
if (i && i == m_ext_match)
item_append(menu_item_type::SEPARATOR);
- item_append(fmt->description(), fmt->name(), 0, fmt);
+ item_append(fmt->description(), fmt->name(), 0, const_cast<floppy_image_format_t *>(fmt));
}
}
@@ -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;
}
@@ -290,9 +341,9 @@ SELECT FORMAT MENU
// ctor
//-------------------------------------------------
-menu_select_floppy_init::menu_select_floppy_init(mame_ui_manager &mui, render_container &container, const std::vector<floppy_image_device::fs_info> &fs, int *result)
+menu_select_floppy_init::menu_select_floppy_init(mame_ui_manager &mui, render_container &container, std::vector<std::reference_wrapper<const floppy_image_device::fs_info>> &&fs, int *result)
: menu(mui, container),
- m_fs(fs),
+ m_fs(std::move(fs)),
m_result(result)
{
@@ -312,11 +363,11 @@ 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;
- for (const auto &fmt : m_fs)
+ for (const floppy_image_device::fs_info &fmt : m_fs)
item_append(fmt.m_description, fmt.m_name, 0, (void *)(uintptr_t)(id++));
}
@@ -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 2debc892859..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;
@@ -67,17 +69,17 @@ class menu_select_format : public menu
{
public:
menu_select_format(mame_ui_manager &mui, render_container &container,
- const std::vector<floppy_image_format_t *> &formats, int ext_match, floppy_image_format_t **result);
+ const std::vector<const floppy_image_format_t *> &formats, int ext_match, const floppy_image_format_t **result);
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<floppy_image_format_t *> m_formats;
- int m_ext_match;
- floppy_image_format_t * *m_result;
+ std::vector<const floppy_image_format_t *> m_formats;
+ int m_ext_match;
+ const floppy_image_format_t * *m_result;
};
// ======================> menu_select_floppy_init
@@ -86,16 +88,16 @@ class menu_select_floppy_init : public menu
{
public:
menu_select_floppy_init(mame_ui_manager &mui, render_container &container,
- const std::vector<floppy_image_device::fs_info> &fs, int *result);
+ std::vector<std::reference_wrapper<const floppy_image_device::fs_info>> &&fs, int *result);
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
- const std::vector<floppy_image_device::fs_info> &m_fs;
- int * m_result;
+ std::vector<std::reference_wrapper<const floppy_image_device::fs_info>> m_fs;
+ int * m_result;
};
diff --git a/src/frontend/mame/ui/filemngr.cpp b/src/frontend/mame/ui/filemngr.cpp
index 06c2d822b1e..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,11 +40,12 @@ 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
+ set_heading(_("File Manager"));
}
@@ -53,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(")");
}
@@ -100,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);
@@ -110,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;
@@ -123,36 +142,56 @@ 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);
}
+ }
}
}
}
item_append(menu_item_type::SEPARATOR);
if (m_warnings.empty() || !missing_mandatory)
- item_append(m_warnings.empty() ? _("Reset Machine") : _("Start Machine"), 0, (void *)1);
-
- custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
+ item_append(m_warnings.empty() ? _("Reset System") : _("Start System"), 0, (void *)1);
}
@@ -160,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 3da27ae7a36..5f4dddc65fc 100644
--- a/src/frontend/mame/ui/filesel.cpp
+++ b/src/frontend/mame/ui/filesel.cpp
@@ -19,8 +19,10 @@
#include "imagedev/floppy.h"
-#include "corestr.h"
-#include "zippath.h"
+#include "uiinput.h"
+
+#include "util/corestr.h"
+#include "util/zippath.h"
#include <cstring>
#include <locale>
@@ -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,102 +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);
}
//-------------------------------------------------
-// compare_file_selector_entries - sorting proc
-// for file selector entries
+// menu_activated - menu has gained focus
//-------------------------------------------------
-int menu_file_selector::compare_entries(const file_selector_entry *e1, const file_selector_entry *e2)
+void menu_file_selector::menu_activated()
{
- int result;
- const char *e1_basename = e1->basename.c_str();
- const char *e2_basename = e2->basename.c_str();
-
- if (e1->type < e2->type)
- {
- result = -1;
- }
- else if (e1->type > e2->type)
- {
- result = 1;
- }
- else
- {
- result = core_stricmp(e1_basename, e2_basename);
- if (result == 0)
- {
- result = strcmp(e1_basename, e2_basename);
- if (result == 0)
- {
- if (e1 < e2)
- result = -1;
- else if (e1 > e2)
- result = 1;
- }
- }
- }
-
- return result;
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
}
@@ -325,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;
@@ -340,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;
@@ -440,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;
}
}
@@ -450,7 +536,8 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
if (m_entrylist.size() > first)
{
// sort the menu entries
- const std::collate<wchar_t> &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(),
@@ -458,7 +545,7 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
{
std::wstring const xstr = wstring_from_utf8(x.basename);
std::wstring const ystr = wstring_from_utf8(y.basename);
- return coll.compare(xstr.data(), xstr.data()+xstr.size(), ystr.data(), ystr.data()+ystr.size()) < 0;
+ return coll.compare(xstr.data(), xstr.data() + xstr.size(), ystr.data(), ystr.data() + ystr.size()) < 0;
});
}
@@ -469,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();
}
@@ -479,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;
}
@@ -519,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)
{
}
@@ -534,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);
}
@@ -541,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));
@@ -556,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 11e5643c2de..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,28 +84,31 @@ 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
- int compare_entries(const file_selector_entry *e1, const file_selector_entry *e2);
file_selector_entry &append_entry(file_selector_entry_type entry_type, const std::string &entry_basename, const std::string &entry_fullpath);
file_selector_entry &append_entry(file_selector_entry_type entry_type, std::string &&entry_basename, std::string &&entry_fullpath);
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);
};
@@ -109,23 +125,27 @@ public:
WRITE_OTHER,
WRITE_DIFF
};
+
+ using handler_function = std::function<void (result result)>;
+
menu_select_rw(
mame_ui_manager &mui,
render_container &container,
bool can_in_place,
- result &result);
+ handler_function &&handler);
virtual ~menu_select_rw() override;
+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 ee7740c76d1..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) {
- fs_meta_data meta;
+ 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,25 +97,62 @@ 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;
+ }
+ });
}
}
+bool menu_control_floppy_image::can_format(const floppy_image_device::fs_info &fs)
+{
+ return !fs.m_manager || fs.m_manager->can_format();
+}
+
void menu_control_floppy_image::menu_activated()
{
switch (m_state) {
case DO_CREATE: {
- std::vector<floppy_image_format_t *> format_array;
- for(floppy_image_format_t *i : fd.get_formats()) {
+ std::vector<const floppy_image_format_t *> format_array;
+ for(const floppy_image_format_t *i : fd.get_formats()) {
if(!i->supports_save())
continue;
if (i->extension_matches(m_current_file.c_str()))
format_array.push_back(i);
}
int ext_match = format_array.size();
- for(floppy_image_format_t *i : fd.get_formats()) {
+ for(const floppy_image_format_t *i : fd.get_formats()) {
if(!i->supports_save())
continue;
if (!i->extension_matches(m_current_file.c_str()))
@@ -124,62 +170,51 @@ void menu_control_floppy_image::menu_activated()
m_state = START_FILE;
menu_activated();
} else {
- const auto &fs = fd.get_create_fs();
+ // get all formatable file systems
+ std::vector<std::reference_wrapper<const floppy_image_device::fs_info>> fs;
+ for (const auto &this_fs : fd.get_fs()) {
+ if (can_format(this_fs))
+ fs.emplace_back(std::ref(this_fs));
+ }
+
output_filename = util::zippath_combine(m_current_directory, m_current_file);
if(fs.size() == 1) {
- create_fs = &fs[0];
+ create_fs = &(fs[0].get());
do_load_create();
stack_pop();
} else {
m_submenu_result.i = -1;
- menu::stack_push<menu_select_floppy_init>(ui(), container(), fs, &m_submenu_result.i);
+ menu::stack_push<menu_select_floppy_init>(ui(), container(), std::move(fs), &m_submenu_result.i);
m_state = SELECT_INIT;
}
}
break;
case SELECT_INIT:
- if(m_submenu_result.i == -1) {
+ // figure out which (if any) create file system was selected
+ create_fs = nullptr;
+ if(m_submenu_result.i >= 0) {
+ int i = 0;
+ for (const auto &this_fs : fd.get_fs()) {
+ if (can_format(this_fs)) {
+ if (i == m_submenu_result.i) {
+ create_fs = &this_fs;
+ break;
+ }
+ i++;
+ }
+ }
+ }
+
+ if(!create_fs) {
m_state = START_FILE;
menu_activated();
} else {
- create_fs = &fd.get_create_fs()[m_submenu_result.i];
do_load_create();
stack_pop();
}
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 bdc1104d0a5..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,15 +29,16 @@ 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;
- floppy_image_format_t *input_format, *output_format;
+ const floppy_image_format_t *input_format, *output_format;
const floppy_image_device::fs_info *create_fs;
std::string input_filename, output_filename;
void do_load_create();
virtual void hook_load(const std::string &filename) override;
+ static bool can_format(const floppy_image_device::fs_info &fs);
};
} // namespace ui
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 1d1e975a143..945c18d32a2 100644
--- a/src/frontend/mame/ui/info.cpp
+++ b/src/frontend/mame/ui/info.cpp
@@ -14,11 +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 "emuopts.h"
+#include "speaker.h"
+#include "util/unicode.h"
+
+#include <locale>
#include <set>
#include <sstream>
#include <type_traits>
@@ -63,7 +70,7 @@ void get_general_warnings(std::ostream &buf, running_machine &machine, machine_f
if (machine.rom_load().warnings() > 0)
{
bad_roms = true;
- buf << _("One or more ROMs/CHDs for this machine are incorrect. The machine may not run correctly.\n");
+ buf << _("One or more ROMs/disk images for this system are incorrect. The system may not run correctly.\n");
}
if (!machine.rom_load().software_load_warnings_message().empty())
{
@@ -76,12 +83,12 @@ void get_general_warnings(std::ostream &buf, running_machine &machine, machine_f
{
if (bad_roms)
buf << '\n';
- buf << _("There are known problems with this machine\n\n");
+ buf << _("There are known problems with this system:\n\n");
}
// add a warning if any ROMs are flagged BAD_DUMP/NO_DUMP
if (machine.rom_load().knownbad() > 0)
- buf << _("One or more ROMs/CHDs for this machine have not been correctly dumped.\n");
+ buf << _("One or more ROMs/disk images for this system have not been correctly dumped.\n");
}
void get_device_warnings(std::ostream &buf, device_t::feature_type unemulated, device_t::feature_type imperfect)
@@ -121,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);
@@ -128,17 +137,35 @@ void get_system_warnings(std::ostream &buf, running_machine &machine, machine_fl
if (flags & ::machine_flags::NO_COCKTAIL)
buf << _("Screen flipping in cocktail mode is not supported.\n");
if (flags & ::machine_flags::REQUIRES_ARTWORK)
- buf << _("This machine requires external artwork files.\n");
+ buf << _("This system requires external artwork files.\n");
+
+ // add the 'BTANB' warnings
if (flags & ::machine_flags::IS_INCOMPLETE)
- buf << _("This machine was never completed. It may exhibit strange behavior or missing elements that are not bugs in the emulation.\n");
+ {
+ 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 machine 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 MACHINE DOESN'T WORK. The emulation for this machine 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 machine cannot be emulated as they require physical interaction or consist of mechanical devices. It is not possible to fully experience this machine.\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))
{
@@ -160,7 +187,7 @@ void get_system_warnings(std::ostream &buf, running_machine &machine, machine_fl
{
// this one works, add a header and display the name of the clone
if (!foundworking)
- util::stream_format(buf, _("\n\nThere are working clones of this machine: %s"), driver.name);
+ util::stream_format(buf, _("\n\nThere are working clones of this system: %s"), driver.name);
else
util::stream_format(buf, _(", %s"), driver.name);
foundworking = true;
@@ -207,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
@@ -234,8 +261,8 @@ machine_static_info::machine_static_info(const ui_options &options, machine_conf
// suppress "requires external artwork" warning when external artwork was loaded
if (config.root_device().has_running_machine())
{
- for (render_target *target = config.root_device().machine().render().first_target(); target != nullptr; target = target->next())
- if (!target->hidden() && target->external_artwork())
+ for (render_target const &target : config.root_device().machine().render().targets())
+ if (!target.hidden() && target.external_artwork())
{
m_flags &= ~::machine_flags::REQUIRES_ARTWORK;
break;
@@ -360,12 +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:
- 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,
- core_filename_extract_base(m_machine.system().type.source()));
+ info_xml_creator::format_sourcefile(m_machine.system().type.source()));
// loop over all CPUs
execute_interface_enumerator execiter(m_machine.root_device());
@@ -392,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));
}
@@ -400,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"));
}
@@ -435,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));
}
@@ -443,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"));
}
@@ -464,13 +498,19 @@ std::string machine_info::game_info_string() const
detail = _("Vector");
else
{
- std::string hz(std::to_string(float(screen.frame_period().as_hz())));
- size_t last = hz.find_last_not_of('0');
- size_t dpos = hz.find_last_of('.');
- hz = hz.substr(0, last + (last != dpos ? 1 : 0));
+ const u32 rate = u32(screen.frame_period().as_hz() * 1'000'000 + 0.5);
+ const bool valid = rate >= 1'000'000;
+ std::string hz(valid ? std::to_string(rate) : "?");
+ if (valid)
+ {
+ size_t dpos = hz.length() - 6;
+ 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);
@@ -514,28 +554,28 @@ menu_game_info::~menu_game_info()
{
}
+void menu_game_info::menu_activated()
+{
+ // screen modes can be reconfigured while the menu isn't displayed, etc.
+ reset_layout();
+}
+
void menu_game_info::populate_text(std::optional<text_layout> &layout, float &width, int &lines)
{
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
@@ -584,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);
}
@@ -608,6 +642,7 @@ void menu_warn_info::handle(event const *ev)
menu_image_info::menu_image_info(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("Media Image Information"));
}
menu_image_info::~menu_image_info()
@@ -619,18 +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()
{
- ui_system_info const &system(system_list::instance().systems()[driver_list::find(machine().system().name)]);
- item_append(system.description, FLAG_DISABLE, nullptr);
- item_append(std::string(), FLAG_DISABLE, nullptr);
-
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;
}
@@ -639,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);
@@ -671,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(std::string(), FLAG_DISABLE, nullptr);
+ 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 b05a1d62639..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 {
@@ -96,11 +99,11 @@ public:
virtual ~menu_game_info() override;
protected:
+ virtual void menu_activated() 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;
};
@@ -114,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;
};
@@ -129,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 4e541a7988c..6aa4826e241 100644
--- a/src/frontend/mame/ui/info_pty.cpp
+++ b/src/frontend/mame/ui/info_pty.cpp
@@ -19,17 +19,15 @@ namespace ui {
menu_pty_info::menu_pty_info(mame_ui_manager &mui, render_container &container) :
menu(mui, container)
{
+ set_heading(_("Pseudo Terminals"));
}
menu_pty_info::~menu_pty_info()
{
}
-void menu_pty_info::populate(float &customtop, float &custombottom)
+void menu_pty_info::populate()
{
- item_append(_("Pseudo terminals"), FLAG_DISABLE, nullptr);
- item_append(std::string(), FLAG_DISABLE, nullptr);
-
for (device_pty_interface &pty : pty_interface_enumerator(machine().root_device()))
{
const char *port_name = pty.device().owner()->tag() + 1;
@@ -41,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 87cdd9329b1..5e4be6502ef 100644
--- a/src/frontend/mame/ui/inifile.cpp
+++ b/src/frontend/mame/ui/inifile.cpp
@@ -16,13 +16,16 @@
#include "language.h"
#include "drivenum.h"
+#include "fileio.h"
#include "softlist_dev.h"
#include "corestr.h"
+#include "path.h"
#include <algorithm>
#include <cstring>
#include <iterator>
+#include <locale>
namespace {
@@ -55,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());
+ }
+ );
}
//-------------------------------------------------
@@ -97,7 +111,7 @@ void inifile_manager::load_ini_category(size_t file, size_t category, std::unord
// initialize category
//-------------------------------------------------
-void inifile_manager::init_category(std::string &&filename, emu_file &file)
+void inifile_manager::init_category(std::string &&filename, util::core_file &file)
{
categoryindex index;
char rbuf[MAX_CHAR_INFO];
@@ -110,12 +124,29 @@ void inifile_manager::init_category(std::string &&filename, emu_file &file)
auto const tail(std::find_if(head, std::end(rbuf), [] (char ch) { return !ch || (']' == ch); }));
name.assign(head, tail);
if ("FOLDER_SETTINGS" != name)
- index.emplace_back(std::move(name), file.tell());
+ {
+ u64 result;
+ if (!file.tell(result))
+ index.emplace_back(std::move(name), result);
+ }
}
}
- 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));
+ }
}
@@ -314,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;
@@ -459,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)
+ bool have_software(false);
+ for (device_image_interface &image_dev : image_interface_enumerator(machine.root_device()))
{
- action(machine.system(), nullptr, nullptr, done);
- }
- else
- {
- 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()
@@ -504,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/inifile.h b/src/frontend/mame/ui/inifile.h
index 68adca3c917..68abaac566a 100644
--- a/src/frontend/mame/ui/inifile.h
+++ b/src/frontend/mame/ui/inifile.h
@@ -46,7 +46,7 @@ private:
// ini file structure
using categoryindex = std::vector<std::pair<std::string, int64_t>>;
- void init_category(std::string &&filename, emu_file &file);
+ void init_category(std::string &&filename, util::core_file &file);
// internal state
ui_options &m_options;
diff --git a/src/frontend/mame/ui/inputdevices.cpp b/src/frontend/mame/ui/inputdevices.cpp
new file mode 100644
index 00000000000..1ee03ad5c81
--- /dev/null
+++ b/src/frontend/mame/ui/inputdevices.cpp
@@ -0,0 +1,310 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/inputdevices.cpp
+
+ Input devices menu.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "inputdevices.h"
+
+#include "inputdev.h"
+
+// FIXME: allow OSD module headers to be included in a less ugly way
+#include "../osd/modules/lib/osdlib.h"
+
+
+namespace ui {
+
+namespace {
+
+class menu_input_device : public menu
+{
+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)"),
+ device.name(),
+ machine().input().device_class(device.devclass()).name(),
+ device.devindex() + 1));
+ }
+
+protected:
+ 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 indicator
+ float const indleft = x + gutter_width();
+ float const indright = x2 - gutter_width();
+ float const indtop = y2 + (tb_border() * 2.0F) + (line_height() * 1.2F);
+ float const indbottom = y2 + (tb_border() * 2.0F) + (line_height() * 1.8F);
+ float const indcentre = (x + x2) * 0.5F;
+ 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_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_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));
+ 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));
+ container().add_line(indcentre, indtop, indcentre, indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+private:
+ virtual void populate() override
+ {
+ 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);
+ if (input)
+ {
+ switch (input->itemclass())
+ {
+ case ITEM_CLASS_ABSOLUTE:
+ case ITEM_CLASS_RELATIVE:
+ m_have_analog = true;
+ break;
+ default:
+ break;
+ }
+ item_append(input->name(), format_value(*input), 0U, input);
+ }
+ }
+
+ item_append(menu_item_type::SEPARATOR);
+
+ set_custom_space(0.0F, (line_height() * (m_have_analog ? 2.0F : 1.0F)) + (tb_border() * 3.0F));
+ }
+
+ 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);
+ 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)
+ {
+ switch (input.itemclass())
+ {
+ default:
+ case ITEM_CLASS_SWITCH:
+ return util::string_format("%d", input.read_as_switch(ITEM_MODIFIER_NONE));
+ case ITEM_CLASS_ABSOLUTE:
+ return util::string_format("%d", input.read_as_absolute(ITEM_MODIFIER_NONE));
+ case ITEM_CLASS_RELATIVE:
+ return util::string_format("%d", input.read_as_relative(ITEM_MODIFIER_NONE));
+ }
+ }
+
+ input_device &m_device;
+ bool m_have_analog;
+};
+
+} // anonymous namespace
+
+
+
+menu_input_devices::menu_input_devices(mame_ui_manager &mui, render_container &container)
+ : menu(mui, container)
+{
+ set_heading(_("menu-inputdev", "Input Devices"));
+}
+
+
+menu_input_devices::~menu_input_devices()
+{
+}
+
+
+void menu_input_devices::populate()
+{
+ // iterate input device classes and devices within each class
+ bool found = false;
+ for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno)
+ {
+ input_class &devclass = machine().input().device_class(classno);
+ if (devclass.enabled())
+ {
+ bool first = true;
+ for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum)
+ {
+ input_device *const device = devclass.device(devnum);
+ if (device)
+ {
+ // add a device class heading
+ found = true;
+ if (first)
+ {
+ first = false;
+ item_append(devclass.name(), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ }
+
+ // add the item for the device itself
+ item_append(util::string_format("%d", device->devindex() + 1), device->name(), 0U, device);
+ }
+ }
+ }
+ }
+
+ // highly unlikely - at least one keyboard or mouse will be enabled in almost all cases
+ if (!found)
+ item_append(_("menu-inputdev", "[no input devices are enabled]"), FLAG_DISABLE, nullptr);
+
+ item_append(menu_item_type::SEPARATOR);
+}
+
+
+bool menu_input_devices::handle(event const *ev)
+{
+ if (!ev || !ev->itemref)
+ return false;
+
+ 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_PREV_GROUP:
+ {
+ 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)
+ {
+ if (candidate->devclass() == group)
+ {
+ target = i;
+ }
+ else if (!found_break)
+ {
+ group = candidate->devclass();
+ found_break = true;
+ target = i;
+ }
+ else
+ {
+ set_selected_index(target);
+ return true;
+ }
+ }
+ if (!i && found_break)
+ {
+ set_selected_index(target);
+ return true;
+ }
+ }
+ }
+ break;
+
+ case IPT_UI_NEXT_GROUP:
+ {
+ 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))
+ {
+ set_selected_index(i);
+ return true;
+ }
+ }
+ }
+ break;
+ }
+
+ return false;
+}
+
+} // namespace ui
diff --git a/src/frontend/mame/ui/inputdevices.h b/src/frontend/mame/ui/inputdevices.h
new file mode 100644
index 00000000000..3d4bfb51e4e
--- /dev/null
+++ b/src/frontend/mame/ui/inputdevices.h
@@ -0,0 +1,34 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/inputdevices.h
+
+ Input devices menu.
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_INPUTDEVICES_H
+#define MAME_FRONTEND_UI_INPUTDEVICES_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+
+namespace ui {
+
+class menu_input_devices : public menu
+{
+public:
+ menu_input_devices(mame_ui_manager &mui, render_container &container);
+ virtual ~menu_input_devices();
+
+private:
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_INPUTDEVICES_H
diff --git a/src/frontend/mame/ui/inputmap.cpp b/src/frontend/mame/ui/inputmap.cpp
index ada0d087505..d8a4d9656f4 100644
--- a/src/frontend/mame/ui/inputmap.cpp
+++ b/src/frontend/mame/ui/inputmap.cpp
@@ -26,13 +26,14 @@ namespace ui {
menu_input_groups::menu_input_groups(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("Input Assignments (general)"));
}
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));
@@ -45,11 +46,18 @@ 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>(ui(), container(), int(uintptr_t(ev->itemref) - 1));
+ {
+ menu::stack_push<menu_input_general>(
+ ui(),
+ container(),
+ int(uintptr_t(ev->itemref) - 1),
+ util::string_format(_("Input Assignments (%1$s)"), ev->item->text()));
+ }
+
+ return false;
}
@@ -58,17 +66,24 @@ void menu_input_groups::handle(event const *ev)
input menu
-------------------------------------------------*/
-menu_input_general::menu_input_general(mame_ui_manager &mui, render_container &container, int _group)
+menu_input_general::menu_input_general(mame_ui_manager &mui, render_container &container, int _group, std::string &&heading)
: menu_input(mui, container)
, group(_group)
{
+ set_heading(std::move(heading));
}
menu_input_general::~menu_input_general()
{
}
-void menu_input_general::populate(float &customtop, float &custombottom)
+void menu_input_general::menu_activated()
+{
+ // scripts can change settings out from under us
+ reset(reset_options::REMEMBER_POSITION);
+}
+
+void menu_input_general::populate()
{
if (data.empty())
{
@@ -78,26 +93,30 @@ void menu_input_general::populate(float &customtop, float &custombottom)
for (const input_type_entry &entry : machine().ioport().types())
{
// add if we match the group and we have a valid name
- if ((entry.group() == group) && entry.name() && entry.name()[0])
+ if (entry.group() == group)
{
- // loop over all sequence types
- for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
+ std::string name = entry.name();
+ if (!name.empty())
{
- // build an entry for the standard sequence
- input_item_data &item(data.emplace_back());
- item.ref = &entry;
- item.seqtype = seqtype;
- item.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqtype);
- item.defseq = &entry.defseq(seqtype);
- item.group = entry.group();
- item.type = ioport_manager::type_is_analog(entry.type()) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
- item.is_optional = false;
- item.name = _("input-name", entry.name());
- item.owner = nullptr;
-
- // stop after one, unless we're analog
- if (item.type == INPUT_TYPE_DIGITAL)
- break;
+ // loop over all sequence types
+ for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
+ {
+ // build an entry for the standard sequence
+ input_item_data &item(data.emplace_back());
+ item.ref = &entry;
+ item.seqtype = seqtype;
+ item.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqtype);
+ item.defseq = &entry.defseq(seqtype);
+ item.group = entry.group();
+ item.type = ioport_manager::type_is_analog(entry.type()) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
+ item.is_optional = false;
+ item.name = name;
+ item.owner = nullptr;
+
+ // stop after one, unless we're analog
+ if (item.type == INPUT_TYPE_DIGITAL)
+ break;
+ }
}
}
}
@@ -112,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);
}
@@ -131,13 +150,22 @@ void menu_input_general::update_input(input_item_data &seqchangeditem)
menu_input_specific::menu_input_specific(mame_ui_manager &mui, render_container &container) : menu_input(mui, container)
{
+ set_heading(_("Input Assignments (this system)"));
}
menu_input_specific::~menu_input_specific()
{
}
-void menu_input_specific::populate(float &customtop, float &custombottom)
+void menu_input_specific::menu_activated()
+{
+ // scripts can change settings out from under us
+ assert(!pollingitem);
+ data.clear();
+ reset(reset_options::REMEMBER_POSITION);
+}
+
+void menu_input_specific::populate()
{
if (data.empty())
{
@@ -165,7 +193,7 @@ void menu_input_specific::populate(float &customtop, float &custombottom)
item.group = machine().ioport().type_group(field.type(), field.player());
item.type = field.is_analog() ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
item.is_optional = field.optional();
- item.name = _("input-name", field.name());
+ item.name = field.name();
item.owner = &field.device();
// stop after one, unless we're analog
@@ -203,7 +231,7 @@ void menu_input_specific::populate(float &customtop, float &custombottom)
return true;
if (!codes2.empty() && (codes1.empty() || codes1[0] > codes2[0]))
return false;
- cmp = strcmp(i1.name, i2.name);
+ cmp = i1.name.compare(i2.name);
if (cmp < 0)
return true;
if (cmp > 0)
@@ -222,9 +250,9 @@ 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(_("This machine has no configurable inputs."), FLAG_DISABLE, nullptr);
+ item_append(_("[no assignable inputs are enabled]"), FLAG_DISABLE, nullptr);
item_append(menu_item_type::SEPARATOR);
}
@@ -270,12 +298,6 @@ menu_input::~menu_input()
{
}
-void menu_input::menu_activated()
-{
- // scripts can change settings out from under us
- reset(reset_options::REMEMBER_POSITION);
-}
-
/*-------------------------------------------------
toggle_none_default - toggle between "NONE"
@@ -290,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)
{
@@ -298,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
{
@@ -315,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)
{
@@ -327,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
{
@@ -338,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)
@@ -363,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
{
@@ -394,10 +417,16 @@ void menu_input::handle(event const *ev)
{
// entered invalid sequence - abandon change
invalidate = true;
- errormsg = _("Invalid sequence entered");
+ errormsg = _("Invalid combination entered");
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)
@@ -494,6 +523,7 @@ void menu_input::handle(event const *ev)
}
record_next = false;
lastitem = &item;
+ redraw = true;
}
// flip between set and append
@@ -509,6 +539,7 @@ void menu_input::handle(event const *ev)
{
record_next = !record_next;
}
+ redraw = true;
}
}
@@ -524,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;
}
@@ -533,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 };
@@ -587,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 841382daf21..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;
};
@@ -54,7 +54,7 @@ protected:
input_seq_type seqtype = SEQ_TYPE_INVALID; // sequence type
input_seq seq; // copy of the live sequence
const input_seq * defseq = nullptr; // pointer to the default sequence
- const char * name = nullptr; // pointer to the base name of the item
+ std::string name; // base name of the item
const device_t * owner = nullptr; // pointer to the owner of the item
ioport_group group = IPG_INVALID; // group type
uint8_t type = 0U; // type of port
@@ -64,9 +64,10 @@ protected:
menu_input(mame_ui_manager &mui, render_container &container);
- virtual void menu_activated() 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 x1, float y1, float x2, float y2) override;
- void populate_sorted(float &customtop, float &custombottom);
+ void populate_sorted();
void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq);
data_vector data;
@@ -83,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;
};
@@ -92,11 +92,14 @@ private:
class menu_input_general : public menu_input
{
public:
- menu_input_general(mame_ui_manager &mui, render_container &container, int group);
+ menu_input_general(mame_ui_manager &mui, render_container &container, int group, std::string &&heading);
virtual ~menu_input_general() override;
+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;
@@ -109,8 +112,11 @@ public:
menu_input_specific(mame_ui_manager &mui, render_container &container);
virtual ~menu_input_specific() override;
+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
new file mode 100644
index 00000000000..48db7698e71
--- /dev/null
+++ b/src/frontend/mame/ui/inputopts.cpp
@@ -0,0 +1,144 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/inputopts.cpp
+
+ Input options submenu.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ui/inputopts.h"
+
+#include "ui/analogipt.h"
+#include "ui/inputdevices.h"
+#include "ui/inputmap.h"
+#include "ui/inputtoggle.h"
+#include "ui/keyboard.h"
+
+#include "natkeyboard.h"
+
+
+namespace ui {
+
+namespace {
+
+enum : unsigned
+{
+ INPUTMAP_GENERAL = 1,
+ INPUTMAP_MACHINE,
+ ANALOG,
+ KEYBOARD,
+ TOGGLES,
+ INPUTDEV
+};
+
+
+void scan_inputs(running_machine &machine, bool &inputmap, bool &analog, bool &toggle)
+{
+ inputmap = analog = toggle = false;
+ for (auto &port : machine.ioport().ports())
+ {
+ for (ioport_field &field : port.second->fields())
+ {
+ if (field.enabled())
+ {
+ switch (field.type_class())
+ {
+ case INPUT_CLASS_CONTROLLER:
+ case INPUT_CLASS_MISC:
+ case INPUT_CLASS_KEYBOARD:
+ inputmap = true;
+ if (field.live().toggle)
+ toggle = true;
+ break;
+ default:
+ break;
+ }
+ if (field.is_analog())
+ analog = true;
+
+ if (inputmap && analog && toggle)
+ return;
+ }
+ }
+ }
+}
+
+} // anonymous namespace
+
+
+menu_input_options::menu_input_options(mame_ui_manager &mui, render_container &container)
+ : menu(mui, container)
+{
+ set_heading(_("menu-inputopts", "Input Settings"));
+}
+
+
+menu_input_options::~menu_input_options()
+{
+}
+
+
+void menu_input_options::menu_activated()
+{
+ reset(reset_options::REMEMBER_REF);
+}
+
+
+void menu_input_options::populate()
+{
+ bool inputmap, analog, toggle;
+ scan_inputs(machine(), inputmap, analog, toggle);
+
+ // system-specific stuff
+ if (inputmap)
+ item_append(_("menu-inputopts", "Input Assignments (this system)"), 0, (void *)INPUTMAP_MACHINE);
+ if (analog)
+ item_append(_("menu-inputopts", "Analog Input Adjustments"), 0, (void *)ANALOG);
+ if (machine().natkeyboard().keyboard_count())
+ item_append(_("menu-inputopts", "Keyboard Selection"), 0, (void *)KEYBOARD);
+ if (toggle)
+ item_append(_("menu-inputopts", "Toggle Inputs"), 0, (void *)TOGGLES);
+ if (inputmap || analog || machine().natkeyboard().keyboard_count() || toggle)
+ item_append(menu_item_type::SEPARATOR);
+
+ // general stuff
+ item_append(_("menu-inputopts", "Input Assignments (general)"), 0, (void *)INPUTMAP_GENERAL);
+ item_append(_("menu-inputopts", "Input Devices"), 0, (void *)INPUTDEV);
+ item_append(menu_item_type::SEPARATOR);
+}
+
+
+bool menu_input_options::handle(event const *ev)
+{
+ if (ev && (IPT_UI_SELECT == ev->iptkey))
+ {
+ switch (uintptr_t(ev->itemref))
+ {
+ case INPUTMAP_GENERAL:
+ stack_push<menu_input_groups>(ui(), container());
+ break;
+ case INPUTMAP_MACHINE:
+ stack_push<menu_input_specific>(ui(), container());
+ break;
+ case ANALOG:
+ stack_push<menu_analog>(ui(), container());
+ break;
+ case KEYBOARD:
+ stack_push<menu_keyboard_mode>(ui(), container());
+ break;
+ case TOGGLES:
+ stack_push<menu_input_toggles>(ui(), container());
+ break;
+ case INPUTDEV:
+ stack_push<menu_input_devices>(ui(), container());
+ break;
+ }
+ }
+
+ return false;
+}
+
+} // namespace ui
diff --git a/src/frontend/mame/ui/inputopts.h b/src/frontend/mame/ui/inputopts.h
new file mode 100644
index 00000000000..bf6afebabf5
--- /dev/null
+++ b/src/frontend/mame/ui/inputopts.h
@@ -0,0 +1,37 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/inputopts.h
+
+ Input options submenu.
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_INPUTOPTS_H
+#define MAME_FRONTEND_UI_INPUTOPTS_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+
+namespace ui {
+
+class menu_input_options : public menu
+{
+public:
+ menu_input_options(mame_ui_manager &mui, render_container &container);
+ virtual ~menu_input_options();
+
+protected:
+ virtual void menu_activated() override;
+
+private:
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_INPUTOPTS_H
diff --git a/src/frontend/mame/ui/inputtoggle.cpp b/src/frontend/mame/ui/inputtoggle.cpp
new file mode 100644
index 00000000000..7a307c01c66
--- /dev/null
+++ b/src/frontend/mame/ui/inputtoggle.cpp
@@ -0,0 +1,220 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/inputtoggle.cpp
+
+ Toggle inputs menu.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ui/inputtoggle.h"
+
+#include <iterator>
+
+
+namespace ui {
+
+menu_input_toggles::menu_input_toggles(mame_ui_manager &mui, render_container &container)
+ : menu(mui, container)
+{
+ set_heading(_("menu-inputtoggle", "Toggle Inputs"));
+}
+
+
+menu_input_toggles::~menu_input_toggles()
+{
+}
+
+
+void menu_input_toggles::menu_activated()
+{
+ // enabled inputs and state of inputs can change while menu is inactive
+ reset(reset_options::REMEMBER_REF);
+}
+
+
+void menu_input_toggles::populate()
+{
+ // find toggle fields
+ if (m_fields.empty())
+ {
+ for (auto &port : machine().ioport().ports())
+ {
+ for (ioport_field &field : port.second->fields())
+ {
+ switch (field.type_class())
+ {
+ case INPUT_CLASS_CONTROLLER:
+ case INPUT_CLASS_MISC:
+ case INPUT_CLASS_KEYBOARD:
+ if (field.live().toggle)
+ m_fields.emplace_back(field);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // create corresponding items for enabled fields
+ device_t *prev_owner = nullptr;
+ for (auto &field : m_fields)
+ {
+ if (field.get().enabled())
+ {
+ // add a device heading if necessary
+ if (&field.get().device() != prev_owner)
+ {
+ prev_owner = &field.get().device();
+ if (prev_owner->owner())
+ item_append(string_format(_("%1$s [root%2$s]"), prev_owner->type().fullname(), prev_owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ else
+ item_append(string_format(_("[root%1$s]"), prev_owner->tag()), FLAG_UI_HEADING | FLAG_DISABLE, nullptr);
+ }
+
+ // choose the display name for the value
+ char const *setting;
+ u32 flags = 0U;
+ if (!field.get().settings().empty())
+ {
+ setting = field.get().setting_name();
+ if (field.get().has_previous_setting())
+ flags |= FLAG_LEFT_ARROW;
+ if (field.get().has_next_setting())
+ flags |= FLAG_RIGHT_ARROW;
+ }
+ else if (field.get().defvalue() == field.get().live().value)
+ {
+ setting = _("Off");
+ flags = FLAG_RIGHT_ARROW;
+ }
+ else
+ {
+ setting = _("On");
+ flags = FLAG_LEFT_ARROW;
+ }
+
+ // actually create the item
+ item_append(field.get().name(), setting, flags, &field);
+ }
+ }
+
+ // display a message if there are toggle inputs enabled
+ if (!prev_owner)
+ item_append(_("menu-inputtoggle", "[no toggle inputs are enabled]"), FLAG_DISABLE, nullptr);
+
+ item_append(menu_item_type::SEPARATOR);
+}
+
+
+bool menu_input_toggles::handle(event const *ev)
+{
+ 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)
+ {
+ 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)
+ {
+ 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();
+ 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)
+ {
+ if (!found_break)
+ {
+ if (m_fields[--current].get().enabled())
+ {
+ device_t const *prev = &m_fields[current].get().device();
+ if (prev != dev)
+ {
+ 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);
+ 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;
+
+ 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)
+ {
+ if (m_fields[current].get().enabled() && (&m_fields[current].get().device() != dev))
+ {
+ 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);
+
+ return false;
+}
+
+} // namespace ui
diff --git a/src/frontend/mame/ui/inputtoggle.h b/src/frontend/mame/ui/inputtoggle.h
new file mode 100644
index 00000000000..676be71e58d
--- /dev/null
+++ b/src/frontend/mame/ui/inputtoggle.h
@@ -0,0 +1,42 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ui/inputtoggle.h
+
+ Toggle inputs menu.
+
+***************************************************************************/
+
+#ifndef MAME_FRONTEND_UI_INPUTTOGGLE_H
+#define MAME_FRONTEND_UI_INPUTTOGGLE_H
+
+#pragma once
+
+#include "ui/menu.h"
+
+#include <functional>
+#include <vector>
+
+
+namespace ui {
+
+class menu_input_toggles : public menu
+{
+public:
+ menu_input_toggles(mame_ui_manager &mui, render_container &container);
+ virtual ~menu_input_toggles();
+
+protected:
+ virtual void menu_activated() override;
+
+private:
+ virtual void populate() override;
+ virtual bool handle(event const *ev) override;
+
+ std::vector<std::reference_wrapper<ioport_field> > m_fields;
+};
+
+} // namespace ui
+
+#endif // MAME_FRONTEND_UI_INPUTTOGGLE_H
diff --git a/src/frontend/mame/ui/keyboard.cpp b/src/frontend/mame/ui/keyboard.cpp
index cbdc3324212..9eaeb5c94ee 100644
--- a/src/frontend/mame/ui/keyboard.cpp
+++ b/src/frontend/mame/ui/keyboard.cpp
@@ -26,6 +26,7 @@ constexpr uintptr_t ITEM_KBDEV_FIRST = 0x00000200;
menu_keyboard_mode::menu_keyboard_mode(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("menu-keyboard", "Keyboard Selection"));
}
void menu_keyboard_mode::menu_activated()
@@ -34,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());
@@ -42,8 +43,8 @@ void menu_keyboard_mode::populate(float &customtop, float &custombottom)
{
bool const natmode(natkbd.in_use());
item_append(
- _("Keyboard Mode"),
- natmode ? _("Natural") : _("Emulated"),
+ _("menu-keyboard", "Keyboard Mode"),
+ natmode ? _("menu-keyboard", "Natural") : _("menu-keyboard", "Emulated"),
natmode ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW,
reinterpret_cast<void *>(ITEM_KBMODE));
item_append(menu_item_type::SEPARATOR);
@@ -70,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 ? _("Natural") : _("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 0de7d7ca4d3..22a2c01f6c1 100644
--- a/src/frontend/mame/ui/mainmenu.cpp
+++ b/src/frontend/mame/ui/mainmenu.cpp
@@ -12,7 +12,6 @@
#include "ui/mainmenu.h"
#include "ui/about.h"
-#include "ui/analogipt.h"
#include "ui/barcode.h"
#include "ui/cheatopt.h"
#include "ui/confswitch.h"
@@ -21,8 +20,7 @@
#include "ui/info.h"
#include "ui/info_pty.h"
#include "ui/inifile.h"
-#include "ui/inputmap.h"
-#include "ui/keyboard.h"
+#include "ui/inputopts.h"
#include "ui/miscmenu.h"
#include "ui/pluginopt.h"
#include "ui/selgame.h"
@@ -35,23 +33,21 @@
#include "mame.h"
#include "luaengine.h"
-#include "machine/bcreader.h"
#include "imagedev/cassette.h"
+#include "machine/bcreader.h"
#include "crsshair.h"
+#include "dinetwork.h"
#include "dipty.h"
#include "emuopts.h"
-#include "natkeyboard.h"
namespace ui {
enum : unsigned {
- INPUT_GROUPS,
- INPUT_SPECIFIC,
+ INPUT_OPTIONS,
SETTINGS_DIP_SWITCHES,
SETTINGS_DRIVER_CONFIG,
- ANALOG,
BOOKKEEPING,
GAME_INFO,
WARN_INFO,
@@ -60,10 +56,8 @@ enum : unsigned {
TAPE_CONTROL,
SLOT_DEVICES,
NETWORK_DEVICES,
- KEYBOARD_MODE,
SLIDERS,
VIDEO_TARGETS,
- VIDEO_OPTIONS,
CROSSHAIR,
CHEAT,
PLUGINS,
@@ -71,8 +65,7 @@ enum : unsigned {
BARCODE_READ,
PTY_INFO,
EXTERNAL_DATS,
- ADD_FAVORITE,
- REMOVE_FAVORITE,
+ FAVORITE,
ABOUT,
QUIT_GAME,
DISMISS,
@@ -114,103 +107,98 @@ 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();
- item_append(_("Input (general)"), 0, (void *)INPUT_GROUPS);
+ item_append(_("menu-main", "Input Settings"), 0, (void *)INPUT_OPTIONS);
- item_append(_("Input (this Machine)"), 0, (void *)INPUT_SPECIFIC);
-
- if (ui().machine_info().has_analog())
- item_append(_("Analog Controls"), 0, (void *)ANALOG);
if (ui().machine_info().has_dips())
- item_append(_("DIP Switches"), 0, (void *)SETTINGS_DIP_SWITCHES);
+ item_append(_("menu-main", "DIP Switches"), 0, (void *)SETTINGS_DIP_SWITCHES);
if (ui().machine_info().has_configs())
- item_append(_("Machine Configuration"), 0, (void *)SETTINGS_DRIVER_CONFIG);
+ item_append(_("menu-main", "Machine Configuration"), 0, (void *)SETTINGS_DRIVER_CONFIG);
- item_append(_("Bookkeeping Info"), 0, (void *)BOOKKEEPING);
+ item_append(_("menu-main", "Bookkeeping Info"), 0, (void *)BOOKKEEPING);
- item_append(_("Machine Information"), 0, (void *)GAME_INFO);
+ item_append(_("menu-main", "System Information"), 0, (void *)GAME_INFO);
if (ui().found_machine_warnings())
- item_append(_("Warning Information"), 0, (void *)WARN_INFO);
+ 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(_("Image Information"), 0, (void *)IMAGE_MENU_IMAGE_INFO);
-
- item_append(_("File Manager"), 0, (void *)IMAGE_MENU_FILE_MANAGER);
+ 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(_("Tape Control"), 0, (void *)TAPE_CONTROL);
+ item_append(_("menu-main", "Tape Control"), 0, (void *)TAPE_CONTROL);
if (pty_interface_enumerator(machine().root_device()).first() != nullptr)
- item_append(_("Pseudo terminals"), 0, (void *)PTY_INFO);
+ item_append(_("menu-main", "Pseudo Terminals"), 0, (void *)PTY_INFO);
if (ui().machine_info().has_bioses())
- item_append(_("BIOS Selection"), 0, (void *)BIOS_SELECTION);
+ item_append(_("menu-main", "BIOS Selection"), 0, (void *)BIOS_SELECTION);
if (slot_interface_enumerator(machine().root_device()).first() != nullptr)
- item_append(_("Slot Devices"), 0, (void *)SLOT_DEVICES);
+ item_append(_("menu-main", "Slot Devices"), 0, (void *)SLOT_DEVICES);
if (barcode_reader_device_enumerator(machine().root_device()).first() != nullptr)
- item_append(_("Barcode Reader"), 0, (void *)BARCODE_READ);
+ item_append(_("menu-main", "Barcode Reader"), 0, (void *)BARCODE_READ);
if (network_interface_enumerator(machine().root_device()).first() != nullptr)
- item_append(_("Network Devices"), 0, (void*)NETWORK_DEVICES);
-
- if (machine().natkeyboard().keyboard_count())
- item_append(_("Keyboard Mode"), 0, (void *)KEYBOARD_MODE);
+ item_append(_("menu-main", "Network Devices"), 0, (void*)NETWORK_DEVICES);
- item_append(_("Slider Controls"), 0, (void *)SLIDERS);
+ item_append(_("menu-main", "Slider Controls"), 0, (void *)SLIDERS);
- item_append(_("Video Options"), 0, (void *)VIDEO_TARGETS);
+ item_append(_("menu-main", "Video Options"), 0, (void *)VIDEO_TARGETS);
if (machine().crosshair().get_usage())
- item_append(_("Crosshair Options"), 0, (void *)CROSSHAIR);
+ item_append(_("menu-main", "Crosshair Options"), 0, (void *)CROSSHAIR);
if (machine().options().cheat())
- item_append(_("Cheat"), 0, (void *)CHEAT);
+ item_append(_("menu-main", "Cheat Options"), 0, (void *)CHEAT);
if (machine_phase::RESET <= m_phase)
{
if (machine().options().plugins() && !mame_machine_manager::instance()->lua()->get_menu().empty())
- item_append(_("Plugin Options"), 0, (void *)PLUGINS);
+ item_append(_("menu-main", "Plugin Options"), 0, (void *)PLUGINS);
if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", "", true))
- item_append(_("External DAT View"), 0, (void *)EXTERNAL_DATS);
+ item_append(_("menu-main", "External DAT View"), 0, (void *)EXTERNAL_DATS);
}
item_append(menu_item_type::SEPARATOR);
if (!mame_machine_manager::instance()->favorite().is_favorite(machine()))
- item_append(_("Add To Favorites"), 0, (void *)ADD_FAVORITE);
+ item_append(_("menu-main", "Add To Favorites"), 0, (void *)FAVORITE);
else
- item_append(_("Remove From Favorites"), 0, (void *)REMOVE_FAVORITE);
+ item_append(_("menu-main", "Remove From Favorites"), 0, (void *)FAVORITE);
item_append(menu_item_type::SEPARATOR);
- item_append(string_format(_("About %s"), emulator_info::get_appname()), 0, (void *)ABOUT);
+ item_append(string_format(_("menu-main", "About %1$s"), emulator_info::get_appname()), 0, (void *)ABOUT);
item_append(menu_item_type::SEPARATOR);
-// item_append(_("Quit from Machine"), 0, (void *)QUIT_GAME);
+// item_append(_("menu-main", "Quit from System"), 0, (void *)QUIT_GAME);
if (machine_phase::INIT == m_phase)
{
- item_append(_("Start Machine"), 0, (void *)DISMISS);
+ item_append(_("menu-main", "Start System"), 0, (void *)DISMISS);
}
else
{
- item_append(_("Select New Machine"), 0, (void *)SELECT_GAME);
- item_append(_("Return to Machine"), 0, (void *)DISMISS);
+ item_append(_("menu-main", "Select New System"), 0, (void *)SELECT_GAME);
+ item_append(_("menu-main", "Close Menu"), 0, (void *)DISMISS);
}
}
@@ -219,19 +207,15 @@ 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))
{
switch (uintptr_t(ev->itemref))
{
- case INPUT_GROUPS:
- menu::stack_push<menu_input_groups>(ui(), container());
- break;
-
- case INPUT_SPECIFIC:
- menu::stack_push<menu_input_specific>(ui(), container());
+ case INPUT_OPTIONS:
+ menu::stack_push<menu_input_options>(ui(), container());
break;
case SETTINGS_DIP_SWITCHES:
@@ -242,10 +226,6 @@ void menu_main::handle(event const *ev)
menu::stack_push<menu_settings_machine_config>(ui(), container());
break;
- case ANALOG:
- menu::stack_push<menu_analog>(ui(), container());
- break;
-
case BOOKKEEPING:
menu::stack_push<menu_bookkeeping>(ui(), container());
break;
@@ -282,10 +262,6 @@ void menu_main::handle(event const *ev)
menu::stack_push<menu_network_devices>(ui(), container());
break;
- case KEYBOARD_MODE:
- menu::stack_push<menu_keyboard_mode>(ui(), container());
- break;
-
case SLIDERS:
menu::stack_push<menu_sliders>(ui(), container(), false);
break;
@@ -294,10 +270,6 @@ void menu_main::handle(event const *ev)
menu::stack_push<menu_video_targets>(ui(), container());
break;
- case VIDEO_OPTIONS:
- menu::stack_push<menu_video_options>(ui(), container(), *machine().render().first_target(), false);
- break;
-
case CROSSHAIR:
menu::stack_push<menu_crosshair>(ui(), container());
break;
@@ -333,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();
@@ -350,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 4dc265f5d14..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"
@@ -21,13 +20,16 @@
#include "corestr.h"
#include "drivenum.h"
+#include "fileio.h"
#include "rendutil.h"
#include "uiinput.h"
-#include <algorithm>
+#include "osdepend.h"
+
#include <cassert>
-#include <cmath>
-#include <utility>
+#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();
+ }
}
@@ -235,23 +385,43 @@ menu::menu(mame_ui_manager &mui, render_container &container)
, m_ui(mui)
, m_container(container)
, m_parent()
+ , 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_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);
@@ -276,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;
@@ -307,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;
}
//-------------------------------------------------
@@ -318,8 +495,10 @@ 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);
+
// allocate a new item and populate it
menu_item pitem(type, ref, flags);
pitem.set_text(std::move(text));
@@ -342,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));
}
@@ -350,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;
}
@@ -440,31 +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
- 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 = lr_arrow_width * 1.3f;
- float const lr_border = ui().box_lr_border() * aspect;
+ float const max_width = 1.0F - ((lr_border() + (x_aspect() * UI_LINE_WIDTH)) * 2.0F);
if (is_special_main_menu())
draw_background();
@@ -475,48 +620,77 @@ 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();
// track the maximum
- if (total_width > visible_width)
- visible_width = total_width;
+ 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(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 visible_extra_menu_height = m_customtop + m_custombottom;
+ 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
- visible_width += 0.01f;
- visible_main_menu_height += 0.01f;
+ visible_width += 0.01F;
+ visible_main_menu_height += 0.01F;
// if we are too wide or too tall, clamp it down
- visible_width = std::min(visible_width, 1.0f - ((lr_border + (aspect * UI_LINE_WIDTH)) * 2.0f));
+ 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) + m_customtop;
+ float const visible_left = (1.0F - visible_width) * 0.5F;
+ 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)
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ {
+ if (heading_layout)
+ {
+ ui().draw_outlined_box(
+ container(),
+ x1, y1 - top_extra_menu_height,
+ x2, y1 - m_customtop - tb_border(),
+ UI_GREEN_COLOR);
+ heading_layout->emit(container(), (1.0F - heading_layout->width()) * 0.5F, y1 - top_extra_menu_height + tb_border());
+ }
+
+ ui().draw_outlined_box(
+ container(),
+ x1, y1,
+ x2, y2,
+ ui().colors().background_color());
+ }
if ((m_selected >= (top_line + m_visible_lines)) || (m_selected < (top_line + 1)))
top_line = m_selected - (m_visible_lines / 2);
@@ -528,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;
@@ -560,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 (uparrow || downarrow)
{
- // if we're on the top line, display the up 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);
- }
- else if (downarrow)
- {
- // if we're on the bottom line, display the 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,
- 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(),
@@ -638,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
{
@@ -653,19 +845,20 @@ 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(),
- effective_left + effective_width - subitem_width, line_y0 + (UI_LINE_WIDTH * 2.0f),
- effective_left + effective_width, line_y1 - (UI_LINE_WIDTH * 2.0f),
+ effective_left + effective_width - subitem_width, line_y0 + (UI_LINE_WIDTH * 2.0F),
+ effective_left + effective_width, line_y1 - (UI_LINE_WIDTH * 2.0F),
color);
}
else
@@ -673,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))
@@ -684,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
@@ -700,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);
+ }
}
}
}
@@ -736,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);
+ 0, 0, visible_width * 0.75F,
+ 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(
@@ -771,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();
-//-------------------------------------------------
-// map_mouse - map mouse pointer location to menu
-// coordinates
-//-------------------------------------------------
+ // 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);
-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;
}
@@ -816,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;
@@ -826,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)
+ auto const [key, redraw] = handle_pointer_update(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- if (flags & PROCESS_CUSTOM_NAV)
- {
- ev.iptkey = IPT_UI_PAGE_DOWN;
- stop = true;
- }
- else
- {
- m_selected += m_visible_lines - 2 + is_first_selected();
- if (m_selected > m_items.size() - 1)
- m_selected = m_items.size() - 1;
- top_line += m_visible_lines - 2;
- }
- }
- else if (m_hover == HOVER_UI_LEFT)
- {
- ev.iptkey = IPT_UI_LEFT;
+ ev.iptkey = key;
stop = true;
}
- else if (m_hover == HOVER_UI_RIGHT)
+ }
+ break;
+
+ // pointer left the normal way, possibly releasing buttons
+ case ui_event::type::POINTER_LEAVE:
+ {
+ auto const [key, redraw] = handle_pointer_leave(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_RIGHT;
+ ev.iptkey = key;
stop = true;
}
}
break;
- // if we are hovering over a valid item, fake a UI_SELECT with a double-click
- case ui_event::type::MOUSE_DOUBLE_CLICK:
- if (!(flags & PROCESS_ONLYCHAR) && m_hover >= 0 && m_hover < m_items.size())
+ // pointer left in some abnormal way - cancel any associated actions
+ case ui_event::type::POINTER_ABORT:
{
- m_selected = m_hover;
- ev.iptkey = IPT_UI_SELECT;
- if (is_last_selected() && m_needs_prev_menu_item)
+ auto const [key, redraw] = handle_pointer_abort(flags, local_menu_event);
+ need_update = need_update || redraw;
+ if (IPT_INVALID != key)
{
- ev.iptkey = IPT_UI_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)
- {
- if (is_first_selected())
- {
- select_last_item();
- }
- else
- {
- m_selected -= local_menu_event.num_lines;
- validate_selection(-1);
- }
- top_line -= (m_selected <= top_line && top_line != 0);
- if (m_selected <= top_line && m_visible_items != m_visible_lines)
- top_line -= local_menu_event.num_lines;
- }
- else
+ // 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_last_selected())
+ if (lines > 0)
{
- select_first_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 + 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
@@ -946,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);
}
@@ -954,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
@@ -1010,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())
{
@@ -1033,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
@@ -1040,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())
{
@@ -1054,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
@@ -1106,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)
{
@@ -1134,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()
+{
}
@@ -1181,23 +1904,182 @@ void menu::validate_selection(int scandir)
}
+//-------------------------------------------------
+// activate_menu - handle becoming top of the
+// menu stack
+//-------------------------------------------------
-/***************************************************************************
- MENU STACK MANAGEMENT
-***************************************************************************/
+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
+//-------------------------------------------------
-void menu::do_handle()
+bool menu::check_metrics()
{
- if (m_items.empty())
+ 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(m_customtop, m_custombottom);
+ 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
+***************************************************************************/
+
+bool menu::do_handle()
+{
+ 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)))
+ {
+ // read events
+ if (handle_events(flags, *result))
+ need_update = true;
+
+ switch (m_pointer_state)
+ {
+ 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;
+ }
+ }
+
+ // 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];
}
- handle(process());
+ 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;
}
@@ -1221,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
//-------------------------------------------------
@@ -1248,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
@@ -1259,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);
@@ -1271,7 +2163,7 @@ void menu::draw_background()
{
// draw background image if available
if (ui().options().use_background_image() && m_global_state.bgrnd_bitmap() && m_global_state.bgrnd_bitmap()->valid())
- container().add_quad(0.0f, 0.0f, 1.0f, 1.0f, rgb_t::white(), m_global_state.bgrnd_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_quad(0.0F, 0.0F, 1.0F, 1.0F, rgb_t::white(), m_global_state.bgrnd_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
@@ -1283,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;
+ 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 6b137a99631..14031fb2c08 100644
--- a/src/frontend/mame/ui/menu.h
+++ b/src/frontend/mame/ui/menu.h
@@ -19,14 +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 {
/***************************************************************************
@@ -49,14 +64,27 @@ public:
virtual ~menu();
+ // setting menu heading
+ template <typename... T>
+ void set_heading(T &&... args)
+ {
+ if (!m_heading)
+ m_heading.emplace(std::forward<T>(args)...);
+ else
+ m_heading->assign(std::forward<T>(args)...);
+ }
+
// 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(); }
@@ -87,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;
@@ -117,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);
@@ -166,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); }
@@ -181,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(); }
@@ -202,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);
}
}
@@ -222,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(
@@ -236,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)
{
@@ -289,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:
@@ -314,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
@@ -335,26 +489,37 @@ 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;
-
- // 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)); }
+ 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)); }
static global_state &get_global_state(mame_ui_manager &ui);
@@ -369,28 +534,49 @@ private:
render_container &m_container; // render_container we render to
std::unique_ptr<menu> m_parent; // pointer to parent menu in the stack
+ std::optional<std::string> m_heading; // menu heading
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 0d052562fa0..df5754a380e 100644
--- a/src/frontend/mame/ui/miscmenu.cpp
+++ b/src/frontend/mame/ui/miscmenu.cpp
@@ -23,16 +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 {
@@ -48,9 +51,10 @@ namespace ui {
menu_bios_selection::menu_bios_selection(mame_ui_manager &mui, render_container &container) : menu(mui, 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()))
@@ -69,7 +73,7 @@ void menu_bios_selection::populate(float &customtop, float &custombottom)
val = rom->hashdata;
}
if (val)
- item_append(!parent ? "driver" : (device.tag() + 1), val, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&device);
+ item_append(!parent ? _("System") : (device.tag() + 1), val, FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, (void *)&device);
}
}
}
@@ -86,63 +90,71 @@ 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;
+ if (!ev || !ev->itemref)
+ return false;
- switch (ev->iptkey)
- {
- // reset to default
- case IPT_UI_SELECT:
- bios_val = dev->default_bios();
- break;
-
- // 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;
}
menu_network_devices::menu_network_devices(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("Network Devices"));
}
menu_network_devices::~menu_network_devices()
@@ -154,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);
@@ -179,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)
{
- if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
+ return false;
+ }
+ else 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;
}
}
@@ -226,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();
@@ -263,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);
+ }
}
@@ -285,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)
@@ -348,10 +384,10 @@ void menu_crosshair::handle(event const *ev)
{
std::vector<std::string> sel;
sel.reserve(m_pics.size() + 1);
- sel.push_back("DEFAULT");
+ sel.push_back(_("menu-crosshair", "[built-in]"));
std::copy(m_pics.begin(), m_pics.end(), std::back_inserter(sel));
menu::stack_push<menu_selector>(
- ui(), container(), std::move(sel), data.cur,
+ ui(), container(), std::string(ev->item->text()), std::move(sel), data.cur,
[this, &data] (int selection)
{
if (!selection)
@@ -378,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;
}
@@ -389,9 +428,10 @@ void menu_crosshair::handle(event const *ev)
menu_crosshair::menu_crosshair(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading(_("menu-crosshair", "Crosshair Options"));
}
-void menu_crosshair::populate(float &customtop, float &custombottom)
+void menu_crosshair::populate()
{
if (m_data.empty())
{
@@ -441,14 +481,25 @@ 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
- static char const *const vis_text[] = { "Off", "On", "Auto" };
+ static char const *const vis_text[] = {
+ N_p("menu-crosshair", "Never"),
+ N_p("menu-crosshair", "Always"),
+ N_p("menu-crosshair", "When moved") };
bool use_auto = false;
for (crosshair_item_data &data : m_data)
@@ -471,7 +522,11 @@ void menu_crosshair::populate(float &customtop, float &custombottom)
flags |= FLAG_RIGHT_ARROW;
// add CROSSHAIR_ITEM_VIS menu */
- item_append(util::string_format(_("P%d Visibility"), data.player + 1), vis_text[data.crosshair->mode()], flags, &data);
+ item_append(
+ util::string_format(_("menu-crosshair", "P%1$d Visibility"), data.player + 1),
+ _("menu-crosshair", vis_text[data.crosshair->mode()]),
+ flags,
+ &data);
}
break;
@@ -525,7 +580,12 @@ void menu_crosshair::populate(float &customtop, float &custombottom)
flags |= FLAG_LEFT_ARROW;
// add CROSSHAIR_ITEM_PIC menu
- item_append(util::string_format(_("P%d Crosshair"), data.player + 1), using_default ? "DEFAULT" : data.crosshair->bitmap_name(), flags, &data);
+ item_append(
+ util::string_format(_("menu-crosshair", "P%1$d Crosshair"), data.player + 1),
+ using_default ? _("menu-crosshair", "[built-in]") : data.crosshair->bitmap_name(),
+ flags,
+ &data);
+ item_append(menu_item_type::SEPARATOR);
}
break;
@@ -542,18 +602,16 @@ void menu_crosshair::populate(float &customtop, float &custombottom)
flags |= FLAG_RIGHT_ARROW;
// add CROSSHAIR_ITEM_AUTO_TIME menu
- item_append(_("Visible Delay"), util::string_format("%d", data.cur), flags, &data);
- }
- else
- {
- // leave a blank filler line when not in auto time so size does not rescale
- //item_append("", "", nullptr, nullptr);
+ item_append(
+ _("menu-crosshair", "Auto-Hide Delay"),
+ util::string_format(_("menu-crosshair", "%1$d s"), data.cur),
+ flags,
+ &data);
+ item_append(menu_item_type::SEPARATOR);
}
break;
}
}
-
- item_append(menu_item_type::SEPARATOR);
}
menu_crosshair::~menu_crosshair()
@@ -567,6 +625,7 @@ menu_crosshair::~menu_crosshair()
menu_export::menu_export(mame_ui_manager &mui, render_container &container, std::vector<const game_driver *> &&drvlist)
: menu(mui, container), m_list(std::move(drvlist))
{
+ set_heading(_("Export Displayed List to File"));
}
menu_export::~menu_export()
@@ -577,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)
@@ -616,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();
};
@@ -671,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);
@@ -707,6 +768,7 @@ menu_machine_configure::menu_machine_configure(
osd_setup_osd_specific_emu_options(m_opts);
mame_options::parse_standard_inis(m_opts, error, m_sys.driver);
setup_bios();
+ set_heading(util::string_format(_("System Settings:\n%1$s"), m_sys.description));
}
menu_machine_configure::~menu_machine_configure()
@@ -727,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)
@@ -745,7 +807,7 @@ void menu_machine_configure::handle(event const *ev)
{
std::string inistring = m_opts.output_ini();
file.puts(inistring);
- ui().popup_time(2, "%s", _("\n Configuration saved \n\n"));
+ ui().popup_time(2, "%s", _("\n Settings saved \n\n"));
}
}
break;
@@ -757,14 +819,14 @@ void menu_machine_configure::handle(event const *ev)
m_want_favorite = false;
reset(reset_options::REMEMBER_POSITION);
break;
- case CONTROLLER:
- if (ev->iptkey == IPT_UI_SELECT)
- menu::stack_push<submenu>(ui(), container(), submenu::control_options(), m_sys.driver, &m_opts);
- break;
case VIDEO:
if (ev->iptkey == IPT_UI_SELECT)
menu::stack_push<submenu>(ui(), container(), submenu::video_options(), m_sys.driver, &m_opts);
break;
+ case CONTROLLER:
+ if (ev->iptkey == IPT_UI_SELECT)
+ menu::stack_push<submenu>(ui(), container(), submenu::control_options(), m_sys.driver, &m_opts);
+ break;
case ADVANCED:
if (ev->iptkey == IPT_UI_SELECT)
menu::stack_push<submenu>(ui(), container(), submenu::advanced_options(), m_sys.driver, &m_opts);
@@ -780,23 +842,26 @@ 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);
if (!m_bios.empty())
{
uint32_t arrows = get_arrow_flags(std::size_t(0), m_bios.size() - 1, m_curbios);
- item_append(_("Driver"), m_bios[m_curbios].first, arrows, (void *)(uintptr_t)BIOS);
+ item_append(_("System"), m_bios[m_curbios].first, arrows, (void *)(uintptr_t)BIOS);
}
else
- item_append(_("This machine has no BIOS."), FLAG_DISABLE, nullptr);
+ item_append(_("[this system has no BIOS settings]"), FLAG_DISABLE, nullptr);
item_append(menu_item_type::SEPARATOR);
item_append(_(submenu::advanced_options()[0].description), 0, (void *)(uintptr_t)ADVANCED);
@@ -810,25 +875,13 @@ void menu_machine_configure::populate(float &customtop, float &custombottom)
item_append(_("Remove From Favorites"), 0, (void *)DELFAV);
item_append(menu_item_type::SEPARATOR);
- item_append(_("Save Machine Configuration"), 0, (void *)(uintptr_t)SAVE);
-
- customtop = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
+ item_append(_("Save System Settings"), 0, (void *)(uintptr_t)SAVE);
}
//-------------------------------------------------
// perform our special rendering
//-------------------------------------------------
-void menu_machine_configure::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
-{
- char const *const text[] = { _("Configure Machine:"), m_sys.description.c_str() };
- draw_text_box(
- std::begin(text), std::end(text),
- 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_GREEN_COLOR, 1.0f);
-}
-
void menu_machine_configure::setup_bios()
{
if (!m_sys.driver->rom)
@@ -849,7 +902,7 @@ void menu_machine_configure::setup_bios()
u32 const bios_flags(bios.get_value());
std::string const bios_number(std::to_string(bios_flags - 1));
- // check biosnumber and name
+ // check BIOS number and name
if ((bios_number == specbios) || (specbios == bios.get_name()))
m_curbios = bios_count;
@@ -872,6 +925,7 @@ void menu_machine_configure::setup_bios()
menu_plugins_configure::menu_plugins_configure(mame_ui_manager &mui, render_container &container)
: menu(mui, container)
{
+ set_heading(_("Plugins"));
}
menu_plugins_configure::~menu_plugins_configure()
@@ -891,63 +945,48 @@ 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)
- item_append(_("No plugins found"), 0, nullptr);
+ item_append(_("No plugins found"), FLAG_DISABLE, nullptr);
item_append(menu_item_type::SEPARATOR);
- customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
-}
-
-//-------------------------------------------------
-// perform our special rendering
-//-------------------------------------------------
-
-void menu_plugins_configure::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
-{
- char const *const toptext[] = { _("Plugins") };
- draw_text_box(
- std::begin(toptext), std::end(toptext),
- 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_GREEN_COLOR, 1.0f);
}
} // namespace ui
diff --git a/src/frontend/mame/ui/miscmenu.h b/src/frontend/mame/ui/miscmenu.h
index b17e6713597..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;
};
@@ -133,9 +133,6 @@ public:
std::function<void (bool, bool)> &&handler = nullptr);
virtual ~menu_machine_configure();
-protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
-
private:
using s_bios = std::vector<std::pair<std::string, int>>;
@@ -144,15 +141,15 @@ private:
ADDFAV = 1,
DELFAV,
SAVE,
- CONTROLLER,
VIDEO,
+ CONTROLLER,
BIOS,
ADVANCED,
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();
@@ -177,10 +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 custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) 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 e02d453202a..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, OPTION_HEADER, "UI SEARCH PATH OPTIONS" },
- { OPTION_HISTORY_PATH, "history;dats;.", OPTION_STRING, "path to system/software info files" },
- { OPTION_CATEGORYINI_PATH, "folders", OPTION_STRING, "path to category ini files" },
- { OPTION_CABINETS_PATH, "cabinets;cabdevs", OPTION_STRING, "path to cabinets / devices image" },
- { OPTION_CPANELS_PATH, "cpanel", OPTION_STRING, "path to control panel image" },
- { OPTION_PCBS_PATH, "pcb", OPTION_STRING, "path to pcbs image" },
- { OPTION_FLYERS_PATH, "flyers", OPTION_STRING, "path to flyers image" },
- { OPTION_TITLES_PATH, "titles", OPTION_STRING, "path to titles image" },
- { OPTION_ENDS_PATH, "ends", OPTION_STRING, "path to ends image" },
- { OPTION_MARQUEES_PATH, "marquees", OPTION_STRING, "path to marquees image" },
- { OPTION_ARTPREV_PATH, "artwork preview;artpreview", OPTION_STRING, "path to artwork preview image" },
- { OPTION_BOSSES_PATH, "bosses", OPTION_STRING, "path to bosses image" },
- { OPTION_LOGOS_PATH, "logo", OPTION_STRING, "path to logos image" },
- { OPTION_SCORES_PATH, "scores", OPTION_STRING, "path to scores image" },
- { OPTION_VERSUS_PATH, "versus", OPTION_STRING, "path to versus image" },
- { OPTION_GAMEOVER_PATH, "gameover", OPTION_STRING, "path to gameover image" },
- { OPTION_HOWTO_PATH, "howto", OPTION_STRING, "path to howto image" },
- { OPTION_SELECT_PATH, "select", OPTION_STRING, "path to select image" },
- { OPTION_ICONS_PATH, "icons", OPTION_STRING, "path to ICOns image" },
- { OPTION_COVER_PATH, "covers", OPTION_STRING, "path to software cover image" },
- { OPTION_UI_PATH, "ui", OPTION_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, OPTION_HEADER, "UI MISC OPTIONS" },
- { OPTION_SYSTEM_NAMES, "", OPTION_STRING, "translated system names file" },
- { OPTION_SKIP_WARNINGS, "0", OPTION_BOOLEAN, "display fewer repeated warnings about imperfect emulation" },
- { OPTION_REMEMBER_LAST, "1", OPTION_BOOLEAN, "initially select last used system in main menu" },
- { OPTION_ENLARGE_SNAPS, "1", OPTION_BOOLEAN, "enlarge artwork (snapshot, title, etc.) in right panel (keeping aspect ratio)" },
- { OPTION_FORCED4X3, "1", OPTION_BOOLEAN, "force the appearance of the snapshot in the list software to 4:3" },
- { OPTION_USE_BACKGROUND, "1", OPTION_BOOLEAN, "enable background image in main view" },
- { OPTION_SKIP_BIOS_MENU, "0", OPTION_BOOLEAN, "skip bios submenu, start with configured or default" },
- { OPTION_SKIP_PARTS_MENU, "0", OPTION_BOOLEAN, "skip parts submenu, start with first part" },
- { OPTION_LAST_USED_FILTER, "", OPTION_STRING, "latest used filter" },
- { OPTION_LAST_RIGHT_PANEL "(0-1)", "0", OPTION_INTEGER, "latest right panel focus" },
- { OPTION_LAST_USED_MACHINE, "", OPTION_STRING, "latest used machine" },
- { OPTION_INFO_AUTO_AUDIT, "0", OPTION_BOOLEAN, "enable auto audit in the general info panel" },
- { OPTION_HIDE_ROMLESS, "1", OPTION_BOOLEAN, "hide romless machine from available list" },
- { OPTION_UNTHROTTLE_MUTE ";utm", "0", OPTION_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, OPTION_HEADER, "UI OPTIONS" },
- { OPTION_INFOS_SIZE "(0.20-1.00)", "0.75", OPTION_FLOAT, "UI right panel infos text size (0.20 - 1.00)" },
- { OPTION_FONT_ROWS "(25-40)", "30", OPTION_INTEGER, "UI font lines per screen (25 - 40)" },
- { OPTION_HIDE_PANELS "(0-3)", "0", OPTION_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", OPTION_STRING, "UI border color (ARGB)" },
- { OPTION_UI_BACKGROUND_COLOR, "ef101030", OPTION_STRING, "UI background color (ARGB)" },
- { OPTION_UI_CLONE_COLOR, "ff808080", OPTION_STRING, "UI clone color (ARGB)" },
- { OPTION_UI_DIPSW_COLOR, "ffffff00", OPTION_STRING, "UI dipswitch color (ARGB)" },
- { OPTION_UI_GFXVIEWER_BG_COLOR, "ef101030", OPTION_STRING, "UI gfx viewer color (ARGB)" },
- { OPTION_UI_MOUSEDOWN_BG_COLOR, "b0606000", OPTION_STRING, "UI mouse down bg color (ARGB)" },
- { OPTION_UI_MOUSEDOWN_COLOR, "ffffff80", OPTION_STRING, "UI mouse down color (ARGB)" },
- { OPTION_UI_MOUSEOVER_BG_COLOR, "70404000", OPTION_STRING, "UI mouse over bg color (ARGB)" },
- { OPTION_UI_MOUSEOVER_COLOR, "ffffff80", OPTION_STRING, "UI mouse over color (ARGB)" },
- { OPTION_UI_SELECTED_BG_COLOR, "ef808000", OPTION_STRING, "UI selected bg color (ARGB)" },
- { OPTION_UI_SELECTED_COLOR, "ffffff00", OPTION_STRING, "UI selected color (ARGB)" },
- { OPTION_UI_SLIDER_COLOR, "ffffffff", OPTION_STRING, "UI slider color (ARGB)" },
- { OPTION_UI_SUBITEM_COLOR, "ffffffff", OPTION_STRING, "UI subitem color (ARGB)" },
- { OPTION_UI_TEXT_BG_COLOR, "ef000000", OPTION_STRING, "UI text bg color (ARGB)" },
- { OPTION_UI_TEXT_COLOR, "ffffffff", OPTION_STRING, "UI text color (ARGB)" },
- { OPTION_UI_UNAVAILABLE_COLOR, "ff404040", OPTION_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 d21f069f6f7..07178b2cae4 100644
--- a/src/frontend/mame/ui/optsmenu.cpp
+++ b/src/frontend/mame/ui/optsmenu.cpp
@@ -13,6 +13,7 @@
#include "ui/custui.h"
#include "ui/dirmenu.h"
+#include "ui/inputdevices.h"
#include "ui/inputmap.h"
#include "ui/miscmenu.h"
#include "ui/selector.h"
@@ -22,6 +23,8 @@
#include "mame.h"
#include "mameopts.h"
+
+#include "fileio.h"
#include "rendfont.h"
@@ -39,6 +42,7 @@ menu_simple_game_options::menu_simple_game_options(
, m_handler(std::move(handler))
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading(_("General Settings"));
}
//-------------------------------------------------
@@ -56,101 +60,75 @@ 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);
item_append(_(submenu::misc_options()[0].description), 0, (void *)(uintptr_t)MISC_MENU);
item_append(_(submenu::control_options()[0].description), 0, (void *)(uintptr_t)CONTROLLER_MENU);
- item_append(_("General Inputs"), 0, (void *)(uintptr_t)CGI_MENU);
+ item_append(_("Input Assignments"), 0, (void *)(uintptr_t)INPUTASSIGN_MENU);
item_append(_(submenu::advanced_options()[0].description), 0, (void *)(uintptr_t)ADVANCED_MENU);
if (machine().options().plugins())
item_append(_("Plugins"), 0, (void *)(uintptr_t)PLUGINS_MENU);
item_append(menu_item_type::SEPARATOR);
- item_append(_("Save Configuration"), 0, (void *)(uintptr_t)SAVE_CONFIG);
-
- custombottom = 2.0f * ui().get_line_height() + 3.0f * ui().box_tb_border();
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
+ 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);
}
//-------------------------------------------------
// 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)
{
- switch ((uintptr_t)menu_event.itemref)
+ if (IPT_UI_SELECT == menu_event.iptkey)
{
- case MISC_MENU:
- if (menu_event.iptkey == IPT_UI_SELECT)
+ switch ((uintptr_t)menu_event.itemref)
{
+ case MISC_MENU:
menu::stack_push<submenu>(ui(), container(), submenu::misc_options());
ui_globals::reset = true;
- }
- break;
- case SOUND_MENU:
- if (menu_event.iptkey == IPT_UI_SELECT)
- {
+ break;
+ case SOUND_MENU:
menu::stack_push<menu_sound_options>(ui(), container());
ui_globals::reset = true;
- }
- break;
- case DISPLAY_MENU:
- if (menu_event.iptkey == IPT_UI_SELECT)
- {
+ break;
+ case DISPLAY_MENU:
menu::stack_push<submenu>(ui(), container(), submenu::video_options());
ui_globals::reset = true;
- }
- break;
- case CONTROLLER_MENU:
- if (menu_event.iptkey == IPT_UI_SELECT)
+ break;
+ case CONTROLLER_MENU:
menu::stack_push<submenu>(ui(), container(), submenu::control_options());
- break;
- case CGI_MENU:
- if (menu_event.iptkey == IPT_UI_SELECT)
+ break;
+ case INPUTASSIGN_MENU:
menu::stack_push<menu_input_groups>(ui(), container());
- break;
- case ADVANCED_MENU:
- if (menu_event.iptkey == IPT_UI_SELECT)
- {
+ break;
+ case ADVANCED_MENU:
menu::stack_push<submenu>(ui(), container(), submenu::advanced_options());
ui_globals::reset = true;
- }
- break;
- case PLUGINS_MENU:
- if (menu_event.iptkey == IPT_UI_SELECT)
+ break;
+ case PLUGINS_MENU:
menu::stack_push<menu_plugins_configure>(ui(), container());
- break;
- case SAVE_CONFIG:
- if (menu_event.iptkey == IPT_UI_SELECT)
+ break;
+ case INPUTDEV_MENU:
+ menu::stack_push<menu_input_devices>(ui(), container());
+ break;
+ case SAVE_CONFIG:
ui().save_main_option();
- break;
+ break;
+ }
}
-}
-
-//-------------------------------------------------
-// perform our special rendering
-//-------------------------------------------------
-
-void menu_simple_game_options::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
-{
- char const *const toptext[] = { _("Settings") };
- draw_text_box(
- std::begin(toptext), std::end(toptext),
- 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_GREEN_COLOR, 1.0f);
+ return false;
}
@@ -167,7 +145,6 @@ menu_game_options::menu_game_options(
, m_filter_data(filter_data)
, m_main_filter(filter_data.get_current_filter_type())
{
- set_process_flags(PROCESS_LR_REPEAT);
}
//-------------------------------------------------
@@ -183,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;
@@ -202,7 +177,7 @@ void menu_game_options::populate(float &customtop, float &custombottom)
// add filter item
uint32_t arrow_flags = get_arrow_flags<uint16_t>(machine_filter::FIRST, machine_filter::LAST, m_main_filter);
machine_filter &active_filter(m_filter_data.get_filter(m_main_filter));
- item_append(_("Filter"), active_filter.display_name(), arrow_flags, (void *)(uintptr_t)FILTER_MENU);
+ item_append(_("System Filter"), active_filter.display_name(), arrow_flags, (void *)(uintptr_t)FILTER_MENU);
// add subitem if the filter wants it
if (active_filter.wants_adjuster())
@@ -215,17 +190,17 @@ void menu_game_options::populate(float &customtop, float &custombottom)
// add options items
item_append(_("Customize UI"), 0, (void *)(uintptr_t)CUSTOM_MENU);
- item_append(_("Configure Directories"), 0, (void *)(uintptr_t)CONF_DIR);
+ 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;
@@ -244,7 +219,7 @@ void menu_game_options::handle_item_event(event const &menu_event)
s_sel[index] = machine_filter::display_name(machine_filter::type(index));
menu::stack_push<menu_selector>(
- ui(), container(), std::move(s_sel), m_main_filter,
+ ui(), container(), _("System Filter"), std::move(s_sel), m_main_filter,
[this] (int selection)
{
m_main_filter = machine_filter::type(selection);
@@ -290,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 297ec7aa64f..b300ed90cd5 100644
--- a/src/frontend/mame/ui/optsmenu.h
+++ b/src/frontend/mame/ui/optsmenu.h
@@ -28,11 +28,10 @@ public:
virtual ~menu_simple_game_options() override;
protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
- 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
@@ -41,9 +40,10 @@ private:
SOUND_MENU,
MISC_MENU,
CONTROLLER_MENU,
- CGI_MENU,
+ INPUTASSIGN_MENU,
ADVANCED_MENU,
PLUGINS_MENU,
+ INPUTDEV_MENU,
SAVE_CONFIG
};
@@ -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 6f6a980d871..809047357fc 100644
--- a/src/frontend/mame/ui/pluginopt.cpp
+++ b/src/frontend/mame/ui/pluginopt.cpp
@@ -19,55 +19,52 @@
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) :
menu(mui, container),
m_plugins(mame_machine_manager::instance()->lua()->get_menu())
{
+ 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;
@@ -102,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;
@@ -112,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 6a583346400..53f68006c04 100644
--- a/src/frontend/mame/ui/selector.cpp
+++ b/src/frontend/mame/ui/selector.cpp
@@ -27,10 +27,12 @@ namespace ui {
menu_selector::menu_selector(
mame_ui_manager &mui,
render_container &container,
+ std::string &&title,
std::vector<std::string> &&sel,
int initial,
std::function<void (int)> &&handler)
: menu(mui, container)
+ , m_title(std::move(title))
, m_search()
, m_str_items(std::move(sel))
, m_handler(std::move(handler))
@@ -47,12 +49,15 @@ menu_selector::~menu_selector()
// handle
//-------------------------------------------------
-void menu_selector::handle(event const *ev)
+bool menu_selector::handle(event const *ev)
{
- // process the menu
- if (ev && ev->itemref)
+ if (!ev)
+ return false;
+
+ switch (ev->iptkey)
{
- if (ev->iptkey == IPT_UI_SELECT)
+ case IPT_UI_SELECT:
+ if (ev->itemref)
{
int selection(-1);
for (size_t idx = 0; (m_str_items.size() > idx) && (0 > selection); ++idx)
@@ -60,36 +65,50 @@ void menu_selector::handle(event const *ev)
selection = int(unsigned(idx));
m_handler(selection);
-
stack_pop();
}
- else if (ev->iptkey == 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;
- // escape pressed with non-empty text clears the text
- else if (ev->iptkey == IPT_UI_CANCEL && !m_search.empty())
+ 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));
+
if (!m_search.empty())
{
find_matches(m_search.c_str());
- for (int curitem = 0; m_searchlist[curitem]; ++curitem)
+ int curitem;
+ for (curitem = 0; m_searchlist[curitem]; ++curitem)
item_append(*m_searchlist[curitem], 0, (void *)m_searchlist[curitem]);
+ if (!curitem)
+ item_append(_("menu-selector", "[no matches]"), FLAG_DISABLE, nullptr);
}
else
{
@@ -100,33 +119,39 @@ void menu_selector::populate(float &customtop, float &custombottom)
item_append(m_str_items[index], 0, (void *)&m_str_items[index]);
}
+
+ if (m_str_items.empty())
+ item_append(_("menu-selector", "[no choices]"), FLAG_DISABLE, nullptr); // the caller was probably being dumb
}
item_append(menu_item_type::SEPARATOR);
- customtop = custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
m_initial = -1;
}
//-------------------------------------------------
-// perform our special rendering
+// recompute metrics
//-------------------------------------------------
-void menu_selector::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_selector::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
- std::string tempbuf[1] = { std::string(_("Selection List - Search: ")).append(m_search).append("_") };
- draw_text_box(
- std::begin(tempbuf), std::end(tempbuf),
- 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_GREEN_COLOR, 1.0f);
+ 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(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+{
// get the text for 'UI Select'
- tempbuf[0] = string_format(_("Double-click or press %1$s to select"), ui().get_general_input_setting(IPT_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());
}
//-------------------------------------------------
@@ -137,7 +162,7 @@ void menu_selector::find_matches(const char *str)
{
// allocate memory to track the penalty value
m_ucs_items.reserve(m_str_items.size());
- std::vector<double> penalty(VISIBLE_GAMES_IN_SEARCH, 1.0);
+ std::vector<double> penalty(VISIBLE_SEARCH_ITEMS, 2.0); // impossibly high penalty for unpopulated slots
std::u32string const search(ustr_from_utf8(normalize_unicode(str, unicode_normalization_form::D, true)));
int index = 0;
@@ -149,14 +174,14 @@ void menu_selector::find_matches(const char *str)
double const curpenalty(util::edit_distance(search, m_ucs_items[index]));
// insert into the sorted table of matches
- for (int matchnum = VISIBLE_GAMES_IN_SEARCH - 1; matchnum >= 0; --matchnum)
+ for (int matchnum = VISIBLE_SEARCH_ITEMS - 1; matchnum >= 0; --matchnum)
{
// stop if we're worse than the current entry
if (curpenalty >= penalty[matchnum])
break;
// as long as this isn't the last entry, bump this one down
- if (matchnum < VISIBLE_GAMES_IN_SEARCH - 1)
+ if (matchnum < VISIBLE_SEARCH_ITEMS - 1)
{
penalty[matchnum + 1] = penalty[matchnum];
m_searchlist[matchnum + 1] = m_searchlist[matchnum];
@@ -166,7 +191,7 @@ void menu_selector::find_matches(const char *str)
penalty[matchnum] = curpenalty;
}
}
- (index < VISIBLE_GAMES_IN_SEARCH) ? m_searchlist[index] = nullptr : m_searchlist[VISIBLE_GAMES_IN_SEARCH] = nullptr;
+ (index < VISIBLE_SEARCH_ITEMS) ? m_searchlist[index] = nullptr : m_searchlist[VISIBLE_SEARCH_ITEMS] = nullptr;
}
} // namespace ui
diff --git a/src/frontend/mame/ui/selector.h b/src/frontend/mame/ui/selector.h
index d8b0abc0472..58b93c5185a 100644
--- a/src/frontend/mame/ui/selector.h
+++ b/src/frontend/mame/ui/selector.h
@@ -12,9 +12,12 @@
#pragma once
-
#include "ui/menu.h"
+#include <functional>
+#include <string>
+#include <vector>
+
namespace ui {
@@ -28,31 +31,34 @@ public:
menu_selector(
mame_ui_manager &mui,
render_container &container,
+ std::string &&title,
std::vector<std::string> &&sel,
int initial,
std::function<void (int)> &&handler);
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_GAMES_IN_SEARCH = 200 };
+ 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);
+ std::string const m_title;
std::string m_search;
std::vector<std::string> m_str_items;
std::function<void (int)> m_handler;
std::vector<std::u32string> m_ucs_items;
int m_initial;
- std::string *m_searchlist[VISIBLE_GAMES_IN_SEARCH + 1];
+ std::string *m_searchlist[VISIBLE_SEARCH_ITEMS + 1];
};
} // namespace ui
-#endif /* MAME_FRONTEND_UI_SELECTOR_H */
+#endif // MAME_FRONTEND_UI_SELECTOR_H
diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp
index d52274a6cb9..e9cc85e9a43 100644
--- a/src/frontend/mame/ui/selgame.cpp
+++ b/src/frontend/mame/ui/selgame.cpp
@@ -28,6 +28,7 @@
#include "corestr.h"
#include "drivenum.h"
#include "emuopts.h"
+#include "fileio.h"
#include "rendutil.h"
#include "romload.h"
#include "softlist_dev.h"
@@ -77,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(',');
@@ -93,12 +93,9 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta
fake_ini = util::string_format(u8"\uFEFF%s = %s\n", tmp, sub_filter);
}
- emu_file file(ui().options().ui_path(), OPEN_FLAG_READ);
- if (!file.open_ram(fake_ini.c_str(), fake_ini.size()))
- {
- m_persistent_data.filter_data().load_ini(file);
- file.close();
- }
+ util::core_file::ptr file;
+ if (!util::core_file::open_ram(fake_ini.c_str(), fake_ini.size(), OPEN_FLAG_READ, file))
+ m_persistent_data.filter_data().load_ini(*file);
}
// do this after processing the last used filter setting so it overwrites the placeholder
@@ -110,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;
}
@@ -121,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)
{
@@ -156,17 +159,44 @@ 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)
+ if (!m_prev_selected && item_count() > 0)
m_prev_selected = item(0).ref();
// if I have to select software, force software list submenu
@@ -177,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;
@@ -228,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)
@@ -304,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
{
@@ -323,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();
@@ -424,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,30 +401,23 @@ void menu_select_game::populate(float &customtop, float &custombottom)
if (stack_has_special_main_menu())
{
item_append(menu_item_type::SEPARATOR, 0);
- item_append(_("Configure Options"), 0, (void *)(uintptr_t)CONF_OPTS);
- item_append(_("Configure Machine"), 0, (void *)(uintptr_t)CONF_MACHINE);
- skip_main_items = 3;
+ item_append(_("General Settings"), 0, (void *)(uintptr_t)CONF_OPTS);
+ item_append(_("System Settings"), 0, (void *)(uintptr_t)CONF_MACHINE);
+ 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();
@@ -504,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)
@@ -594,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);
@@ -606,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
{
@@ -626,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;
}
}
@@ -639,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;
}
}
}
@@ -652,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;
@@ -664,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)
{
@@ -682,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();
@@ -696,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;
}
}
@@ -712,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
@@ -729,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
{
@@ -743,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;
}
}
}
@@ -764,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
//-------------------------------------------------
@@ -931,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;
@@ -1030,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());
}
@@ -1055,9 +987,35 @@ 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 machines (%5$d BIOS) )"),
+ line0 = string_format(_("%1$s %2$s ( %3$d / %4$d systems (%5$d BIOS) )"),
emulator_info::get_appname(),
bare_build_version,
m_available_items,
@@ -1089,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 1b393edc91a..5655c226d3f 100644
--- a/src/frontend/mame/ui/selmenu.cpp
+++ b/src/frontend/mame/ui/selmenu.cpp
@@ -15,18 +15,16 @@
#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"
#include "corestr.h"
#include "drivenum.h"
#include "emuopts.h"
+#include "fileio.h"
#include "rendfont.h"
#include "rendutil.h"
#include "romload.h"
@@ -41,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,
@@ -71,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[] = {
@@ -99,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)
{
@@ -157,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
{
@@ -166,12 +182,9 @@ public:
software_parts(mame_ui_manager &mui, render_container &container, s_parts &&parts, ui_software_info const &ui_info);
virtual ~software_parts() override;
-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;
ui_software_info const &m_uiinfo;
s_parts const m_parts;
@@ -184,14 +197,11 @@ public:
bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, ui_software_info const &swinfo, bool inlist);
virtual ~bios_selection() override;
-protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
-
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;
@@ -206,8 +216,8 @@ bool menu_select_launch::reselect_last::s_reselect = false;
// instantiate possible variants of these so derived classes don't get link errors
template bool menu_select_launch::select_bios(game_driver const &, bool);
template bool menu_select_launch::select_bios(ui_software_info const &, bool);
-template float menu_select_launch::draw_left_panel<machine_filter>(machine_filter::type current, std::map<machine_filter::type, machine_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
-template float menu_select_launch::draw_left_panel<software_filter>(software_filter::type current, std::map<software_filter::type, software_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
+template void menu_select_launch::draw_left_panel<machine_filter>(u32 flags, machine_filter::type current, std::map<machine_filter::type, machine_filter::ptr> const &filters);
+template void menu_select_launch::draw_left_panel<software_filter>(u32 flags, software_filter::type current, std::map<software_filter::type, software_filter::ptr> const &filters);
menu_select_launch::system_flags::system_flags(machine_static_info const &info)
@@ -262,6 +272,7 @@ menu_select_launch::software_parts::software_parts(mame_ui_manager &mui, render_
, m_uiinfo(ui_info)
, m_parts(std::move(parts))
{
+ set_heading(_("Select Software Package Part"));
}
//-------------------------------------------------
@@ -276,27 +287,25 @@ 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);
item_append(menu_item_type::SEPARATOR);
- customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
}
//-------------------------------------------------
// 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)
@@ -308,20 +317,8 @@ void menu_select_launch::software_parts::handle(event const *ev)
}
}
}
-}
-
-//-------------------------------------------------
-// perform our special rendering
-//-------------------------------------------------
-void menu_select_launch::software_parts::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
-{
- char const *const text[] = { _("Software part selection:") };
- draw_text_box(
- std::begin(text), std::end(text),
- 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_GREEN_COLOR, 1.0f);
+ return false;
}
@@ -346,6 +343,7 @@ menu_select_launch::bios_selection::bios_selection(mame_ui_manager &mui, render_
, m_inlist(inlist)
, m_bios(std::move(biosname))
{
+ set_heading(_("Select System BIOS"));
}
//-------------------------------------------------
@@ -360,22 +358,20 @@ 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)
+ for (auto &elem : m_bios)
item_append(elem.first, 0, (void *)&elem.first);
item_append(menu_item_type::SEPARATOR);
- customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
}
//-------------------------------------------------
// 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)
@@ -413,20 +409,8 @@ void menu_select_launch::bios_selection::handle(event const *ev)
}
}
}
-}
-//-------------------------------------------------
-// perform our special rendering
-//-------------------------------------------------
-
-void menu_select_launch::bios_selection::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
-{
- char const *const text[] = { _("BIOS selection:") };
- draw_text_box(
- std::begin(text), std::end(text),
- 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_GREEN_COLOR, 1.0f);
+ return false;
}
@@ -444,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);
}
@@ -461,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();
@@ -477,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(),
@@ -489,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);
}
}
}
@@ -515,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)
@@ -526,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)
{
@@ -543,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);
}
@@ -640,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;
@@ -711,11 +865,11 @@ void menu_select_launch::custom_render(void *selectedref, float top, float botto
int cloneof = driver_list::non_bios_clone(driver);
if (0 > cloneof)
- tempbuf[1] = _("Driver is parent");
+ tempbuf[1] = _("System is parent");
else if (system)
- tempbuf[1] = string_format(_("Driver is clone of: %1$s"), system->parent);
+ tempbuf[1] = string_format(_("System is clone of: %1$s"), system->parent);
else
- tempbuf[1] = string_format(_("Driver is clone of: %1$s"), driver_list::driver(cloneof).type.fullname());
+ tempbuf[1] = string_format(_("System is clone of: %1$s"), driver_list::driver(cloneof).type.fullname());
// next line is overall driver status
system_flags const &flags(get_system_flags(driver));
@@ -747,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);
+
+ // first line is version string
+ tempbuf[line++] = string_format("%s %s", emulator_info::get_appname(), build_version);
- tempbuf[0] = string_format(_("%1$s %2$s"), emulator_info::get_appname(), build_version);
- tempbuf[1] = copyright.substr(0, found1);
- tempbuf[2] = copyright.substr(found1 + 1, found2 - (found1 + 1));
- tempbuf[3] = copyright.substr(found2 + 1);
+ // output message
+ while (line < std::size(tempbuf))
+ {
+ auto const found = copyright.find('\n');
+ if (std::string::npos != found)
+ {
+ tempbuf[line++] = copyright.substr(0, found);
+ copyright.remove_prefix(found + 1);
+ }
+ else
+ {
+ tempbuf[line++] = copyright;
+ copyright = std::string_view();
+ }
+ }
}
// draw the footer
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);
}
@@ -775,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 (skip_main_items || (ui_globals::panels_status != HIDE_BOTH))
+ 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))
+ {
+ 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;
@@ -803,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);
}
@@ -824,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;
@@ -863,205 +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)
{
- auto line_height = ui().get_line_height();
- auto lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect(&container());
- auto gutter_width = lr_arrow_width * 1.3f;
-
- // 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);
- // 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);
+ 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);
+
+ // 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);
}
@@ -1159,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
@@ -1185,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);
@@ -1241,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));
}
}
@@ -1326,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(),
@@ -1337,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
//-------------------------------------------------
@@ -1353,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));
};
}
@@ -1365,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);
@@ -1397,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;
}
@@ -1404,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))
@@ -1419,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))
@@ -1459,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));
@@ -1548,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));
@@ -1566,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)
@@ -1635,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
+//-------------------------------------------------
- case ui_event::type::MOUSE_RDOWN:
- if (hover() >= 0 && hover() < item_count() - skip_main_items)
+std::tuple<int, bool, bool> menu_select_launch::update_right_switch(bool changed, ui_event const &uievt)
+{
+ // get new page
+ ui_software_info const *software;
+ ui_system_info const *system;
+ get_selection(software, system);
+ int const min((RP_IMAGES == m_right_panel) ? FIRST_VIEW : 0);
+ int const max(
+ (RP_IMAGES == m_right_panel) ? LAST_VIEW :
+ (software && !software->startempty) ? (ui_globals::cur_sw_dats_total - 1) :
+ (system || (software && software->driver)) ? (ui_globals::curdats_total - 1) :
+ 0);
+ auto const newpage(drag_scroll(
+ pointer_location().first, m_base_pointer.first, m_last_pointer.first, 0.125F * (m_right_content_hbounds.first - m_right_content_hbounds.second),
+ m_clicked_line, min, max));
+
+ // switch page
+ u8 dummy(newpage);
+ u8 &current(
+ (RP_IMAGES == m_right_panel) ? m_image_view :
+ (software && !software->startempty) ? ui_globals::cur_sw_dats_view :
+ (system || (software && software->driver)) ? ui_globals::curdats_view :
+ dummy);
+ bool const redraw(newpage != current);
+ if (redraw)
+ {
+ current = newpage;
+ if (RP_IMAGES == m_right_panel)
+ set_switch_image();
+ else
+ m_topline_datsview = 0;
+ }
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+}
+
+
+//-------------------------------------------------
+// track right panel touch drag scroll
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_right_drag(bool changed, ui_event const &uievt)
+{
+ auto const newtop(drag_scroll(
+ pointer_location().second, m_base_pointer.second, m_last_pointer.second, -m_info_line_height,
+ m_clicked_line, 0, m_total_lines - m_right_visible_lines));
+ bool const moved(newtop != m_topline_datsview);
+ m_topline_datsview = newtop;
+
+ // stop tracking if the primary button is released or another button is pressed
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, moved);
+}
+
+
+//-------------------------------------------------
+// track click on toolbar button
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_toolbar_track(bool changed, ui_event const &uievt)
+{
+ // see if it was released inside the button
+ if (uievt.pointer_released & 0x01)
+ {
+ m_pointer_action = pointer_action::NONE;
+ auto const [x, y] = pointer_location();
+ float const left((0 > m_clicked_line) ? m_toolbar_backtrack_left : (m_toolbar_main_left + (float(m_clicked_line) * m_toolbar_button_spacing)));
+ if ((x >= left) && (x < (left + m_toolbar_button_width)) && (y >= m_toolbar_button_vbounds.first) && (y < m_toolbar_button_vbounds.second))
+ {
+ if (0 > m_clicked_line)
{
- set_selected_index(hover());
- m_prev_selected = get_selection_ref();
- m_focus = focused_menu::MAIN;
- ev.iptkey = IPT_CUSTOM;
- ev.mouse.x0 = local_menu_event.mouse_x;
- ev.mouse.y0 = local_menu_event.mouse_y;
- stop = true;
+ // backtrack button
+ stack_pop();
+ if (is_special_main_menu())
+ machine().schedule_exit();
+ return std::make_tuple(IPT_UI_BACK, false, true);
+ }
+ else
+ {
+ // main buttons
+ auto const *const toolbar_bitmaps(m_is_swlist ? SW_TOOLBAR_BITMAPS : SYS_TOOLBAR_BITMAPS);
+ auto const [bitmap, action, need_selection] = toolbar_bitmaps[m_clicked_line];
+ switch (action)
+ {
+ case IPT_UI_EXPORT:
+ inkey_export();
+ break;
+ case IPT_UI_DATS:
+ inkey_dats();
+ break;
+ default:
+ return std::make_tuple(action, false, true);
+ }
+ return std::make_tuple(IPT_CUSTOM, false, true); // return IPT_CUSTOM to stop processing events
}
- break;
-
- // ignore everything else
- default:
- break;
}
+ }
- // need to update search before processing certain kinds of events, but others don't matter
- if (search_changed)
+ // stop tracking if another button is pressed
+ if (uievt.pointer_pressed & ~u32(0x01))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, pointer_action::TOOLBAR_TRACK != m_pointer_action);
+}
+
+
+//-------------------------------------------------
+// track click on divider
+//-------------------------------------------------
+
+std::tuple<int, bool, bool> menu_select_launch::update_divider_track(bool changed, ui_event const &uievt)
+{
+ // see if it was released inside the divider
+ if (uievt.pointer_released & 0x01)
+ {
+ m_pointer_action = pointer_action::NONE;
+ auto const [x, y] = pointer_location();
+ float const left(m_clicked_line ? right_divider_left() : left_divider_left());
+ float const right(m_clicked_line ? right_divider_right() : left_divider_right());
+ if ((x >= left) && (x < right) && (y >= m_primary_vbounds.first) && (y < m_primary_vbounds.second))
{
- switch (machine().ui_input().peek_event_type())
+ if (m_clicked_line)
{
- case ui_event::type::MOUSE_DOWN:
- case ui_event::type::MOUSE_RDOWN:
- case ui_event::type::MOUSE_DOUBLE_CLICK:
- case ui_event::type::MOUSE_WHEEL:
- stop = true;
- break;
- case ui_event::type::NONE:
- case ui_event::type::WINDOW_FOCUS:
- case ui_event::type::WINDOW_DEFOCUS:
- case ui_event::type::MOUSE_MOVE:
- case ui_event::type::MOUSE_LEAVE:
- case ui_event::type::MOUSE_UP:
- case ui_event::type::MOUSE_RUP:
- case ui_event::type::IME_CHAR:
- break;
+ if ((get_focus() == focused_menu::RIGHTTOP) || (get_focus() == focused_menu::RIGHTBOTTOM))
+ {
+ set_focus(focused_menu::MAIN);
+ select_prev();
+ }
+ m_panels_status ^= HIDE_RIGHT_PANEL;
}
+ else
+ {
+ if (get_focus() == focused_menu::LEFT)
+ {
+ set_focus(focused_menu::MAIN);
+ select_prev();
+ }
+ m_panels_status ^= HIDE_LEFT_PANEL;
+ }
+ return std::make_tuple(IPT_CUSTOM, false, true); // return IPT_CUSTOM to stop processing events
}
}
- if (search_changed)
- reset(reset_options::SELECT_FIRST);
+
+ // stop tracking if another button is pressed
+ if (uievt.pointer_pressed & ~u32(0x01))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, pointer_action::DIVIDER_TRACK != m_pointer_action);
}
//-------------------------------------------------
-// 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)
{
@@ -1975,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);
@@ -2010,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)
{
- // else if the mouse is over this item, draw with a different background
- fgcolor = fgcolor3 = ui().options().mouseover_color();
+ // 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()))
+ {
+ // draw hover highlight when hovered over or dragged off
+ fgcolor = fgcolor3 = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
- highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
}
else if (pitem.ref() == m_prev_selected)
{
- fgcolor = fgcolor3 = ui().options().mouseover_color();
+ fgcolor = fgcolor3 = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
- ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43),
+ ui().draw_textured_box(
+ container(),
+ m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom,
+ bgcolor, rgb_t(43, 43, 43),
hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
}
- if (linenum == 0 && top_line != 0)
+ if ((!linenum && top_line) || ((linenum == (m_visible_lines - 1)) && (itemnum != m_available_items - 1)))
{
- // if we're on the top line, display the up arrow
- draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
- 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0);
-
- if (hover() == itemnum)
- set_hover(HOVER_ARROW_UP);
- }
- else if (linenum == m_visible_lines - 1 && itemnum != m_available_items - 1)
- {
- // if we're on the bottom line, display the down arrow
- draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
- 0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0 ^ ORIENTATION_FLIP_Y);
-
- if (hover() == itemnum)
- set_hover(HOVER_ARROW_DOWN);
+ // if we're on the top or bottom line, display the up or down arrow
+ draw_arrow(
+ 0.5F * (m_primary_items_hbounds.first + m_primary_items_hbounds.second - ud_arrow_width()), linetop + (0.25F * line_height()),
+ 0.5F * (m_primary_items_hbounds.first + m_primary_items_hbounds.second + ud_arrow_width()), linetop + (0.75F * line_height()),
+ fgcolor,
+ linenum ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
}
else if (pitem.type() == menu_item_type::SEPARATOR)
{
// if we're just a divider, draw a line
- container().add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height,
+ container().add_line(
+ m_primary_items_hbounds.first, linetop + (0.5F * line_height()),
+ m_primary_items_hbounds.second, linetop + (0.5F * line_height()),
UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
- else if (pitem.subtext().empty())
- {
- // draw the item centered
- int const item_invert = pitem.flags() & FLAG_INVERT;
- if (m_has_icons)
- draw_icon(linenum, item(itemnum).ref(), effective_left, line_y);
- ui().draw_text_full(
- container(),
- itemtext,
- effective_left + icon_offset, line_y, effective_width - icon_offset,
- text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
- mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
- nullptr, nullptr);
- }
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)
{
- fgcolor = ui().options().mouseover_color();
+ // draw selected highlight for tracked item
+ fgcolor = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
+ }
+ else if (pointerline || (!m_ui_error && !(flags & PROCESS_NOINPUT) && hovered && pointer_idle()))
+ {
+ // draw hover highlight when hovered over or dragged off
+ fgcolor = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
- highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
+ highlight(m_primary_items_hbounds.first, linetop, m_primary_items_hbounds.second, linebottom, bgcolor);
}
if (pitem.type() == menu_item_type::SEPARATOR)
{
+ // if we're just a divider, draw a line
container().add_line(
- visible_left, line + 0.5f * line_height,
- visible_left + visible_width, line + 0.5f * line_height,
- UI_LINE_WIDTH,
- ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ m_primary_items_hbounds.first, linetop + (0.5F * line_height()),
+ m_primary_items_hbounds.second, linetop + (0.5F * line_height()),
+ UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
else
{
+ // draw the item centred
ui().draw_text_full(
container(),
itemtext,
- effective_left, line, effective_width,
+ item_text_left, linetop, item_text_width,
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
mame_ui_manager::NORMAL, fgcolor, bgcolor,
- nullptr, nullptr);
+ 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);
}
@@ -2186,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;
+ m_right_tabs_bottom = m_primary_vbounds.first + line_height();
- // add outlined box for options
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ float const x1(1.0F - lr_border() - m_right_panel_width);
+ float const x2(1.0F - lr_border());
+ float const tabwidth = right_tab_width();
- // add separator line
- container().add_line(x1 + midl, y1, x1 + midl, y1 + line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
-
- std::string buffer[RP_LAST + 1];
- 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
+//-------------------------------------------------
+
+void menu_select_launch::draw_right_box_heading(u32 flags, bool larrow, bool rarrow, std::string_view text)
+{
+ float const text_left(m_right_content_hbounds.first + (2.0F * lr_border()) + lr_arrow_width());
+ float const text_width(m_right_content_hbounds.second - m_right_content_hbounds.first - (4.0F * lr_border()) - (2.0F * lr_arrow_width()));
+
+ rgb_t fgcolor(ui().colors().text_color());
+ rgb_t bgcolor(ui().colors().text_bg_color());
+ if (pointer_action::RIGHT_SWITCH == m_pointer_action)
+ {
+ // touch swipe to switch
+ fgcolor = ui().colors().selected_color();
+ bgcolor = ui().colors().selected_bg_color();
+ highlight(m_right_content_hbounds.first, m_right_heading_top, m_right_content_hbounds.second, m_right_heading_top + line_height(), bgcolor);
+ }
+ else if (focused_menu::RIGHTBOTTOM == m_focus)
+ {
+ // keyboard focus
+ fgcolor = rgb_t(0xff, 0xff, 0x00);
+ bgcolor = rgb_t(0xff, 0xff, 0xff);
+ ui().draw_textured_box(
+ container(),
+ m_right_content_hbounds.first, m_right_heading_top, m_right_content_hbounds.second, m_right_heading_top + line_height(),
+ bgcolor, rgb_t(43, 43, 43),
+ hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
+ }
+
+ ui().draw_text_full(container(),
+ text, text_left, m_right_heading_top, text_width,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor,
+ nullptr, nullptr,
+ line_height());
+
+ if (larrow)
+ {
+ // not using the selected colour here because the background isn't changed
+ float const left(m_right_content_hbounds.first + lr_border());
+ float const right(m_right_content_hbounds.first + lr_border() + lr_arrow_width());
+ bool const hovered(pointer_in_rect(left, right_arrows_top(), right, right_arrows_bottom()));
+ bool const tracked((pointer_action::RIGHT_TRACK_ARROW == m_pointer_action) && !m_clicked_line);
+ rgb_t fg(fgcolor);
+ if ((focused_menu::RIGHTBOTTOM != m_focus) && (tracked || (!m_ui_error && !(flags & PROCESS_NOINPUT) && pointer_idle() && hovered)))
+ fg = ui().colors().mouseover_color();
+ draw_arrow(left, right_arrows_top(), right, right_arrows_bottom(), fg, ROT90 ^ ORIENTATION_FLIP_X);
}
- return (y1 + line_height + UI_LINE_WIDTH);
+ if (rarrow)
+ {
+ // not using the selected colour here because the background isn't changed
+ float const left(m_right_content_hbounds.second - lr_border() - lr_arrow_width());
+ float const right(m_right_content_hbounds.second - lr_border());
+ bool const hovered(pointer_in_rect(left, right_arrows_top(), right, right_arrows_bottom()));
+ bool const tracked((pointer_action::RIGHT_TRACK_ARROW == m_pointer_action) && m_clicked_line);
+ rgb_t fg(fgcolor);
+ if ((focused_menu::RIGHTBOTTOM != m_focus) && (tracked || (!m_ui_error && !(flags & PROCESS_NOINPUT) && pointer_idle() && hovered)))
+ fg = ui().colors().mouseover_color();
+ draw_arrow(left, right_arrows_top(), right, right_arrows_bottom(), fg, ROT90);
+ }
}
@@ -2308,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);
@@ -2318,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)
@@ -2347,104 +3673,43 @@ 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);
+ draw_snapx();
}
}
//-------------------------------------------------
-// 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));
- }
-
- 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;
-}
-
-
-//-------------------------------------------------
// 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());
@@ -2453,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);
}
@@ -2530,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)
{
@@ -2640,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))
{
@@ -2657,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;
@@ -2768,7 +4042,7 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2,
}
else
{
- m_info_buffer = "";
+ m_info_buffer.clear();
mame_machine_manager::instance()->lua()->call_plugin("data", m_info_view - 1, m_info_buffer);
}
}
@@ -2779,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)
@@ -2857,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));
}
@@ -2893,7 +4111,7 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
str << driver.type.fullname();
str << "\t\n\n";
- util::stream_format(str, _("Romset\t%1$s\n"), driver.name);
+ util::stream_format(str, _("Short Name\t%1$s\n"), driver.name);
util::stream_format(str, _("Year\t%1$s\n"), driver.year);
util::stream_format(str, _("Manufacturer\t%1$s\n"), driver.manufacturer);
@@ -2902,12 +4120,12 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
{
util::stream_format(
str,
- _("Driver is Clone of\t%1$s\n"),
+ _("System is Clone of\t%1$s\n"),
system ? std::string_view(system->parent) : std::string_view(driver_list::driver(cloneof).type.fullname()));
}
else
{
- str << _("Driver is Parent\t\n");
+ str << _("System is Parent\t\n");
}
if (flags.has_analog())
@@ -3027,12 +4245,11 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
else if (flags.imperfect_features() & device_t::feature::TIMING)
str << _("Timing\tImperfect\n");
- str << ((flags.machine_flags() & machine_flags::MECHANICAL) ? _("Mechanical Machine\tYes\n") : _("Mechanical Machine\tNo\n"));
+ str << ((flags.machine_flags() & machine_flags::MECHANICAL) ? _("Mechanical System\tYes\n") : _("Mechanical System\tNo\n"));
str << ((flags.machine_flags() & machine_flags::REQUIRES_ARTWORK) ? _("Requires Artwork\tYes\n") : _("Requires Artwork\tNo\n"));
- 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) ? _("Driver is BIOS\tYes\n") : _("Driver is BIOS\tNo\n"));
+ str << ((flags.machine_flags() & machine_flags::IS_BIOS_ROOT) ? _("System is BIOS\tYes\n") : _("System is BIOS\tNo\n"));
str << ((flags.machine_flags() & machine_flags::SUPPORTS_SAVE) ? _("Support Save\tYes\n") : _("Support Save\tNo\n"));
str << ((flags.machine_flags() & ORIENTATION_SWAP_XY) ? _("Screen Orientation\tVertical\n") : _("Screen Orientation\tHorizontal\n"));
bool found = false;
@@ -3057,9 +4274,9 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
// if everything looks good, schedule the new driver
if (audit_passed(summary))
- str << _("ROM Audit Result\tOK\n");
+ str << _("Media Audit Result\tOK\n");
else
- str << _("ROM Audit Result\tBAD\n");
+ str << _("Media Audit Result\tBAD\n");
if (summary_samples == media_auditor::NONE_NEEDED)
str << _("Samples Audit Result\tNone Needed\n");
@@ -3070,10 +4287,12 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver
}
else
{
- str << _("ROM Audit \tDisabled\nSamples Audit \tDisabled\n");
+ str << _("Media Audit\tDisabled\nSamples Audit\tDisabled\n");
}
- buffer = str.str();
+ util::stream_format(str, _("Source File\t%1$s\n"), info_xml_creator::format_sourcefile(driver.type.source()));
+
+ buffer = std::move(str).str();
}
} // namespace ui
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 c5c32303e93..5a0a4f1eb87 100644
--- a/src/frontend/mame/ui/selsoft.cpp
+++ b/src/frontend/mame/ui/selsoft.cpp
@@ -14,16 +14,20 @@
#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"
#include "mame.h"
#include "rendutil.h"
#include "softlist_dev.h"
#include "uiinput.h"
#include "luaengine.h"
+
+#include "corestr.h"
+#include "path.h"
#include "unicode.h"
#include <algorithm>
@@ -84,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);
@@ -218,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
{
@@ -344,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;
@@ -353,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;
};
@@ -371,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();
@@ -394,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:
@@ -483,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();
@@ -563,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();
@@ -582,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);
@@ -594,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)
{
@@ -602,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
{
@@ -617,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;
}
}
}
@@ -631,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());
}
@@ -698,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(_("Driver: \"%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);
@@ -721,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 7cb346ee397..354a8ad2c87 100644
--- a/src/frontend/mame/ui/simpleselgame.cpp
+++ b/src/frontend/mame/ui/simpleselgame.cpp
@@ -17,13 +17,17 @@
#include "ui/ui.h"
#include "ui/utils.h"
+#include "infoxml.h"
+
#include "audit.h"
#include "drivenum.h"
#include "emuopts.h"
+#include "fileio.h"
#include "mame.h"
#include "uiinput.h"
#include <cctype>
+#include <string_view>
namespace ui {
@@ -109,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;
}
@@ -154,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;
@@ -164,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
{
@@ -183,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;
}
}
}
@@ -217,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)
@@ -231,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;
@@ -277,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());
}
@@ -288,122 +299,151 @@ 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(
- _("No machines found. Please check the rompath specified in the %1$s.ini file.\n\n"
- "If this is your first time using %2$s, please see the config.txt file in "
- "the docs directory for information on configuring %2$s."),
+ _("No system ROMs found. Please check the rompath setting specified in the %1$s.ini file.\n\n"
+ "If this is your first time using %2$s, please see the %2$s.pdf file in "
+ "the docs folder for information on setting up and using %2$s."),
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);
+ std::string tempbuf[5];
- // next line source path
- tempbuf[2] = string_format(_("Driver: %1$s"), core_filename_extract_base(driver->type.source()));
-
- // update cached values if selection changed
- if (driver != m_cached_driver)
+ // display the current typeahead
+ if (!m_search.empty())
+ tempbuf[0] = string_format(_("Type name or select: %1$s_"), m_search);
+ else
+ tempbuf[0] = _("Type name or select: (random)");
+
+ // draw the top box
+ draw_text_box(
+ tempbuf, tempbuf + 1,
+ origx1, origx2, origy1 - top, origy1 - tb_border(),
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
+ ui().colors().text_color(), ui().colors().background_color());
+
+ // determine the text to render below
+ game_driver const *const driver = (uintptr_t(selectedref) > 1) ? (const game_driver *)selectedref : nullptr;
+ if (driver)
{
- emu_options clean_options;
- machine_static_info const info(ui().options(), machine_config(*driver, clean_options));
- m_cached_driver = driver;
- m_cached_flags = info.machine_flags();
- m_cached_unemulated = info.unemulated_features();
- m_cached_imperfect = info.imperfect_features();
- m_cached_color = info.status_color();
- }
+ // first line is game name
+ tempbuf[0] = string_format(_("%1$-.100s"), driver->type.fullname());
- // next line is overall driver status
- if (m_cached_flags & machine_flags::NOT_WORKING)
- tempbuf[3] = _("Overall: NOT WORKING");
- else if ((m_cached_unemulated | m_cached_imperfect) & device_t::feature::PROTECTION)
- tempbuf[3] = _("Overall: Unemulated Protection");
- else
- tempbuf[3] = _("Overall: Working");
+ // next line is year, manufacturer
+ tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver->year, driver->manufacturer);
- // next line is graphics, sound status
- if (m_cached_unemulated & device_t::feature::GRAPHICS)
- tempbuf[4] = _("Graphics: Unimplemented, ");
- else if ((m_cached_unemulated | m_cached_imperfect) & (device_t::feature::GRAPHICS | device_t::feature::PALETTE))
- tempbuf[4] = _("Graphics: Imperfect, ");
- else
- tempbuf[4] = _("Graphics: OK, ");
-
- if (m_cached_flags & machine_flags::NO_SOUND_HW)
- tempbuf[4].append(_("Sound: None"));
- else if (m_cached_unemulated & device_t::feature::SOUND)
- tempbuf[4].append(_("Sound: Unimplemented"));
- else if (m_cached_imperfect & device_t::feature::SOUND)
- tempbuf[4].append(_("Sound: Imperfect"));
- else
- tempbuf[4].append(_("Sound: OK"));
- }
- else
- {
- const char *s = emulator_info::get_copyright();
- unsigned line = 0;
+ // next line source path
+ tempbuf[2] = string_format(_("Source file: %1$s"), info_xml_creator::format_sourcefile(driver->type.source()));
- // 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 404e174f803..1cbec4e3528 100644
--- a/src/frontend/mame/ui/sliders.cpp
+++ b/src/frontend/mame/ui/sliders.cpp
@@ -27,6 +27,7 @@ menu_sliders::menu_sliders(mame_ui_manager &mui, render_container &container, bo
set_one_shot(menuless_mode);
set_needs_prev_menu_item(!menuless_mode);
set_process_flags(PROCESS_LR_REPEAT | (m_hidden ? PROCESS_CUSTOM_ONLY : 0));
+ set_heading(_("Slider Controls"));
}
menu_sliders::~menu_sliders()
@@ -38,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;
}
@@ -159,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;
@@ -223,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;
@@ -256,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;
@@ -298,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 e27e6261c63..10fbeed03c6 100644
--- a/src/frontend/mame/ui/slotopt.cpp
+++ b/src/frontend/mame/ui/slotopt.cpp
@@ -45,6 +45,7 @@ namespace ui {
menu_slot_devices::menu_slot_devices(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("Slot Devices"));
}
//-------------------------------------------------
@@ -170,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
@@ -200,10 +201,20 @@ void menu_slot_devices::populate(float &customtop, float &custombottom)
item_append(slot.slot_name(), opt_name, item_flags, (void *)&slot);
}
item_append(menu_item_type::SEPARATOR);
- item_append(_("Reset Machine"), 0, ITEMREF_RESET);
+ 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());
}
@@ -211,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))
{
@@ -220,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());
}
}
@@ -231,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 1ee1e82e25c..32a590b9730 100644
--- a/src/frontend/mame/ui/sndmenu.cpp
+++ b/src/frontend/mame/ui/sndmenu.cpp
@@ -27,6 +27,8 @@ const int menu_sound_options::m_sound_rate[] = { 11025, 22050, 44100, 48000 };
menu_sound_options::menu_sound_options(mame_ui_manager &mui, render_container &container) : menu(mui, container)
{
+ set_heading(_("Sound Options"));
+
osd_options &options = downcast<osd_options &>(mui.machine().options());
m_sample_rate = mui.machine().options().sample_rate();
@@ -63,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;
@@ -109,7 +110,7 @@ void menu_sound_options::handle(event const *ev)
s_sel[index] = std::to_string(m_sound_rate[index]);
menu::stack_push<menu_selector>(
- ui(), container(), std::move(s_sel), m_cur_rates,
+ ui(), container(), _("Sample Rate"), std::move(s_sel), m_cur_rates,
[this] (int selection)
{
m_cur_rates = selection;
@@ -128,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;
}
@@ -137,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];
@@ -148,22 +150,6 @@ void menu_sound_options::populate(float &customtop, float &custombottom)
item_append(_("Sample Rate"), string_format("%d", m_sample_rate), arrow_flags, (void *)(uintptr_t)SAMPLE_RATE);
item_append_on_off(_("Use External Samples"), m_samples, 0, (void *)(uintptr_t)ENABLE_SAMPLES);
item_append(menu_item_type::SEPARATOR);
-
- customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
-}
-
-//-------------------------------------------------
-// perform our special rendering
-//-------------------------------------------------
-
-void menu_sound_options::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
-{
- char const *const toptext[] = { _("Sound Options") };
- draw_text_box(
- std::begin(toptext), std::end(toptext),
- 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_GREEN_COLOR, 1.0f);
}
} // namespace ui
diff --git a/src/frontend/mame/ui/sndmenu.h b/src/frontend/mame/ui/sndmenu.h
index 03f68202748..01d69f6d8ff 100644
--- a/src/frontend/mame/ui/sndmenu.h
+++ b/src/frontend/mame/ui/sndmenu.h
@@ -27,7 +27,6 @@ public:
menu_sound_options(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 menu_dismissed() override;
private:
@@ -39,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 4bda4d473ba..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 {
@@ -91,14 +94,15 @@ menu_load_save_state_base::menu_load_save_state_base(
bool one_shot)
: autopause_menu<>(mui, container)
, m_switch_poller(machine().input())
- , m_header(header)
, m_footer(footer)
, 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);
+ set_heading(header);
}
@@ -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,10 +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
- customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
- 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;
@@ -235,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)
{
@@ -246,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))
{
@@ -261,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;
}
}
@@ -293,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 "";
}
@@ -320,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;
+ }
}
@@ -338,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();
}
@@ -348,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)
{
@@ -382,36 +410,65 @@ 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_render - perform our special rendering
+// custom_pointer_updated - override pointer
+// handling
//-------------------------------------------------
-void menu_load_save_state_base::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+std::tuple<int, bool, bool> menu_load_save_state_base::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- // draw menu title
- draw_text_box(
- &m_header, &m_header + 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_GREEN_COLOR, 1.0f);
+ // 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(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);
@@ -423,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;
}
@@ -432,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());
}
@@ -469,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 af44b47ba5b..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;
@@ -67,22 +70,22 @@ private:
switch_code_poller m_switch_poller;
std::unordered_map<std::string, file_entry> m_file_entries;
std::unordered_map<std::string, std::string> m_filename_to_code_map;
- std::string_view const m_header;
std::string_view const m_footer;
std::string m_delete_prompt;
std::string m_confirm_prompt;
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 c36f837ce5c..cce1f28eeb2 100644
--- a/src/frontend/mame/ui/submenu.cpp
+++ b/src/frontend/mame/ui/submenu.cpp
@@ -32,16 +32,16 @@ std::vector<submenu::option> submenu::misc_options()
return std::vector<option>{
{ option_type::HEAD, N_("Miscellaneous Options") },
{ option_type::UI, N_("Skip imperfect emulation warnings"), OPTION_SKIP_WARNINGS },
- { option_type::UI, N_("Re-select last machine launched"), OPTION_REMEMBER_LAST },
+ { option_type::UI, N_("Re-select last system launched"), OPTION_REMEMBER_LAST },
{ option_type::UI, N_("Enlarge images in the right panel"), OPTION_ENLARGE_SNAPS },
{ option_type::EMU, N_("Cheats"), OPTION_CHEAT },
{ option_type::EMU, N_("Show mouse pointer"), OPTION_UI_MOUSE },
- { option_type::EMU, N_("Confirm quit from machines"), OPTION_CONFIRM_QUIT },
- { option_type::EMU, N_("Skip information screen at startup"), OPTION_SKIP_GAMEINFO },
+ { option_type::EMU, N_("Confirm quit from emulation"), OPTION_CONFIRM_QUIT },
+ { option_type::EMU, N_("Skip system information screen"), OPTION_SKIP_GAMEINFO },
{ option_type::UI, N_("Force 4:3 aspect for snapshot display"), OPTION_FORCED4X3 },
{ option_type::UI, N_("Use image as background"), OPTION_USE_BACKGROUND },
{ option_type::UI, N_("Skip BIOS selection menu"), OPTION_SKIP_BIOS_MENU },
- { option_type::UI, N_("Skip software parts selection menu"), OPTION_SKIP_PARTS_MENU },
+ { option_type::UI, N_("Skip software part selection menu"), OPTION_SKIP_PARTS_MENU },
{ option_type::UI, N_("Info auto audit"), OPTION_INFO_AUTO_AUDIT },
{ option_type::UI, N_("Hide romless machine from available list"),OPTION_HIDE_ROMLESS } };
}
@@ -88,11 +88,12 @@ std::vector<submenu::option> submenu::advanced_options()
{ option_type::EMU, N_("Multi-mouse"), OPTION_MULTIMOUSE },
{ option_type::EMU, N_("Steadykey"), OPTION_STEADYKEY },
{ option_type::EMU, N_("UI active"), OPTION_UI_ACTIVE },
- { option_type::EMU, N_("Offscreen reload"), OPTION_OFFSCREEN_RELOAD },
+ { 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_("Simultaneous contradictory"), OPTION_JOYSTICK_CONTRADICTORY },
+ { option_type::EMU, N_("Allow contradictory joystick inputs"), OPTION_JOYSTICK_CONTRADICTORY },
{ option_type::EMU, N_("Coin impulse"), OPTION_COIN_IMPULSE } };
}
@@ -103,7 +104,7 @@ std::vector<submenu::option> submenu::control_options()
{ option_type::EMU, N_("Lightgun Device Assignment"), OPTION_LIGHTGUN_DEVICE },
{ option_type::EMU, N_("Trackball Device Assignment"), OPTION_TRACKBALL_DEVICE },
{ option_type::EMU, N_("Pedal Device Assignment"), OPTION_PEDAL_DEVICE },
- { option_type::EMU, N_("Adstick Device Assignment"), OPTION_ADSTICK_DEVICE },
+ { option_type::EMU, N_("AD Stick Device Assignment"), OPTION_ADSTICK_DEVICE },
{ option_type::EMU, N_("Paddle Device Assignment"), OPTION_PADDLE_DEVICE },
{ option_type::EMU, N_("Dial Device Assignment"), OPTION_DIAL_DEVICE },
{ option_type::EMU, N_("Positional Device Assignment"), OPTION_POSITIONAL_DEVICE },
@@ -158,6 +159,7 @@ submenu::submenu(mame_ui_manager &mui, render_container &container, std::vector<
, m_driver(drv)
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading(_(m_options[0].description));
core_options *opts = nullptr;
if (m_driver == nullptr)
@@ -172,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() == OPTION_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());
@@ -196,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() == OPTION_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(", ");
@@ -243,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;
@@ -261,11 +263,11 @@ void submenu::handle(event const *ev)
case option_type::OSD:
switch (sm_option.entry->type())
{
- case OPTION_BOOLEAN:
+ case core_options::option_type::BOOLEAN:
changed = true;
sm_option.options->set_value(sm_option.name, !strcmp(sm_option.entry->value(),"1") ? "0" : "1", OPTION_PRIORITY_CMDLINE);
break;
- case OPTION_INTEGER:
+ case core_options::option_type::INTEGER:
if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
{
changed = true;
@@ -274,7 +276,7 @@ void submenu::handle(event const *ev)
sm_option.options->set_value(sm_option.name, i_cur, OPTION_PRIORITY_CMDLINE);
}
break;
- case OPTION_FLOAT:
+ case core_options::option_type::FLOAT:
if (ev->iptkey == IPT_UI_LEFT || ev->iptkey == IPT_UI_RIGHT)
{
changed = true;
@@ -284,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');
@@ -305,7 +307,9 @@ void submenu::handle(event const *ev)
sm_option.options->set_value(sm_option.name, tmptxt.c_str(), OPTION_PRIORITY_CMDLINE);
}
break;
- case OPTION_STRING:
+ 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;
@@ -328,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)
@@ -362,14 +367,14 @@ void submenu::populate(float &customtop, float &custombottom)
case option_type::OSD:
switch (sm_option->entry->type())
{
- case OPTION_BOOLEAN:
+ case core_options::option_type::BOOLEAN:
item_append_on_off(
_(sm_option->description),
sm_option->options->bool_value(sm_option->name),
0,
static_cast<void*>(&(*sm_option)));
break;
- case OPTION_INTEGER:
+ case core_options::option_type::INTEGER:
{
int i_min, i_max;
int i_cur = atoi(sm_option->entry->value());
@@ -391,7 +396,7 @@ void submenu::populate(float &customtop, float &custombottom)
reinterpret_cast<void *>(&*sm_option));
}
break;
- case OPTION_FLOAT:
+ case core_options::option_type::FLOAT:
{
float f_min, f_max;
float f_cur = atof(sm_option->entry->value());
@@ -402,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);
@@ -414,7 +419,7 @@ void submenu::populate(float &customtop, float &custombottom)
reinterpret_cast<void *>(&*sm_option));
}
break;
- case OPTION_STRING:
+ case core_options::option_type::STRING:
{
std::string v_cur(sm_option->entry->value());
int const cur_value = std::distance(sm_option->value.begin(), std::find(sm_option->value.begin(), sm_option->value.end(), v_cur));
@@ -443,22 +448,25 @@ void submenu::populate(float &customtop, float &custombottom)
}
item_append(menu_item_type::SEPARATOR);
- custombottom = customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
}
//-------------------------------------------------
-// perform our special rendering
+// recompute metrics
//-------------------------------------------------
-void submenu::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void submenu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
- char const *const toptext[] = { _(m_options[0].description) };
- draw_text_box(
- std::begin(toptext), std::end(toptext),
- 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_GREEN_COLOR, 1.0f);
+ 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(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+{
if (selectedref)
{
option &selected_sm_option(*reinterpret_cast<option *>(selectedref));
@@ -467,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 258df48b0b2..d7450aac162 100644
--- a/src/frontend/mame/ui/systemlist.cpp
+++ b/src/frontend/mame/ui/systemlist.cpp
@@ -14,12 +14,14 @@
#include "ui/moptions.h"
#include "drivenum.h"
+#include "fileio.h"
#include "util/corestr.h"
#include "util/unicode.h"
#include <algorithm>
#include <cassert>
+#include <functional>
#include <locale>
#include <string_view>
@@ -32,7 +34,11 @@ void system_list::cache_data(ui_options const &options)
if (!m_started)
{
m_started = true;
+#if defined(__EMSCRIPTEN__)
+ std::invoke(
+#else
m_thread = std::make_unique<std::thread>(
+#endif
[this, datpath = std::string(options.history_path()), titles = std::string(options.system_names())]
{
do_cache_data(datpath, titles);
@@ -133,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
{
@@ -294,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 aad554982f5..c302577cc63 100644
--- a/src/frontend/mame/ui/tapectrl.cpp
+++ b/src/frontend/mame/ui/tapectrl.cpp
@@ -39,6 +39,7 @@ menu_tape_control::menu_tape_control(mame_ui_manager &mui, render_container &con
: menu_device_control<cassette_image_device>(mui, container, device)
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading(_("Tape Control"));
}
@@ -55,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())
{
@@ -117,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)
@@ -157,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 cd529aa297d..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.52f * 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 taking into account the hilighting arrows
- float const maximum_width = visible_width - 2.0f * gutter_width;
+ // determine effective positions
+ 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 663354420d2..0c4af7b8ba5 100644
--- a/src/frontend/mame/ui/ui.cpp
+++ b/src/frontend/mame/ui/ui.cpp
@@ -33,13 +33,19 @@
#include "emuopts.h"
#include "mameopts.h"
#include "drivenum.h"
+#include "fileio.h"
#include "natkeyboard.h"
#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>
@@ -48,18 +54,6 @@
/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-enum
-{
- LOADSAVE_NONE,
- LOADSAVE_LOAD,
- LOADSAVE_SAVE
-};
-
-
-/***************************************************************************
LOCAL VARIABLES
***************************************************************************/
@@ -160,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
//-------------------------------------------------
@@ -168,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)
@@ -177,16 +206,18 @@ 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)
+ , m_image_display_enabled(true)
, m_machine_info()
, m_unemulated_features()
, 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()
{
@@ -200,8 +231,6 @@ void mame_ui_manager::init()
ui::system_list::instance().cache_data(options());
// initialize the other UI bits
- ui_gfx_init(machine());
-
m_ui_colors.refresh(options());
// update font row info from setting
@@ -209,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
{
@@ -217,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",
@@ -245,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
//-------------------------------------------------
@@ -341,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);
@@ -351,7 +400,7 @@ void mame_ui_manager::initialize(running_machine &machine)
const char *const service_mode_dipname = ioport_configurer::string_from_token(DEF_STR(Service_Mode));
for (auto &port : machine.ioport().ports())
for (ioport_field &field : port.second->fields())
- if (field.type() == IPT_DIPSWITCH && strcmp(field.name(), service_mode_dipname) == 0)
+ if ((field.type() == IPT_DIPSWITCH) && (field.name() == service_mode_dipname)) // FIXME: probably breaks with localisation, also issues with multiple devices
field.set_defseq(machine.ioport().type_seq(IPT_SERVICE));
}
@@ -371,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;
}
@@ -434,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;
@@ -551,7 +606,7 @@ void mame_ui_manager::display_startup_screens(bool first_time)
if (!mandatory_images.empty() && show_mandatory_fileman)
{
std::ostringstream warning;
- warning << _("This driver requires images to be loaded in the following device(s): ");
+ warning << _("This system requires media images to be mounted for the following device(s): ");
output_joined_collection(mandatory_images,
[&warning](const std::reference_wrapper<const std::string> &img) { warning << "\"" << img.get() << "\""; },
@@ -627,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()))
@@ -662,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)
@@ -671,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);
}
@@ -715,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)
@@ -738,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;
@@ -771,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);
}
@@ -832,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)
@@ -924,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);
}
@@ -936,10 +1026,7 @@ void mame_ui_manager::set_show_fps(bool show)
{
m_showfps = show;
if (!show)
- {
- m_showfps = 0;
m_showfps_end = 0;
- }
}
@@ -960,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;
@@ -995,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));
}
@@ -1014,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;
}
@@ -1027,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;
+ }
}
}
}
@@ -1115,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();
}
@@ -1151,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
//-------------------------------------------------
@@ -1180,7 +1306,7 @@ void mame_ui_manager::start_load_state()
void mame_ui_manager::image_handler_ingame()
{
// run display routine for devices
- if (machine().phase() == machine_phase::RUNNING)
+ if (m_image_display_enabled && machine().phase() == machine_phase::RUNNING)
{
auto layout = create_layout(machine().render().ui_container());
@@ -1212,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())
@@ -1230,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)
@@ -1240,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)
{
@@ -1265,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))
{
@@ -1274,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;
@@ -1284,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
@@ -1297,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
@@ -1331,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
@@ -1364,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))
@@ -1424,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();
}
}
@@ -1438,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;
}
@@ -1462,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
@@ -1476,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));
}
}
}
@@ -1505,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
@@ -1543,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));
}
@@ -1613,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;
}
@@ -1629,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;
}
@@ -1657,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;
}
@@ -1670,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;
}
@@ -1691,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);
}
@@ -1711,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);
}
@@ -1730,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);
}
@@ -1748,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);
}
@@ -1767,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);
}
@@ -1786,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);
}
@@ -1805,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);
}
@@ -1824,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);
}
@@ -1845,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);
}
@@ -1866,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);
}
@@ -1887,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);
}
@@ -1908,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);
}
@@ -1925,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);
}
@@ -1940,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);
}
@@ -1955,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);
}
@@ -1970,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);
}
@@ -1985,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);
}
@@ -2004,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
@@ -2033,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);
@@ -2170,7 +2389,7 @@ void mame_ui_manager::save_main_option()
return;
}
}
- popup_time(3, "%s", _("\n Configuration saved \n\n"));
+ popup_time(3, "%s", _("\n Settings saved \n\n"));
}
void mame_ui_manager::menu_reset()
@@ -2179,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 e155a261871..d213d861f34 100644
--- a/src/frontend/mame/ui/ui.h
+++ b/src/frontend/mame/ui/ui.h
@@ -13,22 +13,27 @@
#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 <map>
+#include <functional>
#include <set>
+#include <string>
#include <string_view>
#include <typeindex>
#include <typeinfo>
+#include <unordered_map>
#include <utility>
#include <vector>
@@ -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,14 +244,17 @@ 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; }
// draw an outlined box with given line color and filled with a texture
void draw_textured_box(render_container &container, float x0, float y0, float x1, float y1, rgb_t backcolor, rgb_t linecolor, render_texture *texture = nullptr, uint32_t flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
virtual void popup_time_string(int seconds, std::string message) override;
virtual void menu_reset() override;
+ virtual bool set_ui_event_handler(std::function<bool ()> &&handler) override;
template <typename Owner, typename Data, typename... Param>
Data &get_session_data(Param &&... args)
@@ -225,29 +272,40 @@ 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::map<std::type_index, std::any>;
+ 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;
bool m_has_warnings;
bool m_unthrottle_mute;
+ bool m_image_display_enabled;
std::unique_ptr<ui::machine_info> m_machine_info;
device_feature_set m_unemulated_features;
@@ -268,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);
@@ -276,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 27a27b45678..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"),
@@ -165,7 +167,7 @@ public:
virtual bool adjust_left() override { return false; }
virtual bool adjust_right() override { return false; }
- virtual void save_ini(emu_file &file, unsigned indent) const override
+ virtual void save_ini(util::core_file &file, unsigned indent) const override
{
file.puts(util::string_format("%2$*1$s%3$s = 1\n", 2 * indent, "", config_name()));
}
@@ -202,22 +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,
- 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(); }
@@ -243,7 +239,7 @@ public:
return true;
}
- virtual void save_ini(emu_file &file, unsigned indent) const override
+ virtual void save_ini(util::core_file &file, unsigned indent) const override
{
char const *const text(filter_text());
file.puts(util::string_format("%2$*1$s%3$s = %4$s\n", 2 * indent, "", this->config_name(), text ? text : ""));
@@ -290,7 +286,7 @@ public:
virtual bool wants_adjuster() const override { return true; }
virtual char const *adjust_text() const override { return _("<set up filters>"); }
- virtual void save_ini(emu_file &file, unsigned indent) const override
+ virtual void save_ini(util::core_file &file, unsigned indent) const override
{
auto const tail(std::find_if(std::begin(m_filters), std::end(m_filters), [] (typename Base::ptr const &flt) { return !flt; }));
file.puts(util::string_format("%2$*1$s%3$s = %4$d\n", 2 * indent, "", this->config_name(), std::distance(std::begin(m_filters), tail)));
@@ -346,7 +342,7 @@ public:
protected:
composite_filter_impl_base() { }
- void populate(char const *value, emu_file *file, unsigned indent)
+ void populate(char const *value, util::core_file *file, unsigned indent)
{
// try to load filters from a file
if (value && file)
@@ -386,21 +382,11 @@ private:
, m_added(false)
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading(_("Select Filters"));
}
virtual ~menu_configure() override { m_handler(m_parent); }
- protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
- {
- char const *const text[] = { _("Select custom filters:") };
- draw_text_box(
- std::begin(text), std::end(text),
- x, x2, y - top, y - ui().box_tb_border(),
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
- }
-
private:
enum : uintptr_t
{
@@ -412,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)
{
@@ -538,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;
@@ -562,115 +548,114 @@ void composite_filter_impl_base<Impl, Base, Type>::menu_configure::populate(floa
if (MAX > i)
item_append(_("Add filter"), 0, (void *)ADD_FILTER);
item_append(menu_item_type::SEPARATOR);
-
- // leave space for heading
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
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::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;
}
@@ -683,7 +668,7 @@ template <machine_filter::type Type = machine_filter::AVAILABLE>
class available_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- available_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ available_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override { return system.available; }
};
@@ -693,7 +678,7 @@ template <machine_filter::type Type = machine_filter::WORKING>
class working_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- working_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ working_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override { return !(system.driver->flags & machine_flags::NOT_WORKING); }
};
@@ -703,7 +688,7 @@ template <machine_filter::type Type = machine_filter::MECHANICAL>
class mechanical_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- mechanical_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ mechanical_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::MECHANICAL; }
};
@@ -713,7 +698,7 @@ template <machine_filter::type Type = machine_filter::BIOS>
class bios_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- bios_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ bios_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::IS_BIOS_ROOT; }
};
@@ -723,7 +708,7 @@ template <machine_filter::type Type = machine_filter::PARENTS>
class parents_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- parents_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ parents_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override
{
@@ -738,7 +723,7 @@ template <machine_filter::type Type = machine_filter::CHD>
class chd_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- chd_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ chd_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override
{
@@ -756,7 +741,7 @@ template <machine_filter::type Type = machine_filter::SAVE>
class save_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- save_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ save_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::SUPPORTS_SAVE; }
};
@@ -766,7 +751,7 @@ template <machine_filter::type Type = machine_filter::VERTICAL>
class vertical_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- vertical_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ vertical_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override { return system.driver->flags & machine_flags::SWAP_XY; }
};
@@ -780,7 +765,7 @@ public:
class manufacturer_machine_filter : public choice_filter_impl_base<machine_filter, machine_filter::MANUFACTURER>
{
public:
- manufacturer_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ manufacturer_machine_filter(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: choice_filter_impl_base<machine_filter, machine_filter::MANUFACTURER>(data.manufacturers(), value)
{
}
@@ -801,7 +786,7 @@ public:
class year_machine_filter : public choice_filter_impl_base<machine_filter, machine_filter::YEAR>
{
public:
- year_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ year_machine_filter(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: choice_filter_impl_base<machine_filter, machine_filter::YEAR>(data.years(), value)
{
}
@@ -810,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
@@ -819,7 +816,7 @@ template <template <machine_filter::type T> class Base, machine_filter::type Typ
class inverted_machine_filter : public Base<Type>
{
public:
- inverted_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ inverted_machine_filter(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: Base<Type>(data, value, file, indent)
{
}
@@ -855,7 +852,7 @@ template <machine_filter::type Type>
class inclusive_machine_filter_impl : public simple_filter_impl_base<machine_filter, Type>
{
public:
- inclusive_machine_filter_impl(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ inclusive_machine_filter_impl(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_system_info const &system) const override { return true; }
};
@@ -872,7 +869,7 @@ using favorite_machine_filter = inclusive_machine_filter_impl<machine_filt
class category_machine_filter : public simple_filter_impl_base<machine_filter, machine_filter::CATEGORY>
{
public:
- category_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ category_machine_filter(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: m_ini(0)
, m_group(0)
, m_include_clones(false)
@@ -926,7 +923,7 @@ public:
virtual bool wants_adjuster() const override { return mame_machine_manager::instance()->inifile().get_file_count(); }
virtual char const *adjust_text() const override { return m_adjust_text.c_str(); }
- virtual void save_ini(emu_file &file, unsigned indent) const override
+ virtual void save_ini(util::core_file &file, unsigned indent) const override
{
char const *const text(filter_text());
file.puts(util::string_format("%2$*1$s%3$s = %4$s\n", 2 * indent, "", this->config_name(), text ? text : ""));
@@ -972,6 +969,8 @@ private:
, m_ini(parent.m_ini)
{
set_process_flags(PROCESS_LR_REPEAT);
+ set_heading("Select Category");
+
inifile_manager const &mgr(mame_machine_manager::instance()->inifile());
for (size_t i = 0; mgr.get_file_count() > i; ++i)
{
@@ -996,17 +995,6 @@ private:
m_handler(m_parent);
}
- protected:
- virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override
- {
- char const *const text[] = { _("Select category:") };
- draw_text_box(
- std::begin(text), std::end(text),
- x, x2, y - top, y - ui().box_tb_border(),
- text_layout::text_justify::CENTER, text_layout::word_wrapping::NEVER, false,
- ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
- }
-
private:
enum : uintptr_t
{
@@ -1015,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;
@@ -1048,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;
@@ -1066,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());
@@ -1091,106 +1080,108 @@ void category_machine_filter::menu_configure::populate(float &customtop, float &
}
}
item_append(menu_item_type::SEPARATOR);
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
-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(),
- 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(),
- 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;
}
@@ -1202,7 +1193,7 @@ void category_machine_filter::menu_configure::handle(event const *ev)
class custom_machine_filter : public composite_filter_impl_base<custom_machine_filter, machine_filter, machine_filter::CUSTOM>
{
public:
- custom_machine_filter(machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ custom_machine_filter(machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: composite_filter_impl_base<custom_machine_filter, machine_filter, machine_filter::CUSTOM>()
, m_data(data)
{
@@ -1210,7 +1201,7 @@ public:
}
ptr create(type n) const { return machine_filter::create(n, m_data); }
- ptr create(emu_file &file, unsigned indent) const { return machine_filter::create(file, m_data, indent); }
+ ptr create(util::core_file &file, unsigned indent) const { return machine_filter::create(file, m_data, indent); }
static bool type_allowed(unsigned pos, type n)
{
@@ -1243,6 +1234,7 @@ public:
case FAVORITE:
case MANUFACTURER:
case YEAR:
+ case SOURCE_FILE:
case CUSTOM:
case COUNT:
break;
@@ -1252,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:
@@ -1268,7 +1270,7 @@ private:
class all_software_filter : public simple_filter_impl_base<software_filter, software_filter::ALL>
{
public:
- all_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ all_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return true; }
};
@@ -1277,7 +1279,7 @@ public:
class available_software_filter : public simple_filter_impl_base<software_filter, software_filter::AVAILABLE>
{
public:
- available_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ available_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return info.available; }
};
@@ -1286,7 +1288,7 @@ public:
class unavailable_software_filter : public simple_filter_impl_base<software_filter, software_filter::UNAVAILABLE>
{
public:
- unavailable_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ unavailable_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return !info.available; }
};
@@ -1295,7 +1297,7 @@ public:
class favorite_software_filter : public simple_filter_impl_base<software_filter, software_filter::FAVORITE>
{
public:
- favorite_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ favorite_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: m_manager(mame_machine_manager::instance()->favorite())
{
}
@@ -1310,7 +1312,7 @@ private:
class parents_software_filter : public simple_filter_impl_base<software_filter, software_filter::PARENTS>
{
public:
- parents_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ parents_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return info.parentname.empty(); }
};
@@ -1319,7 +1321,7 @@ public:
class clones_software_filter : public simple_filter_impl_base<software_filter, software_filter::CLONES>
{
public:
- clones_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ clones_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return !info.parentname.empty(); }
};
@@ -1328,7 +1330,7 @@ public:
class years_software_filter : public choice_filter_impl_base<software_filter, software_filter::YEAR>
{
public:
- years_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ years_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: choice_filter_impl_base<software_filter, software_filter::YEAR>(data.years(), value)
{
}
@@ -1340,7 +1342,7 @@ public:
class publishers_software_filter : public choice_filter_impl_base<software_filter, software_filter::PUBLISHERS>
{
public:
- publishers_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ publishers_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: choice_filter_impl_base<software_filter, software_filter::PUBLISHERS>(data.publishers(), value)
{
}
@@ -1361,7 +1363,7 @@ public:
class supported_software_filter : public simple_filter_impl_base<software_filter, software_filter::SUPPORTED>
{
public:
- supported_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ supported_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return software_support::SUPPORTED == info.supported; }
};
@@ -1371,7 +1373,7 @@ public:
class partial_supported_software_filter : public simple_filter_impl_base<software_filter, software_filter::PARTIAL_SUPPORTED>
{
public:
- partial_supported_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ partial_supported_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return software_support::PARTIALLY_SUPPORTED == info.supported; }
};
@@ -1380,7 +1382,7 @@ public:
class unsupported_software_filter : public simple_filter_impl_base<software_filter, software_filter::UNSUPPORTED>
{
public:
- unsupported_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent) { }
+ unsupported_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent) { }
virtual bool apply(ui_software_info const &info) const override { return software_support::UNSUPPORTED == info.supported; }
};
@@ -1389,7 +1391,7 @@ public:
class region_software_filter : public choice_filter_impl_base<software_filter, software_filter::REGION>
{
public:
- region_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ region_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: choice_filter_impl_base<software_filter, software_filter::REGION>(data.regions(), value)
{
}
@@ -1410,7 +1412,7 @@ public:
class device_type_software_filter : public choice_filter_impl_base<software_filter, software_filter::DEVICE_TYPE>
{
public:
- device_type_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ device_type_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: choice_filter_impl_base<software_filter, software_filter::DEVICE_TYPE>(data.device_types(), value)
{
}
@@ -1422,7 +1424,7 @@ public:
class list_software_filter : public choice_filter_impl_base<software_filter, software_filter::LIST>
{
public:
- list_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ list_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: choice_filter_impl_base<software_filter, software_filter::LIST>(data.list_descriptions(), value)
, m_data(data)
{
@@ -1488,7 +1490,7 @@ private:
class developer_software_filter : public software_info_filter_base<software_filter::DEVELOPERS>
{
public:
- developer_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ developer_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: software_info_filter_base<software_filter::DEVELOPERS>("developer", data.developers(), value)
{
}
@@ -1498,7 +1500,7 @@ public:
class distributor_software_filter : public software_info_filter_base<software_filter::DISTRIBUTORS>
{
public:
- distributor_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ distributor_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: software_info_filter_base<software_filter::DISTRIBUTORS>("distributor", data.distributors(), value)
{
}
@@ -1508,7 +1510,7 @@ public:
class author_software_filter : public software_info_filter_base<software_filter::AUTHORS>
{
public:
- author_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ author_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: software_info_filter_base<software_filter::AUTHORS>("author", data.authors(), value)
{
}
@@ -1518,7 +1520,7 @@ public:
class programmer_software_filter : public software_info_filter_base<software_filter::PROGRAMMERS>
{
public:
- programmer_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ programmer_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: software_info_filter_base<software_filter::PROGRAMMERS>("programmer", data.programmers(), value)
{
}
@@ -1533,7 +1535,7 @@ public:
class custom_software_filter : public composite_filter_impl_base<custom_software_filter, software_filter, software_filter::CUSTOM>
{
public:
- custom_software_filter(software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+ custom_software_filter(software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
: composite_filter_impl_base<custom_software_filter, software_filter, software_filter::CUSTOM>()
, m_data(data)
{
@@ -1541,7 +1543,7 @@ public:
}
ptr create(type n) const { return software_filter::create(n, m_data); }
- ptr create(emu_file &file, unsigned indent) const { return software_filter::create(file, m_data, indent); }
+ ptr create(util::core_file &file, unsigned indent) const { return software_filter::create(file, m_data, indent); }
static bool type_allowed(unsigned pos, type n)
{
@@ -1618,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)
@@ -1662,7 +1675,7 @@ std::string machine_filter_data::get_config_string() const
}
}
-bool machine_filter_data::load_ini(emu_file &file)
+bool machine_filter_data::load_ini(util::core_file &file)
{
machine_filter::ptr flt(machine_filter::create(file, *this));
if (flt)
@@ -1761,7 +1774,7 @@ std::string software_filter_data::extract_publisher(std::string const &publisher
// public machine filter interface
//-------------------------------------------------
-machine_filter::ptr machine_filter::create(type n, machine_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+machine_filter::ptr machine_filter::create(type n, machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
{
assert(COUNT > n);
switch (n)
@@ -1796,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:
@@ -1816,7 +1831,7 @@ machine_filter::ptr machine_filter::create(type n, machine_filter_data const &da
return nullptr;
}
-machine_filter::ptr machine_filter::create(emu_file &file, machine_filter_data const &data, unsigned indent)
+machine_filter::ptr machine_filter::create(util::core_file &file, machine_filter_data const &data, unsigned indent)
{
char buffer[MAX_CHAR_INFO];
if (!file.gets(buffer, std::size(buffer)))
@@ -1883,7 +1898,7 @@ software_filter::software_filter()
{
}
-software_filter::ptr software_filter::create(type n, software_filter_data const &data, char const *value, emu_file *file, unsigned indent)
+software_filter::ptr software_filter::create(type n, software_filter_data const &data, char const *value, util::core_file *file, unsigned indent)
{
assert(COUNT > n);
switch (n)
@@ -1932,7 +1947,7 @@ software_filter::ptr software_filter::create(type n, software_filter_data const
return nullptr;
}
-software_filter::ptr software_filter::create(emu_file &file, software_filter_data const &data, unsigned indent)
+software_filter::ptr software_filter::create(util::core_file &file, software_filter_data const &data, unsigned indent)
{
char buffer[MAX_CHAR_INFO];
if (!file.gets(buffer, std::size(buffer)))
@@ -1969,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[])
{
@@ -1990,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 415a5e6065e..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>
@@ -144,7 +149,7 @@ public:
virtual bool adjust_left() = 0;
virtual bool adjust_right() = 0;
- virtual void save_ini(emu_file &file, unsigned indent) const = 0;
+ virtual void save_ini(util::core_file &file, unsigned indent) const = 0;
template <typename InputIt, class OutputIt>
void apply(InputIt first, InputIt last, OutputIt dest) const
@@ -181,6 +186,7 @@ public:
CLONES,
MANUFACTURER,
YEAR,
+ SOURCE_FILE,
SAVE,
NOSAVE,
CHD,
@@ -198,7 +204,7 @@ public:
virtual std::string adorned_display_name(type n) const = 0;
static ptr create(type n, machine_filter_data const &data) { return create(n, data, nullptr, nullptr, 0); }
- static ptr create(emu_file &file, machine_filter_data const &data) { return create(file, data, 0); }
+ static ptr create(util::core_file &file, machine_filter_data const &data) { return create(file, data, 0); }
static char const *config_name(type n);
static char const *display_name(type n);
@@ -208,8 +214,8 @@ public:
protected:
machine_filter();
- static ptr create(type n, machine_filter_data const &data, char const *value, emu_file *file, unsigned indent);
- static ptr create(emu_file &file, machine_filter_data const &data, unsigned indent);
+ static ptr create(type n, machine_filter_data const &data, char const *value, util::core_file *file, unsigned indent);
+ static ptr create(util::core_file &file, machine_filter_data const &data, unsigned indent);
};
DECLARE_ENUM_INCDEC_OPERATORS(machine_filter::type)
@@ -249,7 +255,7 @@ public:
virtual std::string adorned_display_name(type n) const = 0;
static ptr create(type n, software_filter_data const &data) { return create(n, data, nullptr, nullptr, 0); }
- static ptr create(emu_file &file, software_filter_data const &data) { return create(file, data, 0); }
+ static ptr create(util::core_file &file, software_filter_data const &data) { return create(file, data, 0); }
static char const *config_name(type n);
static char const *display_name(type n);
@@ -259,8 +265,8 @@ public:
protected:
software_filter();
- static ptr create(type n, software_filter_data const &data, char const *value, emu_file *file, unsigned indent);
- static ptr create(emu_file &file, software_filter_data const &data, unsigned indent);
+ static ptr create(type n, software_filter_data const &data, char const *value, util::core_file *file, unsigned indent);
+ static ptr create(util::core_file &file, software_filter_data const &data, unsigned indent);
};
DECLARE_ENUM_INCDEC_OPERATORS(software_filter::type)
@@ -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
@@ -294,13 +303,14 @@ public:
return (m_filters.end() != it) ? it->second.get() : nullptr;
}
std::string get_config_string() const;
- bool load_ini(emu_file &file);
+ bool load_ini(util::core_file &file);
private:
using filter_map = std::map<machine_filter::type, machine_filter::ptr>;
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 e677993dec2..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"
@@ -37,13 +38,14 @@ constexpr uintptr_t ITEM_VIEW_FIRST = 0x00000300;
menu_video_targets::menu_video_targets(mame_ui_manager &mui, render_container &container)
: menu(mui, container)
{
+ set_heading(_("Video Options"));
}
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++)
@@ -67,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))
{
@@ -79,6 +81,8 @@ void menu_video_targets::handle(event const *ev)
*target,
&machine().video().snapshot_target() == target);
}
+
+ return false;
}
@@ -90,36 +94,18 @@ void menu_video_targets::handle(event const *ev)
menu_video_options::menu_video_options(
mame_ui_manager &mui,
render_container &container,
+ std::string_view title,
render_target &target,
bool snapshot)
: menu(mui, container)
, m_target(target)
- , m_title()
- , m_show_title(false)
, m_snapshot(snapshot)
{
- if (!m_snapshot || !machine().video().snap_native())
- {
- set_selected_index(target.view());
- reset(reset_options::REMEMBER_POSITION);
- }
-}
+ set_heading(util::string_format(_("Video Options: %1$s"), title));
-menu_video_options::menu_video_options(
- mame_ui_manager &mui,
- render_container &container,
- std::string &&title,
- render_target &target,
- bool snapshot)
- : menu(mui, container)
- , m_target(target)
- , m_title(std::move(title))
- , m_show_title(true)
- , m_snapshot(snapshot)
-{
if (!m_snapshot || !machine().video().snap_native())
{
- set_selected_index(target.view() + 2);
+ set_selected_index(target.view());
reset(reset_options::REMEMBER_POSITION);
}
}
@@ -128,22 +114,15 @@ menu_video_options::~menu_video_options()
{
}
-void menu_video_options::populate(float &customtop, float &custombottom)
+void menu_video_options::populate()
{
uintptr_t ref;
- // add title if requested
- if (m_show_title)
- {
- item_append(m_title, FLAG_DISABLE, nullptr);
- item_append(menu_item_type::SEPARATOR);
- }
-
// add items for each view
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);
}
@@ -171,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));
@@ -222,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);
@@ -362,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 be539cf4aa0..a725cba0f71 100644
--- a/src/frontend/mame/ui/videoopt.h
+++ b/src/frontend/mame/ui/videoopt.h
@@ -14,6 +14,8 @@
#include "ui/menu.h"
+#include <string_view>
+
namespace ui {
@@ -24,25 +26,22 @@ 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;
};
class menu_video_options : public menu
{
public:
- menu_video_options(mame_ui_manager &mui, render_container &container, render_target &target, bool snapshot);
- menu_video_options(mame_ui_manager &mui, render_container &container, std::string &&title, render_target &target, bool snapshot);
+ menu_video_options(mame_ui_manager &mui, render_container &container, std::string_view title, render_target &target, bool snapshot);
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;
- std::string const m_title;
- bool const m_show_title;
bool const m_snapshot;
};
diff --git a/src/frontend/mame/ui/viewgfx.cpp b/src/frontend/mame/ui/viewgfx.cpp
index b10a62aee13..cbb104d5460 100644
--- a/src/frontend/mame/ui/viewgfx.cpp
+++ b/src/frontend/mame/ui/viewgfx.cpp
@@ -11,412 +11,1001 @@
#include "emu.h"
#include "ui/viewgfx.h"
-
#include "emupal.h"
#include "render.h"
#include "rendfont.h"
#include "rendutil.h"
+#include "screen.h"
#include "tilemap.h"
#include "uiinput.h"
+#include "util/unicode.h"
+
+#include "osdepend.h"
+
#include <cmath>
+#include <vector>
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
+namespace {
-enum ui_gfx_modes
+class gfx_viewer
{
- UI_GFX_PALETTE = 0,
- UI_GFX_GFXSET,
- UI_GFX_TILEMAP
-};
+public:
+ gfx_viewer(running_machine &machine) :
+ m_machine(machine),
+ m_palette(machine),
+ m_gfxset(machine),
+ m_tilemap(machine)
+ {
+ }
-constexpr uint8_t MAX_GFX_DECODERS = 8;
-constexpr int MAX_ZOOM_LEVEL = 8;
-constexpr int MIN_ZOOM_LEVEL = 8;
+ // copy constructor needed to make std::any happy
+ gfx_viewer(gfx_viewer const &that) :
+ gfx_viewer(that.m_machine)
+ {
+ }
+ ~gfx_viewer()
+ {
+ if (m_texture)
+ m_machine.render().texture_free(m_texture);
+ }
+ uint32_t handle(mame_ui_manager &mui, render_container &container, bool uistate)
+ {
+ // implicitly cancel if there's nothing to display
+ if (!is_relevant())
+ return cancel(uistate);
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
+ // let the OSD do its thing
+ mui.machine().osd().check_osd_inputs();
-// information about a single gfx device
-struct ui_gfx_info
-{
- device_gfx_interface *interface; // pointer to device's gfx interface
- uint8_t setcount; // how many gfx sets device has
- uint8_t rotate[MAX_GFX_ELEMENTS]; // current rotation (orientation) value
- uint8_t columns[MAX_GFX_ELEMENTS]; // number of items per row
- int offset[MAX_GFX_ELEMENTS]; // current offset of top,left item
- int color[MAX_GFX_ELEMENTS]; // current color selected
- device_palette_interface *palette[MAX_GFX_ELEMENTS]; // associated palette (maybe multiple choice one day?)
- int color_count[MAX_GFX_ELEMENTS]; // Range of color values
-};
+ // always mark the bitmap dirty if not paused
+ if (!m_machine.paused())
+ m_bitmap_dirty = true;
-struct ui_gfx_state
-{
- bool started; // have we called ui_gfx_count_devices() yet?
- uint8_t mode; // which mode are we in?
+ // 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));
- // intermediate bitmaps
- bool bitmap_dirty; // is the bitmap dirty?
- bitmap_rgb32 bitmap; // bitmap for drawing gfx and tilemaps
- render_texture *texture; // texture for rendering the above bitmap
+ // try to display the selected view
+ while (true)
+ {
+ switch (m_mode)
+ {
+ case view::PALETTE:
+ if (m_palette.interface())
+ return handle_palette(mui, container, uistate);
+ m_mode = view::GFXSET;
+ break;
+
+ case view::GFXSET:
+ if (m_gfxset.has_gfx())
+ return handle_gfxset(mui, container, uistate);
+ m_mode = view::TILEMAP;
+ break;
+
+ case view::TILEMAP:
+ if (m_machine.tilemap().count())
+ return handle_tilemap(mui, container, uistate);
+ m_mode = view::PALETTE;
+ break;
+ }
+ }
+ }
- // palette-specific data
- struct
+private:
+ enum class view
{
- device_palette_interface *interface; // pointer to current palette
- int devcount; // how many palette devices exist
- int devindex; // which palette device is visible
- uint8_t which; // which subset (pens or indirect colors)?
- uint8_t columns; // number of items per row
- int offset; // current offset of top left item
- } palette;
-
- // graphics-specific data
- struct
+ PALETTE = 0,
+ GFXSET,
+ TILEMAP
+ };
+
+ class palette
{
- uint8_t devcount; // how many gfx devices exist
- uint8_t devindex; // which device is visible
- uint8_t set; // which set is visible
- } gfxset;
+ public:
+ enum class subset
+ {
+ PENS,
+ INDIRECT
+ };
+
+ palette(running_machine &machine) :
+ m_count(palette_interface_enumerator(machine.root_device()).count())
+ {
+ if (m_count)
+ set_device(machine);
+ }
+
+ device_palette_interface *interface() const noexcept
+ {
+ return m_interface;
+ }
+
+ bool indirect() const noexcept
+ {
+ return subset::INDIRECT == m_which;
+ }
+
+ unsigned columns() const noexcept
+ {
+ return m_columns;
+ }
+
+ unsigned index(unsigned x, unsigned y) const noexcept
+ {
+ return m_offset + (y * m_columns) + x;
+ }
+
+ void handle_keys(running_machine &machine);
+
+ private:
+ void set_device(running_machine &machine)
+ {
+ m_interface = palette_interface_enumerator(machine.root_device()).byindex(m_index);
+ }
- // information about each gfx device
- ui_gfx_info gfxdev[MAX_GFX_DECODERS];
+ void next_group(running_machine &machine) noexcept
+ {
+ if ((subset::PENS == m_which) && m_interface->indirect_entries())
+ {
+ m_which = subset::INDIRECT;
+ }
+ else if ((m_count - 1) > m_index)
+ {
+ ++m_index;
+ set_device(machine);
+ m_which = subset::PENS;
+ }
+ }
- // tilemap-specific data
- struct
+ void prev_group(running_machine &machine) noexcept
+ {
+ if (subset::INDIRECT == m_which)
+ {
+ m_which = subset::PENS;
+ }
+ else if (0 < m_index)
+ {
+ --m_index;
+ set_device(machine);
+ m_which = m_interface->indirect_entries() ? subset::INDIRECT : subset::PENS;
+ }
+ }
+
+ device_palette_interface *m_interface = nullptr;
+ unsigned const m_count;
+ unsigned m_index = 0U;
+ subset m_which = subset::PENS;
+ unsigned m_columns = 16U;
+ int m_offset = 0;
+ };
+
+ class gfxset
{
- int which; // which tilemap are we viewing?
- int xoffs; // current X offset
- int yoffs; // current Y offset
- int zoom; // zoom factor, either x or 1/x
- bool zoom_frac; // zoom via reciprocal fractions
- bool auto_zoom; // auto-zoom toggle
- uint8_t rotate; // current rotation (orientation) value
- uint32_t flags; // render flags
- } tilemap;
-};
+ public:
+ struct setinfo
+ {
+ void next_color() noexcept
+ {
+ if ((m_color_count - 1) > m_color)
+ ++m_color;
+ else
+ m_color = 0U;
+ }
+ void prev_color() noexcept
+ {
+ if (m_color)
+ --m_color;
+ else
+ m_color = m_color_count - 1;
+ }
+ device_palette_interface *m_palette = nullptr;
+ int m_offset = 0;
+ unsigned m_color = 0;
+ unsigned m_color_count = 0U;
+ uint8_t m_rotate = 0U;
+ uint8_t m_columns = 16U;
+ bool m_integer_scale = false;
+ };
-/***************************************************************************
- GLOBAL VARIABLES
-***************************************************************************/
+ class devinfo
+ {
+ public:
+ devinfo(device_gfx_interface &interface, device_palette_interface *first_palette, u8 rotate) :
+ m_interface(&interface),
+ m_setcount(0U)
+ {
+ for (gfx_element *gfx; (MAX_GFX_ELEMENTS > m_setcount) && ((gfx = interface.gfx(m_setcount)) != nullptr); ++m_setcount)
+ {
+ auto &set = m_sets[m_setcount];
+ if (gfx->has_palette())
+ {
+ set.m_palette = &gfx->palette();
+ set.m_color_count = gfx->colors();
+ }
+ else
+ {
+ set.m_palette = first_palette;
+ set.m_color_count = first_palette->entries() / gfx->granularity();
+ if (!set.m_color_count)
+ set.m_color_count = 1U;
+ }
+ set.m_rotate = rotate;
+ }
+ }
-static ui_gfx_state ui_gfx;
+ device_gfx_interface &interface() const noexcept
+ {
+ return *m_interface;
+ }
+ unsigned setcount() const noexcept
+ {
+ return m_setcount;
+ }
+ setinfo const &set(unsigned index) const noexcept
+ {
+ return m_sets[index];
+ }
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
+ setinfo &set(unsigned index) noexcept
+ {
+ return m_sets[index];
+ }
-static void ui_gfx_count_devices(running_machine &machine, ui_gfx_state &state);
-static void ui_gfx_exit(running_machine &machine);
+ private:
+ device_gfx_interface *m_interface;
+ unsigned m_setcount;
+ setinfo m_sets[MAX_GFX_ELEMENTS];
+ };
-// palette handling
-static void palette_set_device(running_machine &machine, ui_gfx_state &state);
-static void palette_handle_keys(running_machine &machine, ui_gfx_state &state);
-static void palette_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state);
+ gfxset(running_machine &machine)
+ {
+ // get useful defaults
+ uint8_t const rotate = machine.system().flags & machine_flags::MASK_ORIENTATION;
+ device_palette_interface *const first_palette = palette_interface_enumerator(machine.root_device()).first();
-// graphics set handling
-static void gfxset_handle_keys(running_machine &machine, ui_gfx_state &state, int xcells, int ycells);
-static void gfxset_draw_item(running_machine &machine, gfx_element &gfx, int index, bitmap_rgb32 &bitmap, int dstx, int dsty, int color, int rotate, device_palette_interface *dpalette);
-static void gfxset_update_bitmap(running_machine &machine, ui_gfx_state &state, int xcells, int ycells, gfx_element &gfx);
-static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state);
+ // iterate over graphics decoders
+ for (device_gfx_interface &interface : gfx_interface_enumerator(machine.root_device()))
+ {
+ // if there are any exposed graphics sets, add the device
+ if (interface.gfx(0U))
+ m_devices.emplace_back(interface, first_palette, rotate);
+ }
+ }
-// tilemap handling
-static void tilemap_handle_keys(running_machine &machine, ui_gfx_state &state, float pixelscale);
-static void tilemap_update_bitmap(running_machine &machine, ui_gfx_state &state, int width, int height);
-static void tilemap_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state);
+ bool has_gfx() const noexcept
+ {
+ return !m_devices.empty();
+ }
+ bool handle_keys(running_machine &machine, int xcells, int ycells);
+ std::vector<devinfo> m_devices;
+ unsigned m_device = 0U;
+ unsigned m_set = 0U;
-/***************************************************************************
- CORE IMPLEMENTATION
-***************************************************************************/
+ private:
+ bool next_group() noexcept
+ {
+ if ((m_devices[m_device].setcount() - 1) > m_set)
+ {
+ ++m_set;
+ return true;
+ }
+ else if ((m_devices.size() - 1) > m_device)
+ {
+ ++m_device;
+ m_set = 0U;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
-//-------------------------------------------------
-// ui_gfx_init - initialize the graphics viewer
-//-------------------------------------------------
+ bool prev_group() noexcept
+ {
+ if (m_set)
+ {
+ --m_set;
+ return true;
+ }
+ else if (m_device)
+ {
+ --m_device;
+ m_set = m_devices[m_device].setcount() - 1;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ };
-void ui_gfx_init(running_machine &machine)
-{
- ui_gfx_state &state = ui_gfx;
- uint8_t rotate = machine.system().flags & machine_flags::MASK_ORIENTATION;
-
- // make sure we clean up after ourselves
- machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&ui_gfx_exit, &machine));
-
- // initialize our global state
- state.started = false;
- state.mode = 0;
- state.bitmap_dirty = false;
- state.bitmap.reset();
- state.texture = nullptr;
- state.gfxset.devcount = 0;
- state.gfxset.devindex = 0;
- state.gfxset.set = 0;
-
- // set up the palette state
- state.palette.interface = nullptr;
- state.palette.devcount = 0;
- state.palette.devindex = 0;
- state.palette.which = 0;
- state.palette.columns = 16;
-
- // set up the graphics state
- for (uint8_t i = 0; i < MAX_GFX_DECODERS; i++)
+ class tilemap
{
- state.gfxdev[i].interface = nullptr;
- state.gfxdev[i].setcount = 0;
- for (uint8_t j = 0; j < MAX_GFX_ELEMENTS; j++)
+ public:
+ tilemap(running_machine &machine)
{
- state.gfxdev[i].rotate[j] = rotate;
- state.gfxdev[i].columns[j] = 16;
- state.gfxdev[i].offset[j] = 0;
- state.gfxdev[i].color[j] = 0;
- state.gfxdev[i].palette[j] = nullptr;
- state.gfxdev[i].color_count[j] = 0;
+ uint8_t const rotate = machine.system().flags & machine_flags::MASK_ORIENTATION;
+ m_info.resize(machine.tilemap().count());
+ for (auto &info : m_info)
+ info.m_rotate = rotate;
}
- }
- // set up the tilemap state
- state.tilemap.which = 0;
- state.tilemap.xoffs = 0;
- state.tilemap.yoffs = 0;
- state.tilemap.zoom = 1;
- state.tilemap.zoom_frac = false;
- state.tilemap.auto_zoom = true;
- state.tilemap.rotate = rotate;
- state.tilemap.flags = TILEMAP_DRAW_ALL_CATEGORIES;
-}
+ unsigned index() const noexcept
+ {
+ return m_index;
+ }
+ float zoom_scale() const noexcept
+ {
+ auto const &info = m_info[m_index];
+ return info.m_zoom_frac ? (1.0f / float(info.m_zoom)) : float(info.m_zoom);
+ }
-//-------------------------------------------------
-// ui_gfx_count_devices - count the palettes,
-// gfx decoders and gfx sets in the machine
-//-------------------------------------------------
+ bool auto_zoom() const noexcept
+ {
+ return m_info[m_index].m_auto_zoom;
+ }
-static void ui_gfx_count_devices(running_machine &machine, ui_gfx_state &state)
-{
- // count the palette devices
- state.palette.devcount = palette_interface_enumerator(machine.root_device()).count();
+ uint8_t rotate() const noexcept
+ {
+ return m_info[m_index].m_rotate;
+ }
- // set the pointer to the first palette
- if (state.palette.devcount > 0)
- palette_set_device(machine, state);
+ uint32_t flags() const noexcept
+ {
+ return m_info[m_index].m_flags;
+ }
- // count the gfx devices
- state.gfxset.devcount = 0;
- for (device_gfx_interface &interface : gfx_interface_enumerator(machine.root_device()))
- {
- // count the gfx sets in each device, skipping devices with none
- uint8_t count = 0;
- while (count < MAX_GFX_ELEMENTS && interface.gfx(count) != nullptr)
- count++;
+ int xoffs() const noexcept
+ {
+ return m_info[m_index].m_xoffs;
+ }
+
+ int yoffs() const noexcept
+ {
+ return m_info[m_index].m_yoffs;
+ }
+
+ bool handle_keys(running_machine &machine, float pixelscale);
+
+ private:
+ static constexpr int MAX_ZOOM_LEVEL = 8; // maximum tilemap zoom ratio screen:native
+ static constexpr int MIN_ZOOM_LEVEL = 8; // minimum tilemap zoom ratio native:screen
- // count = index of first nullptr
- if (count > 0)
+ struct info
{
- state.gfxdev[state.gfxset.devcount].interface = &interface;
- state.gfxdev[state.gfxset.devcount].setcount = count;
- for (uint8_t slot = 0; slot != count; slot++) {
- auto gfx = interface.gfx(slot);
- if (gfx->has_palette())
+ bool zoom_in(float pixelscale) noexcept
+ {
+ if (m_auto_zoom)
+ {
+ // auto zoom never uses fractional factors
+ m_zoom = std::min<int>(std::lround(pixelscale) + 1, MAX_ZOOM_LEVEL);
+ m_zoom_frac = false;
+ m_auto_zoom = false;
+ return true;
+ }
+ else if (m_zoom_frac)
+ {
+ m_zoom--;
+ if (m_zoom == 1)
+ m_zoom_frac = false; // entering integer zoom range
+ return true;
+ }
+ else if (MAX_ZOOM_LEVEL > m_zoom)
{
- state.gfxdev[state.gfxset.devcount].palette[slot] = &gfx->palette();
- state.gfxdev[state.gfxset.devcount].color_count[slot] = gfx->colors();
+ m_zoom++; // remaining in integer zoom range
+ return true;
}
else
{
- state.gfxdev[state.gfxset.devcount].palette[slot] = state.palette.interface;
- state.gfxdev[state.gfxset.devcount].color_count[slot] = state.palette.interface->entries() / gfx->granularity();
- if (!state.gfxdev[state.gfxset.devcount].color_count[slot])
- state.gfxdev[state.gfxset.devcount].color_count[slot] = 1;
+ return false;
}
}
- if (++state.gfxset.devcount == MAX_GFX_DECODERS)
- break;
+
+ bool zoom_out(float pixelscale) noexcept
+ {
+ if (m_auto_zoom)
+ {
+ // auto zoom never uses fractional factors
+ m_zoom = std::lround(pixelscale) - 1;
+ m_zoom_frac = !m_zoom;
+ if (m_zoom_frac)
+ m_zoom = 2;
+ m_auto_zoom = false;
+ return true;
+ }
+ else if (!m_zoom_frac)
+ {
+ if (m_zoom == 1)
+ {
+ m_zoom++;
+ m_zoom_frac = true; // entering fractional zoom range
+ }
+ else
+ {
+ m_zoom--; // remaining in integer zoom range
+ }
+ return true;
+ }
+ else if (MIN_ZOOM_LEVEL > m_zoom)
+ {
+ m_zoom++; // remaining in fractional zoom range
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ bool next_category() noexcept
+ {
+ if (TILEMAP_DRAW_ALL_CATEGORIES == m_flags)
+ {
+ m_flags = 0U;
+ return true;
+ }
+ else if (TILEMAP_DRAW_CATEGORY_MASK > m_flags)
+ {
+ ++m_flags;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ bool prev_catagory() noexcept
+ {
+ if (!m_flags)
+ {
+ m_flags = TILEMAP_DRAW_ALL_CATEGORIES;
+ return true;
+ }
+ else if (TILEMAP_DRAW_ALL_CATEGORIES != m_flags)
+ {
+ --m_flags;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ int m_xoffs = 0;
+ int m_yoffs = 0;
+ unsigned m_zoom = 1U;
+ bool m_zoom_frac = false;
+ bool m_auto_zoom = true;
+ uint8_t m_rotate = 0U;
+ uint32_t m_flags = TILEMAP_DRAW_ALL_CATEGORIES;
+ };
+
+ static int scroll_step(running_machine &machine)
+ {
+ auto &input = machine.input();
+ if (input.code_pressed(KEYCODE_LCONTROL) || input.code_pressed(KEYCODE_RCONTROL))
+ return 64;
+ else if (input.code_pressed(KEYCODE_LSHIFT) || input.code_pressed(KEYCODE_RSHIFT))
+ return 1;
+ else
+ return 8;
}
+
+ std::vector<info> m_info;
+ unsigned m_index = 0U;
+ };
+
+ bool is_relevant() const noexcept
+ {
+ return m_palette.interface() || m_gfxset.has_gfx() || m_machine.tilemap().count();
}
- state.started = true;
-}
+ uint32_t handle_general_keys(bool uistate)
+ {
+ auto &input = m_machine.ui_input();
+ // UI select cycles through views
+ 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;
+ }
-//-------------------------------------------------
-// ui_gfx_exit - clean up after ourselves
-//-------------------------------------------------
+ // pause does what you'd expect
+ if (input.pressed(IPT_UI_PAUSE))
+ {
+ if (m_machine.paused())
+ m_machine.resume();
+ else
+ m_machine.pause();
+ }
-static void ui_gfx_exit(running_machine &machine)
-{
- // free the texture
- machine.render().texture_free(ui_gfx.texture);
- ui_gfx.texture = nullptr;
+ // cancel or graphics viewer dismisses the viewer
+ if (input.pressed(IPT_UI_BACK) || input.pressed(IPT_UI_SHOW_GFX))
+ return cancel(uistate);
- // free the bitmap
- ui_gfx.bitmap.reset();
-}
+ return uistate;
+ }
+ uint32_t cancel(bool uistate)
+ {
+ if (!uistate)
+ m_machine.resume();
+ m_machine.ui_input().reset();
+ m_current_pointer = -1;
+ m_pointer_type = ui_event::pointer::UNKNOWN;
+ m_pointer_buttons = 0U;
+ m_pointer_x = -1.0F;
+ m_pointer_y = -1.0F;
+ m_pointer_inside = false;
+ m_bitmap_dirty = true;
+ return mame_ui_manager::HANDLER_CANCEL;
+ }
-//-------------------------------------------------
-// ui_gfx_is_relevant - returns 'true' if the
-// internal graphics viewer has relevance
-//
-// NOTE: this must not be called before machine
-// initialization is complete, as some drivers
-// create or modify gfx sets in VIDEO_START
-//-------------------------------------------------
+ uint32_t handle_palette(mame_ui_manager &mui, render_container &container, bool uistate);
+ uint32_t handle_gfxset(mame_ui_manager &mui, render_container &container, bool uistate);
+ uint32_t handle_tilemap(mame_ui_manager &mui, render_container &container, bool uistate);
-bool ui_gfx_is_relevant(running_machine &machine)
-{
- ui_gfx_state &state = ui_gfx;
+ void update_gfxset_bitmap(int xcells, int ycells, gfx_element &gfx);
+ void update_tilemap_bitmap(int width, int height);
- if (!state.started)
- ui_gfx_count_devices(machine, state);
+ void gfxset_draw_item(gfx_element &gfx, int index, int dstx, int dsty, gfxset::setinfo const &info);
- return state.palette.devcount > 0
- || state.gfxset.devcount > 0
- || machine.tilemap().count() > 0;
-}
+ void draw_text(mame_ui_manager &mui, render_container &container, std::string_view str, float x, float y)
+ {
+ render_font *const font = mui.get_font();
+ float const height = mui.get_line_height();
+ float const aspect = m_machine.render().ui_aspect(&container);
+ rgb_t const color = mui.colors().text_color();
+
+ int n;
+ char32_t ch;
+ while ((n = uchar_from_utf8(&ch, str)) != 0)
+ {
+ if (0 > n)
+ ch = 0xfffd;
+ str.remove_prefix((0 > n) ? 1 : n);
+ container.add_char(x, y, height, aspect, color, *font, ch);
+ x += font->char_width(height, aspect, ch);
+ }
+ }
+ void resize_bitmap(int32_t width, int32_t height)
+ {
+ if (!m_bitmap.valid() || !m_texture || (m_bitmap.width() != width) || (m_bitmap.height() != height))
+ {
+ // free the old stuff
+ if (m_texture)
+ m_machine.render().texture_free(m_texture);
-//-------------------------------------------------
-// ui_gfx_ui_handler - primary UI handler
-//-------------------------------------------------
+ // allocate new stuff
+ m_bitmap.resize(width, height);
+ m_texture = m_machine.render().texture_alloc();
+ m_texture->set_bitmap(m_bitmap, m_bitmap.cliprect(), TEXFORMAT_ARGB32);
-uint32_t ui_gfx_ui_handler(render_container &container, mame_ui_manager &mui, bool uistate)
+ // force a redraw
+ m_bitmap_dirty = true;
+ }
+ }
+
+ bool map_mouse(render_container &container, render_bounds const &clip, float &x, float &y) const
+ {
+ if (((0 > m_current_pointer) && (m_pointer_type != ui_event::pointer::TOUCH)) || !m_pointer_inside)
+ return false;
+
+ 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;
+
+ palette m_palette;
+ gfxset m_gfxset;
+ tilemap m_tilemap;
+};
+
+
+void gfx_viewer::palette::handle_keys(running_machine &machine)
{
- ui_gfx_state &state = ui_gfx;
+ auto &input = machine.ui_input();
- // if we have nothing, implicitly cancel
- if (!ui_gfx_is_relevant(mui.machine()))
- goto cancel;
+ // handle zoom (minus,plus)
+ if (input.pressed(IPT_UI_ZOOM_OUT))
+ m_columns = std::min<unsigned>(m_columns * 2, 64);
+ if (input.pressed(IPT_UI_ZOOM_IN))
+ m_columns = std::max<unsigned>(m_columns / 2, 4);
+ if (input.pressed(IPT_UI_ZOOM_DEFAULT))
+ m_columns = 16;
- // if we're not paused, mark the bitmap dirty
- if (!mui.machine().paused())
- state.bitmap_dirty = true;
+ // handle colormap selection (open bracket,close bracket)
+ if (input.pressed(IPT_UI_PREV_GROUP))
+ prev_group(machine);
+ if (input.pressed(IPT_UI_NEXT_GROUP))
+ next_group(machine);
- // switch off the state to display something
-again:
- switch (state.mode)
- {
- case UI_GFX_PALETTE:
- // if we have a palette, display it
- if (state.palette.devcount > 0)
- {
- palette_handler(mui, container, state);
- break;
- }
+ // cache some info in locals
+ int const total = (subset::INDIRECT == m_which) ? m_interface->indirect_entries() : m_interface->entries();
- state.mode++;
- [[fallthrough]];
- case UI_GFX_GFXSET:
- // if we have graphics sets, display them
- if (state.gfxset.devcount > 0)
- {
- gfxset_handler(mui, container, state);
- break;
- }
+ // determine number of entries per row and total
+ int const rowcount = m_columns;
+ int const screencount = rowcount * rowcount;
+
+ // handle keyboard navigation
+ if (input.pressed_repeat(IPT_UI_UP, 4))
+ m_offset -= rowcount;
+ if (input.pressed_repeat(IPT_UI_DOWN, 4))
+ m_offset += rowcount;
+ if (input.pressed_repeat(IPT_UI_PAGE_UP, 6))
+ m_offset -= screencount;
+ if (input.pressed_repeat(IPT_UI_PAGE_DOWN, 6))
+ m_offset += screencount;
+ if (input.pressed_repeat(IPT_UI_HOME, 4))
+ m_offset = 0;
+ if (input.pressed_repeat(IPT_UI_END, 4))
+ m_offset = total;
+
+ // clamp within range
+ if (m_offset + screencount > ((total + rowcount - 1) / rowcount) * rowcount)
+ m_offset = ((total + rowcount - 1) / rowcount) * rowcount - screencount;
+ if (m_offset < 0)
+ m_offset = 0;
+}
- state.mode++;
- [[fallthrough]];
- case UI_GFX_TILEMAP:
- // if we have tilemaps, display them
- if (mui.machine().tilemap().count() > 0)
- {
- tilemap_handler(mui, container, state);
- break;
- }
- state.mode = UI_GFX_PALETTE;
- goto again;
+bool gfx_viewer::gfxset::handle_keys(running_machine &machine, int xcells, int ycells)
+{
+ auto &input = machine.ui_input();
+ bool const shift_pressed = machine.input().code_pressed(KEYCODE_LSHIFT) || machine.input().code_pressed(KEYCODE_RSHIFT);
+ bool result = false;
+
+ // handle previous/next group
+ if (input.pressed(IPT_UI_PREV_GROUP) && prev_group())
+ result = true;
+ if (input.pressed(IPT_UI_NEXT_GROUP) && next_group())
+ result = true;
+
+ auto &info = m_devices[m_device];
+ auto &set = info.set(m_set);
+ auto &gfx = *info.interface().gfx(m_set);
+
+ // handle cells per line (0/-/=)
+ if (input.pressed(IPT_UI_ZOOM_OUT) && (xcells < 128))
+ {
+ set.m_columns = xcells + 1;
+ set.m_integer_scale = shift_pressed;
+ result = true;
+ }
+ if (input.pressed(IPT_UI_ZOOM_IN) && (xcells > 2))
+ {
+ set.m_columns = xcells - 1;
+ set.m_integer_scale = shift_pressed;
+ result = true;
+ }
+ if (input.pressed(IPT_UI_ZOOM_DEFAULT) && ((xcells != 16) || (set.m_integer_scale != shift_pressed)))
+ {
+ set.m_columns = 16;
+ set.m_integer_scale = shift_pressed;
+ result = true;
}
- // handle keys
- if (mui.machine().ui_input().pressed(IPT_UI_SELECT))
+ // handle rotation (R)
+ if (input.pressed(IPT_UI_ROTATE))
{
- state.mode = (state.mode + 1) % 3;
- state.bitmap_dirty = true;
+ set.m_rotate = orientation_add(ROT90, set.m_rotate);
+ result = true;
}
- if (mui.machine().ui_input().pressed(IPT_UI_PAUSE))
+ // handle navigation within the cells (up,down,pgup,pgdown)
+ if (input.pressed_repeat(IPT_UI_UP, 4))
{
- if (mui.machine().paused())
- mui.machine().resume();
- else
- mui.machine().pause();
+ set.m_offset -= xcells;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_DOWN, 4))
+ {
+ set.m_offset += xcells;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_PAGE_UP, 6))
+ {
+ set.m_offset -= xcells * ycells;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_PAGE_DOWN, 6))
+ {
+ set.m_offset += xcells * ycells;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_HOME, 4))
+ {
+ set.m_offset = 0;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_END, 4))
+ {
+ set.m_offset = gfx.elements();
+ result = true;
}
- if (mui.machine().ui_input().pressed(IPT_UI_CANCEL) || mui.machine().ui_input().pressed(IPT_UI_SHOW_GFX))
- goto cancel;
+ // clamp within range
+ if (set.m_offset + xcells * ycells > ((gfx.elements() + xcells - 1) / xcells) * xcells)
+ {
+ set.m_offset = ((gfx.elements() + xcells - 1) / xcells) * xcells - xcells * ycells;
+ result = true;
+ }
+ if (set.m_offset < 0)
+ {
+ set.m_offset = 0;
+ result = true;
+ }
- return uistate;
+ // handle color selection (left,right)
+ if (input.pressed_repeat(IPT_UI_LEFT, 4))
+ {
+ set.prev_color();
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_RIGHT, 4))
+ {
+ set.next_color();
+ result = true;
+ }
-cancel:
- if (!uistate)
- mui.machine().resume();
- state.bitmap_dirty = true;
- return UI_HANDLER_CANCEL;
+ return result;
}
+bool gfx_viewer::tilemap::handle_keys(running_machine &machine, float pixelscale)
+{
+ auto &input = machine.ui_input();
+ bool result = false;
+
+ // handle tilemap selection (open bracket,close bracket)
+ if (input.pressed(IPT_UI_PREV_GROUP) && m_index > 0)
+ {
+ m_index--;
+ result = true;
+ }
+ if (input.pressed(IPT_UI_NEXT_GROUP) && ((m_info.size() - 1) > m_index))
+ {
+ m_index++;
+ result = true;
+ }
-/***************************************************************************
- PALETTE VIEWER
-***************************************************************************/
+ auto &info = m_info[m_index];
-//-------------------------------------------------
-// palette_set_device - set the pointer to the
-// current palette device
-//-------------------------------------------------
+ // handle zoom (minus,plus)
+ if (input.pressed(IPT_UI_ZOOM_OUT) && info.zoom_out(pixelscale))
+ {
+ result = true;
+ machine.popmessage(info.m_zoom_frac ? _("gfxview", "Zoom = 1/%1$d") : _("gfxview", "Zoom = %1$d"), info.m_zoom);
+ }
+ if (input.pressed(IPT_UI_ZOOM_IN) && info.zoom_in(pixelscale))
+ {
+ result = true;
+ machine.popmessage(info.m_zoom_frac ? _("gfxview", "Zoom = 1/%1$d") : _("gfxview", "Zoom = %1$d"), info.m_zoom);
+ }
+ if (input.pressed(IPT_UI_ZOOM_DEFAULT) && !info.m_auto_zoom)
+ {
+ info.m_auto_zoom = true;
+ machine.popmessage(_("gfxview", "Expand to fit"));
+ }
-static void palette_set_device(running_machine &machine, ui_gfx_state &state)
-{
- palette_interface_enumerator pal_iter(machine.root_device());
- state.palette.interface = pal_iter.byindex(state.palette.devindex);
-}
+ // handle rotation (R)
+ if (input.pressed(IPT_UI_ROTATE))
+ {
+ info.m_rotate = orientation_add(ROT90, info.m_rotate);
+ result = true;
+ }
+ // return to (0,0) (HOME)
+ if (input.pressed(IPT_UI_HOME))
+ {
+ info.m_xoffs = 0;
+ info.m_yoffs = 0;
+ result = true;
+ }
-//-------------------------------------------------
-// palette_handler - handler for the palette
-// viewer
-//-------------------------------------------------
+ // handle flags (category)
+ if (input.pressed(IPT_UI_PAGE_UP) && info.prev_catagory())
+ {
+ result = true;
+ if (TILEMAP_DRAW_ALL_CATEGORIES == info.m_flags)
+ machine.popmessage(_("gfxview", "All categories"));
+ else
+ machine.popmessage(_("gfxview", "Category %1$d"), info.m_flags);
+ }
+ if (input.pressed(IPT_UI_PAGE_DOWN) && info.next_category())
+ {
+ result = true;
+ machine.popmessage(_("gfxview", "Category %1$d"), info.m_flags);
+ }
-static void palette_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state)
+ // handle navigation (up,down,left,right), taking orientation into account
+ int const step = scroll_step(machine); // this may be applied more than once if multiple directions are pressed
+ if (input.pressed_repeat(IPT_UI_UP, 4))
+ {
+ if (info.m_rotate & ORIENTATION_SWAP_XY)
+ info.m_xoffs -= (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step;
+ else
+ info.m_yoffs -= (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_DOWN, 4))
+ {
+ if (info.m_rotate & ORIENTATION_SWAP_XY)
+ info.m_xoffs += (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step;
+ else
+ info.m_yoffs += (info.m_rotate & ORIENTATION_FLIP_Y) ? -step : step;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_LEFT, 6))
+ {
+ if (info.m_rotate & ORIENTATION_SWAP_XY)
+ info.m_yoffs -= (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step;
+ else
+ info.m_xoffs -= (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step;
+ result = true;
+ }
+ if (input.pressed_repeat(IPT_UI_RIGHT, 6))
+ {
+ if (info.m_rotate & ORIENTATION_SWAP_XY)
+ info.m_yoffs += (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step;
+ else
+ info.m_xoffs += (info.m_rotate & ORIENTATION_FLIP_X) ? -step : step;
+ result = true;
+ }
+
+ // cache some info in locals
+ tilemap_t *const tilemap = machine.tilemap().find(m_index);
+ uint32_t const mapwidth = tilemap->width();
+ uint32_t const mapheight = tilemap->height();
+
+ // clamp within range
+ while (info.m_xoffs < 0)
+ info.m_xoffs += mapwidth;
+ while (info.m_xoffs >= mapwidth)
+ info.m_xoffs -= mapwidth;
+ while (info.m_yoffs < 0)
+ info.m_yoffs += mapheight;
+ while (info.m_yoffs >= mapheight)
+ info.m_yoffs -= mapheight;
+
+ return result;
+}
+
+
+uint32_t gfx_viewer::handle_palette(mame_ui_manager &mui, render_container &container, bool uistate)
{
- device_palette_interface *palette = state.palette.interface;
- palette_device *paldev = dynamic_cast<palette_device *>(&palette->device());
+ device_palette_interface &palette = *m_palette.interface();
+ palette_device *const paldev = dynamic_cast<palette_device *>(&palette.device());
- int total = state.palette.which ? palette->indirect_entries() : palette->entries();
- const rgb_t *raw_color = palette->palette()->entry_list_raw();
- render_font *ui_font = mui.get_font();
- float titlewidth;
- float x0, y0;
- render_bounds cellboxbounds;
- render_bounds boxbounds;
- int skip;
+ bool const indirect = m_palette.indirect();
+ unsigned const total = indirect ? palette.indirect_entries() : palette.entries();
+ rgb_t const *const raw_color = palette.palette()->entry_list_raw();
// add a half character padding for the box
- const float aspect = mui.machine().render().ui_aspect(&container);
- const float chheight = mui.get_line_height();
- const float chwidth = ui_font->char_width(chheight, aspect, '0');
- boxbounds.x0 = 0.0f + 0.5f * chwidth;
- boxbounds.x1 = 1.0f - 0.5f * chwidth;
- boxbounds.y0 = 0.0f + 0.5f * chheight;
- boxbounds.y1 = 1.0f - 0.5f * chheight;
+ render_font *const ui_font = mui.get_font();
+ float const aspect = m_machine.render().ui_aspect(&container);
+ float const chheight = mui.get_line_height();
+ float const chwidth = ui_font->char_width(chheight, aspect, '0');
+ render_bounds const boxbounds{
+ 0.0f + (0.5f * chwidth),
+ 0.0f + (0.5f * chheight),
+ 1.0f - (0.5f * chwidth),
+ 1.0f - (0.5f * chheight) };
// the character cell box bounds starts a half character in from the box
- cellboxbounds = boxbounds;
+ render_bounds cellboxbounds = boxbounds;
cellboxbounds.x0 += 0.5f * chwidth;
- cellboxbounds.x1 -= 0.5f * chwidth;
cellboxbounds.y0 += 0.5f * chheight;
+ cellboxbounds.x1 -= 0.5f * chwidth;
cellboxbounds.y1 -= 0.5f * chheight;
// add space on the left for 5 characters of text, plus a half character of padding
@@ -426,41 +1015,57 @@ static void palette_handler(mame_ui_manager &mui, render_container &container, u
cellboxbounds.y0 += 3.0f * chheight;
// compute the cell size
- const float cellwidth = (cellboxbounds.x1 - cellboxbounds.x0) / float(state.palette.columns);
- const float cellheight = (cellboxbounds.y1 - cellboxbounds.y0) / float(state.palette.columns);
+ float const cellwidth = (cellboxbounds.x1 - cellboxbounds.x0) / float(m_palette.columns());
+ float const cellheight = (cellboxbounds.y1 - cellboxbounds.y0) / float(m_palette.columns());
// figure out the title
std::ostringstream title_buf;
- util::stream_format(title_buf, "'%s'", palette->device().tag());
- if (palette->indirect_entries() > 0)
- title_buf << (state.palette.which ? _(" COLORS") : _(" PENS"));
+ util::stream_format(title_buf,
+ !palette.indirect_entries() ? _("gfxview", "[root%1$s]") : indirect ? _("gfxview", "[root%1$s] colors") : _("gfxview", "[root%1$s] pens"),
+ palette.device().tag());
// if the mouse pointer is over one of our cells, add some info about the corresponding palette entry
- int32_t mouse_target_x, mouse_target_y;
float mouse_x, mouse_y;
- bool mouse_button;
- render_target *const mouse_target = mui.machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
- if (mouse_target != nullptr && mouse_target->map_point_container(mouse_target_x, mouse_target_y, container, mouse_x, mouse_y)
- && cellboxbounds.x0 <= mouse_x && cellboxbounds.x1 > mouse_x
- && cellboxbounds.y0 <= mouse_y && cellboxbounds.y1 > mouse_y)
+ if (map_mouse(container, cellboxbounds, mouse_x, mouse_y))
{
- int index = state.palette.offset + int((mouse_x - cellboxbounds.x0) / cellwidth) + int((mouse_y - cellboxbounds.y0) / cellheight) * state.palette.columns;
+ int const index = m_palette.index(int((mouse_x - cellboxbounds.x0) / cellwidth), int((mouse_y - cellboxbounds.y0) / cellheight));
if (index < total)
{
- util::stream_format(title_buf, " #%X", index);
- if (palette->indirect_entries() > 0 && !state.palette.which)
- util::stream_format(title_buf, " => %X", palette->pen_indirect(index));
- else if (paldev != nullptr && paldev->basemem().base() != nullptr)
- util::stream_format(title_buf, " = %X", paldev->read_entry(index));
-
- rgb_t col = state.palette.which ? palette->indirect_color(index) : raw_color[index];
- util::stream_format(title_buf, " (A:%X R:%X G:%X B:%X)", col.a(), col.r(), col.g(), col.b());
+ rgb_t const col = indirect ? palette.indirect_color(index) : raw_color[index];
+ if (palette.indirect_entries() && indirect)
+ {
+ util::stream_format(title_buf,
+ _("gfxview", u8" #%1$X \u2192 %2$X (A:%3$02X R:%4$02X G:%5$02X B:%6$02X)"),
+ index, palette.pen_indirect(index),
+ col.a(), col.r(), col.g(), col.b());
+ }
+ else if (paldev && paldev->basemem().base())
+ {
+ util::stream_format(title_buf,
+ _("gfxview", " #%1$X = %2$X (A:%3$02X R:%4$02X G:%5$02X B:%6$02X)"),
+ index, paldev->read_entry(index),
+ col.a(), col.r(), col.g(), col.b());
+ }
+ else
+ {
+ util::stream_format(title_buf,
+ _("gfxview", " #%1$X (A:%2$02X R:%3$02X G:%4$02X B:%5$02X)"),
+ 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));
}
}
+ float x0, y0;
+
// expand the outer box to fit the title
- const std::string title = title_buf.str();
- titlewidth = ui_font->string_width(chheight, aspect, title);
+ std::string const title = std::move(title_buf).str();
+ float const titlewidth = ui_font->string_width(chheight, aspect, title);
x0 = 0.0f;
if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth)
x0 = boxbounds.x0 - (0.5f - 0.5f * (titlewidth + chwidth));
@@ -469,186 +1074,97 @@ static void palette_handler(mame_ui_manager &mui, render_container &container, u
mui.draw_outlined_box(container, boxbounds.x0 - x0, boxbounds.y0, boxbounds.x1 + x0, boxbounds.y1, mui.colors().gfxviewer_bg_color());
// draw the title
- x0 = 0.5f - 0.5f * titlewidth;
- y0 = boxbounds.y0 + 0.5f * chheight;
- for (auto ch : title)
- {
- container.add_char(x0, y0, chheight, aspect, rgb_t::white(), *ui_font, ch);
- x0 += ui_font->char_width(chheight, aspect, ch);
- }
+ draw_text(mui, container, title, 0.5f - 0.5f * titlewidth, boxbounds.y0 + 0.5f * chheight);
// draw the top column headers
- skip = int(chwidth / cellwidth);
- for (int x = 0; x < state.palette.columns; x += 1 + skip)
+ int const rowskip = int(chwidth / cellwidth);
+ for (int x = 0; x < m_palette.columns(); x += 1 + rowskip)
{
x0 = boxbounds.x0 + 6.0f * chwidth + float(x) * cellwidth;
y0 = boxbounds.y0 + 2.0f * chheight;
container.add_char(x0 + 0.5f * (cellwidth - chwidth), y0, chheight, aspect, rgb_t::white(), *ui_font, "0123456789ABCDEF"[x & 0xf]);
- // if we're skipping, draw a point between the character and the box to indicate which
- // one it's referring to
- if (skip != 0)
+ // if we're skipping, draw a point between the character and the box to indicate which one it's referring to
+ if (rowskip)
container.add_point(x0 + 0.5f * cellwidth, 0.5f * (y0 + chheight + cellboxbounds.y0), UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
// draw the side column headers
- skip = int(chheight / cellheight);
- for (int y = 0; y < state.palette.columns; y += 1 + skip)
-
+ int const colskip = int(chheight / cellheight);
+ for (int y = 0; y < m_palette.columns(); y += 1 + colskip)
+ {
// only display if there is data to show
- if (state.palette.offset + y * state.palette.columns < total)
+ unsigned const index = m_palette.index(0, y);
+ if (index < total)
{
- char buffer[10];
-
// if we're skipping, draw a point between the character and the box to indicate which
// one it's referring to
x0 = boxbounds.x0 + 5.5f * chwidth;
y0 = boxbounds.y0 + 3.5f * chheight + float(y) * cellheight;
- if (skip != 0)
+ if (colskip != 0)
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
- sprintf(buffer, "%5X", state.palette.offset + y * state.palette.columns);
+ auto buffer = util::string_format("%5X", index);
for (int x = 4; x >= 0; x--)
{
x0 -= ui_font->char_width(chheight, aspect, buffer[x]);
container.add_char(x0, y0 + 0.5f * (cellheight - chheight), chheight, aspect, rgb_t::white(), *ui_font, buffer[x]);
}
}
+ }
// now add the rectangles for the colors
- for (int y = 0; y < state.palette.columns; y++)
- for (int x = 0; x < state.palette.columns; x++)
+ for (int y = 0; y < m_palette.columns(); y++)
+ {
+ for (int x = 0; x < m_palette.columns(); x++)
{
- int index = state.palette.offset + y * state.palette.columns + x;
+ int const index = m_palette.index(x, y);
if (index < total)
{
- const pen_t pen = state.palette.which ? palette->indirect_color(index) : raw_color[index];
- container.add_rect(cellboxbounds.x0 + x * cellwidth, cellboxbounds.y0 + y * cellheight,
- cellboxbounds.x0 + (x + 1) * cellwidth, cellboxbounds.y0 + (y + 1) * cellheight,
- 0xff000000 | pen, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ pen_t const pen = indirect ? palette.indirect_color(index) : raw_color[index];
+ container.add_rect(
+ cellboxbounds.x0 + x * cellwidth, cellboxbounds.y0 + y * cellheight,
+ cellboxbounds.x0 + (x + 1) * cellwidth, cellboxbounds.y0 + (y + 1) * cellheight,
+ 0xff000000 | pen, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
}
-
- // handle keys
- palette_handle_keys(mui.machine(), state);
-}
-
-
-//-------------------------------------------------
-// palette_handle_keys - handle key inputs for
-// the palette viewer
-//-------------------------------------------------
-
-static void palette_handle_keys(running_machine &machine, ui_gfx_state &state)
-{
- device_palette_interface *palette = state.palette.interface;
- // handle zoom (minus,plus)
- if (machine.ui_input().pressed(IPT_UI_ZOOM_OUT))
- state.palette.columns = std::min<uint8_t>(state.palette.columns * 2, 64);
- if (machine.ui_input().pressed(IPT_UI_ZOOM_IN))
- state.palette.columns = std::max<uint8_t>(state.palette.columns / 2, 4);
- if (machine.ui_input().pressed(IPT_UI_ZOOM_DEFAULT))
- state.palette.columns = 16;
-
- // handle colormap selection (open bracket,close bracket)
- if (machine.ui_input().pressed(IPT_UI_PREV_GROUP))
- {
- if (state.palette.which)
- state.palette.which = 0;
- else if (state.palette.devindex > 0)
- {
- state.palette.devindex--;
- palette_set_device(machine, state);
- palette = state.palette.interface;
- state.palette.which = (palette->indirect_entries() > 0);
- }
- }
- if (machine.ui_input().pressed(IPT_UI_NEXT_GROUP))
- {
- if (!state.palette.which && palette->indirect_entries() > 0)
- state.palette.which = 1;
- else if (state.palette.devindex < state.palette.devcount - 1)
- {
- state.palette.devindex++;
- palette_set_device(machine, state);
- palette = state.palette.interface;
- state.palette.which = 0;
- }
}
- // cache some info in locals
- const int total = state.palette.which ? palette->indirect_entries() : palette->entries();
-
- // determine number of entries per row and total
- const int rowcount = state.palette.columns;
- const int screencount = rowcount * rowcount;
-
- // handle keyboard navigation
- if (machine.ui_input().pressed_repeat(IPT_UI_UP, 4))
- state.palette.offset -= rowcount;
- if (machine.ui_input().pressed_repeat(IPT_UI_DOWN, 4))
- state.palette.offset += rowcount;
- if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_UP, 6))
- state.palette.offset -= screencount;
- if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_DOWN, 6))
- state.palette.offset += screencount;
- if (machine.ui_input().pressed_repeat(IPT_UI_HOME, 4))
- state.palette.offset = 0;
- if (machine.ui_input().pressed_repeat(IPT_UI_END, 4))
- state.palette.offset = total;
-
- // clamp within range
- if (state.palette.offset + screencount > ((total + rowcount - 1) / rowcount) * rowcount)
- state.palette.offset = ((total + rowcount - 1) / rowcount) * rowcount - screencount;
- if (state.palette.offset < 0)
- state.palette.offset = 0;
+ // handle keys
+ m_palette.handle_keys(m_machine);
+ return handle_general_keys(uistate);
}
-
-/***************************************************************************
- GRAPHICS VIEWER
-***************************************************************************/
-
-//-------------------------------------------------
-// gfxset_handler - handler for the graphics
-// viewer
-//-------------------------------------------------
-
-static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state)
+uint32_t gfx_viewer::handle_gfxset(mame_ui_manager &mui, render_container &container, bool uistate)
{
- render_font *ui_font = mui.get_font();
- int dev = state.gfxset.devindex;
- int set = state.gfxset.set;
- ui_gfx_info &info = state.gfxdev[dev];
- device_gfx_interface &interface = *info.interface;
- gfx_element &gfx = *interface.gfx(set);
- float x0, y0;
- render_bounds cellboxbounds;
- render_bounds boxbounds;
- float cellboxwidth, cellboxheight;
- int targwidth = mui.machine().render().ui_target().width();
- int targheight = mui.machine().render().ui_target().height();
- int cellxpix, cellypix;
- int xcells;
- float pixelscale = 0.0f;
- int skip;
+ // get graphics info
+ auto &info = m_gfxset.m_devices[m_gfxset.m_device];
+ auto &set = info.set(m_gfxset.m_set);
+ device_gfx_interface &interface = info.interface();
+ gfx_element &gfx = *interface.gfx(m_gfxset.m_set);
+
+ // get some UI metrics
+ render_font *const ui_font = mui.get_font();
+ int const targwidth = m_machine.render().ui_target().width();
+ int const targheight = m_machine.render().ui_target().height();
+ float const aspect = m_machine.render().ui_aspect(&container);
+ float const chheight = mui.get_line_height();
+ float const chwidth = ui_font->char_width(chheight, aspect, '0');
// add a half character padding for the box
- const float aspect = mui.machine().render().ui_aspect(&container);
- const float chheight = mui.get_line_height();
- const float chwidth = ui_font->char_width(chheight, aspect, '0');
- boxbounds.x0 = 0.0f + 0.5f * chwidth;
- boxbounds.x1 = 1.0f - 0.5f * chwidth;
- boxbounds.y0 = 0.0f + 0.5f * chheight;
- boxbounds.y1 = 1.0f - 0.5f * chheight;
+ render_bounds boxbounds{
+ 0.0f + (0.5f * chwidth),
+ 0.0f + (0.5f * chheight),
+ 1.0f - (0.5f * chwidth),
+ 1.0f - (0.5f * chheight) };
// the character cell box bounds starts a half character in from the box
- cellboxbounds = boxbounds;
+ render_bounds cellboxbounds = boxbounds;
cellboxbounds.x0 += 0.5f * chwidth;
- cellboxbounds.x1 -= 0.5f * chwidth;
cellboxbounds.y0 += 0.5f * chheight;
+ cellboxbounds.x1 -= 0.5f * chwidth;
cellboxbounds.y1 -= 0.5f * chheight;
// add space on the left for 5 characters of text, plus a half character of padding
@@ -658,92 +1174,110 @@ static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui
cellboxbounds.y0 += 3.0f * chheight;
// convert back to pixels
- cellboxwidth = (cellboxbounds.x1 - cellboxbounds.x0) * float(targwidth);
- cellboxheight = (cellboxbounds.y1 - cellboxbounds.y0) * float(targheight);
+ float cellboxwidth = cellboxbounds.width() * float(targwidth);
+ float cellboxheight = cellboxbounds.height() * float(targheight);
// compute the number of source pixels in a cell
- cellxpix = 1 + ((info.rotate[set] & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width());
- cellypix = 1 + ((info.rotate[set] & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height());
+ int const cellxpix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width());
+ int const cellypix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height());
// compute the largest pixel scale factor that still fits
- xcells = info.columns[set];
+ int xcells = set.m_columns;
+ float pixelscale = 0.0f;
while (xcells > 1)
{
- pixelscale = (cellboxwidth / xcells) / cellxpix;
- if (pixelscale != 0)
+ pixelscale = cellboxwidth / (xcells * cellxpix);
+ if (set.m_integer_scale)
+ pixelscale = std::floor(pixelscale);
+ if (0.25f <= pixelscale)
break;
xcells--;
}
- info.columns[set] = xcells;
-
- if (pixelscale <= 0.0f)
- pixelscale = 1.0f;
+ if (0.0f == pixelscale)
+ pixelscale = cellboxwidth / (xcells * cellxpix);
// in the Y direction, we just display as many as we can
- const int ycells = int(cellboxheight / (pixelscale * cellypix));
+ int ycells = int(cellboxheight / (pixelscale * cellypix));
+ if (!ycells)
+ {
+ ycells = 1;
+ pixelscale = cellboxheight / cellypix;
+ xcells = int(cellboxwidth / (pixelscale * cellxpix));
+ }
// now determine the actual cellbox size
+ set.m_columns = xcells;
cellboxwidth = std::min(cellboxwidth, xcells * pixelscale * cellxpix);
cellboxheight = std::min(cellboxheight, ycells * pixelscale * cellypix);
// compute the size of a single cell at this pixel scale factor
- const float cellwidth = (cellboxwidth / xcells) / targwidth;
- const float cellheight = (cellboxheight / ycells) / targheight;
+ float const cellwidth = (cellboxwidth / xcells) / targwidth;
+ float const cellheight = (cellboxheight / ycells) / targheight;
// working from the new width/height, recompute the boxbounds
- const float fullwidth = cellboxwidth / targwidth + 6.5f * chwidth;
- const float fullheight = cellboxheight / targheight + 4.0f * chheight;
+ float const fullwidth = cellboxwidth / targwidth + 6.5f * chwidth;
+ float const fullheight = cellboxheight / targheight + 4.0f * chheight;
// recompute boxbounds from this
boxbounds.x0 = (1.0f - fullwidth) * 0.5f;
- boxbounds.x1 = boxbounds.x0 + fullwidth;
boxbounds.y0 = (1.0f - fullheight) * 0.5f;
+ boxbounds.x1 = boxbounds.x0 + fullwidth;
boxbounds.y1 = boxbounds.y0 + fullheight;
// recompute cellboxbounds
cellboxbounds.x0 = boxbounds.x0 + 6.0f * chwidth;
- cellboxbounds.x1 = cellboxbounds.x0 + cellboxwidth / float(targwidth);
cellboxbounds.y0 = boxbounds.y0 + 3.5f * chheight;
+ cellboxbounds.x1 = cellboxbounds.x0 + cellboxwidth / float(targwidth);
cellboxbounds.y1 = cellboxbounds.y0 + cellboxheight / float(targheight);
// figure out the title
std::ostringstream title_buf;
- util::stream_format(title_buf, "'%s' %d/%d", interface.device().tag(), set, info.setcount - 1);
+ util::stream_format(title_buf,
+ _("gfxview", "[root%1$s] %2$d/%3$d"),
+ interface.device().tag(),
+ m_gfxset.m_set, info.setcount() - 1);
// if the mouse pointer is over a pixel in a tile, add some info about the tile and pixel
bool found_pixel = false;
- int32_t mouse_target_x, mouse_target_y;
float mouse_x, mouse_y;
- bool mouse_button;
- render_target *const mouse_target = mui.machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
- if (mouse_target != nullptr && mouse_target->map_point_container(mouse_target_x, mouse_target_y, container, mouse_x, mouse_y)
- && cellboxbounds.x0 <= mouse_x && cellboxbounds.x1 > mouse_x
- && cellboxbounds.y0 <= mouse_y && cellboxbounds.y1 > mouse_y)
+ if (map_mouse(container, cellboxbounds, mouse_x, mouse_y))
{
- int code = info.offset[set] + int((mouse_x - cellboxbounds.x0) / cellwidth) + int((mouse_y - cellboxbounds.y0) / cellheight) * xcells;
+ int const code = set.m_offset + int((mouse_x - cellboxbounds.x0) / cellwidth) + int((mouse_y - cellboxbounds.y0) / cellheight) * xcells;
int xpixel = int((mouse_x - cellboxbounds.x0) / (cellwidth / cellxpix)) % cellxpix;
int ypixel = int((mouse_y - cellboxbounds.y0) / (cellheight / cellypix)) % cellypix;
- if (code < gfx.elements() && xpixel < (cellxpix - 1) && ypixel < (cellypix - 1))
+ if ((code < gfx.elements()) && (xpixel < (cellxpix - 1)) && (ypixel < (cellypix - 1)))
{
found_pixel = true;
- if (info.rotate[set] & ORIENTATION_FLIP_X)
+ if (set.m_rotate & ORIENTATION_FLIP_X)
xpixel = (cellxpix - 2) - xpixel;
- if (info.rotate[set] & ORIENTATION_FLIP_Y)
+ if (set.m_rotate & ORIENTATION_FLIP_Y)
ypixel = (cellypix - 2) - ypixel;
- if (info.rotate[set] & ORIENTATION_SWAP_XY)
+ if (set.m_rotate & ORIENTATION_SWAP_XY)
std::swap(xpixel, ypixel);
- uint8_t pixdata = gfx.get_data(code)[xpixel + ypixel * gfx.rowbytes()];
- util::stream_format(title_buf, " #%X:%X @ %d,%d = %X",
- code, info.color[set], xpixel, ypixel,
- gfx.colorbase() + info.color[set] * gfx.granularity() + pixdata);
+ uint8_t const pixdata = gfx.get_data(code)[xpixel + ypixel * gfx.rowbytes()];
+ util::stream_format(title_buf,
+ _("gfxview", " #%1$X:%2$X (%3$d %4$d) = %5$X"),
+ 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)
- util::stream_format(title_buf, " %dx%d COLOR %X/%X", gfx.width(), gfx.height(), info.color[set], info.color_count[set]);
+ util::stream_format(title_buf,
+ _("gfxview", u8" %1$d\u00d7%2$d color %3$X/%4$X"),
+ gfx.width(), gfx.height(),
+ set.m_color, set.m_color_count);
+
+ float x0, y0;
// expand the outer box to fit the title
- const std::string title = title_buf.str();
- const float titlewidth = ui_font->string_width(chheight, aspect, title);
+ std::string const title = std::move(title_buf).str();
+ float const titlewidth = ui_font->string_width(chheight, aspect, title);
x0 = 0.0f;
if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth)
x0 = boxbounds.x0 - (0.5f - 0.5f * (titlewidth + chwidth));
@@ -752,312 +1286,85 @@ static void gfxset_handler(mame_ui_manager &mui, render_container &container, ui
mui.draw_outlined_box(container, boxbounds.x0 - x0, boxbounds.y0, boxbounds.x1 + x0, boxbounds.y1, mui.colors().gfxviewer_bg_color());
// draw the title
- x0 = 0.5f - 0.5f * titlewidth;
- y0 = boxbounds.y0 + 0.5f * chheight;
- for (auto ch : title)
- {
- container.add_char(x0, y0, chheight, aspect, rgb_t::white(), *ui_font, ch);
- x0 += ui_font->char_width(chheight, aspect, ch);
- }
+ draw_text(mui, container, title, 0.5f - 0.5f * titlewidth, boxbounds.y0 + 0.5f * chheight);
// draw the top column headers
- skip = int(chwidth / cellwidth);
- for (int x = 0; x < xcells; x += 1 + skip)
+ int const colskip = int(chwidth / cellwidth);
+ for (int x = 0; x < xcells; x += 1 + colskip)
{
x0 = boxbounds.x0 + 6.0f * chwidth + float(x) * cellwidth;
y0 = boxbounds.y0 + 2.0f * chheight;
container.add_char(x0 + 0.5f * (cellwidth - chwidth), y0, chheight, aspect, rgb_t::white(), *ui_font, "0123456789ABCDEF"[x & 0xf]);
- // if we're skipping, draw a point between the character and the box to indicate which
- // one it's referring to
- if (skip != 0)
+ // if we're skipping, draw a point between the character and the box to indicate which one it's referring to
+ if (colskip)
container.add_point(x0 + 0.5f * cellwidth, 0.5f * (y0 + chheight + boxbounds.y0 + 3.5f * chheight), UI_LINE_WIDTH, rgb_t::white(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
// draw the side column headers
- skip = int(chheight / cellheight);
- for (int y = 0; y < ycells; y += 1 + skip)
-
+ int const rowskip = int(chheight / cellheight);
+ for (int y = 0; y < ycells; y += 1 + rowskip)
+ {
// only display if there is data to show
- if (info.offset[set] + y * xcells < gfx.elements())
+ if (set.m_offset + (y * xcells) < gfx.elements())
{
- char buffer[10];
-
- // if we're skipping, draw a point between the character and the box to indicate which
- // one it's referring to
+ // if we're skipping, draw a point between the character and the box to indicate which one it's referring to
x0 = boxbounds.x0 + 5.5f * chwidth;
y0 = boxbounds.y0 + 3.5f * chheight + float(y) * cellheight;
- if (skip != 0)
+ if (rowskip)
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
- sprintf(buffer, "%5X", info.offset[set] + 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]);
container.add_char(x0, y0 + 0.5f * (cellheight - chheight), chheight, aspect, rgb_t::white(), *ui_font, buffer[x]);
}
}
+ }
// update the bitmap
- gfxset_update_bitmap(mui.machine(), state, xcells, ycells, gfx);
+ update_gfxset_bitmap(xcells, ycells, gfx);
// add the final quad
- container.add_quad(cellboxbounds.x0, cellboxbounds.y0, cellboxbounds.x1, cellboxbounds.y1,
- rgb_t::white(), state.texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container.add_quad(
+ cellboxbounds.x0, cellboxbounds.y0, cellboxbounds.x1, cellboxbounds.y1,
+ rgb_t::white(), m_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
// handle keyboard navigation before drawing
- gfxset_handle_keys(mui.machine(), state, xcells, ycells);
-}
-
-
-//-------------------------------------------------
-// gfxset_handle_keys - handle keys for the
-// graphics viewer
-//-------------------------------------------------
-
-static void gfxset_handle_keys(running_machine &machine, ui_gfx_state &state, int xcells, int ycells)
-{
- // handle gfxset selection (open bracket,close bracket)
- if (machine.ui_input().pressed(IPT_UI_PREV_GROUP))
- {
- if (state.gfxset.set > 0)
- state.gfxset.set--;
- else if (state.gfxset.devindex > 0)
- {
- state.gfxset.devindex--;
- state.gfxset.set = state.gfxdev[state.gfxset.devindex].setcount - 1;
- }
- state.bitmap_dirty = true;
- }
- if (machine.ui_input().pressed(IPT_UI_NEXT_GROUP))
- {
- if (state.gfxset.set < state.gfxdev[state.gfxset.devindex].setcount - 1)
- state.gfxset.set++;
- else if (state.gfxset.devindex < state.gfxset.devcount - 1)
- {
- state.gfxset.devindex++;
- state.gfxset.set = 0;
- }
- state.bitmap_dirty = true;
- }
-
- // cache some info in locals
- int dev = state.gfxset.devindex;
- int set = state.gfxset.set;
- ui_gfx_info &info = state.gfxdev[dev];
- gfx_element &gfx = *info.interface->gfx(set);
-
- // handle cells per line (minus,plus)
- if (machine.ui_input().pressed(IPT_UI_ZOOM_OUT) && (xcells < 128))
- { info.columns[set] = xcells + 1; state.bitmap_dirty = true; }
-
- if (machine.ui_input().pressed(IPT_UI_ZOOM_IN) && (xcells > 2))
- { info.columns[set] = xcells - 1; state.bitmap_dirty = true; }
-
- if (machine.ui_input().pressed(IPT_UI_ZOOM_DEFAULT) && (xcells != 16))
- { info.columns[set] = 16; state.bitmap_dirty = true; }
-
- // handle rotation (R)
- if (machine.ui_input().pressed(IPT_UI_ROTATE))
- {
- info.rotate[set] = orientation_add(ROT90, info.rotate[set]);
- state.bitmap_dirty = true;
- }
-
- // handle navigation within the cells (up,down,pgup,pgdown)
- if (machine.ui_input().pressed_repeat(IPT_UI_UP, 4))
- { info.offset[set] -= xcells; state.bitmap_dirty = true; }
- if (machine.ui_input().pressed_repeat(IPT_UI_DOWN, 4))
- { info.offset[set] += xcells; state.bitmap_dirty = true; }
- if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_UP, 6))
- { info.offset[set] -= xcells * ycells; state.bitmap_dirty = true; }
- if (machine.ui_input().pressed_repeat(IPT_UI_PAGE_DOWN, 6))
- { info.offset[set] += xcells * ycells; state.bitmap_dirty = true; }
- if (machine.ui_input().pressed_repeat(IPT_UI_HOME, 4))
- { info.offset[set] = 0; state.bitmap_dirty = true; }
- if (machine.ui_input().pressed_repeat(IPT_UI_END, 4))
- { info.offset[set] = gfx.elements(); state.bitmap_dirty = true; }
-
- // clamp within range
- if (info.offset[set] + xcells * ycells > ((gfx.elements() + xcells - 1) / xcells) * xcells)
- {
- info.offset[set] = ((gfx.elements() + xcells - 1) / xcells) * xcells - xcells * ycells;
- state.bitmap_dirty = true;
- }
- if (info.offset[set] < 0)
- { info.offset[set] = 0; state.bitmap_dirty = true; }
-
- // handle color selection (left,right)
- if (machine.ui_input().pressed_repeat(IPT_UI_LEFT, 4))
- { info.color[set] -= 1; state.bitmap_dirty = true; }
- if (machine.ui_input().pressed_repeat(IPT_UI_RIGHT, 4))
- { info.color[set] += 1; state.bitmap_dirty = true; }
-
- // clamp within range
- if (info.color[set] >= info.color_count[set])
- { info.color[set] = info.color_count[set] - 1; state.bitmap_dirty = true; }
- if (info.color[set] < 0)
- { info.color[set] = 0; state.bitmap_dirty = true; }
-}
-
-
-//-------------------------------------------------
-// gfxset_update_bitmap - redraw the current
-// graphics view bitmap
-//-------------------------------------------------
-
-static void gfxset_update_bitmap(running_machine &machine, ui_gfx_state &state, int xcells, int ycells, gfx_element &gfx)
-{
- const int dev = state.gfxset.devindex;
- const int set = state.gfxset.set;
- ui_gfx_info &info = state.gfxdev[dev];
-
- // compute the number of source pixels in a cell
- const int cellxpix = 1 + ((info.rotate[set] & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width());
- const int cellypix = 1 + ((info.rotate[set] & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height());
-
- // realloc the bitmap if it is too small
- if (!state.bitmap.valid() || !state.texture || state.bitmap.width() != cellxpix * xcells || state.bitmap.height() != cellypix * ycells)
- {
- // free the old stuff
- machine.render().texture_free(state.texture);
- state.bitmap.reset();
-
- // allocate new stuff
- state.bitmap.allocate(cellxpix * xcells, cellypix * ycells);
- state.texture = machine.render().texture_alloc();
- state.texture->set_bitmap(state.bitmap, state.bitmap.cliprect(), TEXFORMAT_ARGB32);
-
- // force a redraw
- state.bitmap_dirty = true;
- }
-
- // handle the redraw
- if (state.bitmap_dirty)
- {
- // loop over rows
- for (int y = 0, index = info.offset[set]; y < ycells; y++)
- {
- // make a rect that covers this row
- rectangle cellbounds(0, state.bitmap.width() - 1, y * cellypix, (y + 1) * cellypix - 1);
-
- // only display if there is data to show
- if (index < gfx.elements())
- {
- // draw the individual cells
- for (int x = 0; x < xcells; x++, index++)
- {
- // update the bounds for this cell
- cellbounds.min_x = x * cellxpix;
- cellbounds.max_x = (x + 1) * cellxpix - 1;
-
- if (index < gfx.elements()) // only render if there is data
- gfxset_draw_item(machine, gfx, index, state.bitmap, cellbounds.min_x, cellbounds.min_y, info.color[set], info.rotate[set], info.palette[set]);
- else // otherwise, fill with transparency
- state.bitmap.fill(0, cellbounds);
- }
- }
- else // otherwise, fill with transparency
- state.bitmap.fill(0, cellbounds);
- }
-
- // reset the texture to force an update
- state.texture->set_bitmap(state.bitmap, state.bitmap.cliprect(), TEXFORMAT_ARGB32);
- state.bitmap_dirty = false;
- }
-}
-
-
-//-------------------------------------------------
-// gfxset_draw_item - draw a single item into
-// the view
-//-------------------------------------------------
-
-static void gfxset_draw_item(running_machine &machine, gfx_element &gfx, int index, bitmap_rgb32 &bitmap, int dstx, int dsty, int color, int rotate, device_palette_interface *dpalette)
-{
- int width = (rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width();
- int height = (rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height();
- rgb_t const *const palette = dpalette->palette()->entry_list_raw() + gfx.colorbase() + color * gfx.granularity();
-
- // loop over rows in the cell
- for (int y = 0; y < height; y++)
- {
- uint32_t *dest = &bitmap.pix(dsty + y, dstx);
- const uint8_t *src = gfx.get_data(index);
-
- // loop over columns in the cell
- for (int x = 0; x < width; x++)
- {
- int effx = x, effy = y;
- const uint8_t *s;
-
- // compute effective x,y values after rotation
- if (!(rotate & ORIENTATION_SWAP_XY))
- {
- if (rotate & ORIENTATION_FLIP_X)
- effx = gfx.width() - 1 - effx;
- if (rotate & ORIENTATION_FLIP_Y)
- effy = gfx.height() - 1 - effy;
- }
- else
- {
- if (rotate & ORIENTATION_FLIP_X)
- effx = gfx.height() - 1 - effx;
- if (rotate & ORIENTATION_FLIP_Y)
- effy = gfx.width() - 1 - effy;
- std::swap(effx, effy);
- }
-
- // get a pointer to the start of this source row
- s = src + effy * gfx.rowbytes();
-
- // extract the pixel
- *dest++ = 0xff000000 | palette[s[effx]];
- }
- }
+ if (m_gfxset.handle_keys(m_machine, xcells, ycells))
+ m_bitmap_dirty = true;
+ return handle_general_keys(uistate);
}
-
-/***************************************************************************
- TILEMAP VIEWER
-***************************************************************************/
-
-//-------------------------------------------------
-// tilemap_handler - handler for the tilemap
-// viewer
-//-------------------------------------------------
-
-static void tilemap_handler(mame_ui_manager &mui, render_container &container, ui_gfx_state &state)
+uint32_t gfx_viewer::handle_tilemap(mame_ui_manager &mui, render_container &container, bool uistate)
{
- render_font *ui_font = mui.get_font();
- render_bounds mapboxbounds;
- render_bounds boxbounds;
- const int targwidth = mui.machine().render().ui_target().width();
- const int targheight = mui.machine().render().ui_target().height();
- float titlewidth;
- float x0, y0;
- int mapboxwidth, mapboxheight;
+ // get some UI metrics
+ render_font *const ui_font = mui.get_font();
+ int const targwidth = m_machine.render().ui_target().width();
+ int const targheight = m_machine.render().ui_target().height();
+ float const aspect = m_machine.render().ui_aspect(&container);
+ float const chheight = mui.get_line_height();
+ float const chwidth = ui_font->char_width(chheight, aspect, '0');
// get the size of the tilemap itself
- tilemap_t *tilemap = mui.machine().tilemap().find(state.tilemap.which);
- uint32_t mapwidth = tilemap->width();
- uint32_t mapheight = tilemap->height();
- if (state.tilemap.rotate & ORIENTATION_SWAP_XY)
+ tilemap_t &tilemap = *m_machine.tilemap().find(m_tilemap.index());
+ uint32_t mapwidth = tilemap.width();
+ uint32_t mapheight = tilemap.height();
+ if (m_tilemap.rotate() & ORIENTATION_SWAP_XY)
std::swap(mapwidth, mapheight);
// add a half character padding for the box
- const float aspect = mui.machine().render().ui_aspect(&container);
- const float chheight = mui.get_line_height();
- const float chwidth = ui_font->char_width(chheight, aspect, '0');
- boxbounds.x0 = 0.0f + 0.5f * chwidth;
- boxbounds.x1 = 1.0f - 0.5f * chwidth;
- boxbounds.y0 = 0.0f + 0.5f * chheight;
- boxbounds.y1 = 1.0f - 0.5f * chheight;
+ render_bounds boxbounds{
+ 0.0f + (0.5f * chwidth),
+ 0.0f + (0.5f * chheight),
+ 1.0f - (0.5f * chwidth),
+ 1.0f - (0.5f * chheight) };
// the tilemap box bounds starts a half character in from the box
- mapboxbounds = boxbounds;
+ render_bounds mapboxbounds = boxbounds;
mapboxbounds.x0 += 0.5f * chwidth;
mapboxbounds.x1 -= 0.5f * chwidth;
mapboxbounds.y0 += 0.5f * chheight;
@@ -1067,11 +1374,11 @@ static void tilemap_handler(mame_ui_manager &mui, render_container &container, u
mapboxbounds.y0 += 1.5f * chheight;
// convert back to pixels
- mapboxwidth = (mapboxbounds.x1 - mapboxbounds.x0) * float(targwidth);
- mapboxheight = (mapboxbounds.y1 - mapboxbounds.y0) * float(targheight);
+ int mapboxwidth = mapboxbounds.width() * float(targwidth);
+ int mapboxheight = mapboxbounds.height() * float(targheight);
float pixelscale;
- if (state.tilemap.auto_zoom)
+ if (m_tilemap.auto_zoom())
{
// determine the maximum integral scaling factor
pixelscale = std::min(std::floor(mapboxwidth / mapwidth), std::floor(mapboxheight / mapheight));
@@ -1079,7 +1386,7 @@ static void tilemap_handler(mame_ui_manager &mui, render_container &container, u
}
else
{
- pixelscale = state.tilemap.zoom_frac ? (1.0f / state.tilemap.zoom) : float(state.tilemap.zoom);
+ pixelscale = m_tilemap.zoom_scale();
}
// recompute the final box size
@@ -1100,43 +1407,49 @@ static void tilemap_handler(mame_ui_manager &mui, render_container &container, u
// figure out the title
std::ostringstream title_buf;
- util::stream_format(title_buf, "TILEMAP %d/%d", state.tilemap.which + 1, mui.machine().tilemap().count());
+ util::stream_format(title_buf,
+ (m_tilemap.flags() != TILEMAP_DRAW_ALL_CATEGORIES) ? _("gfxview", "Tilemap %1$d/%2$d category %3$u") : _("gfxview", "Tilemap %1$d/%2$d "),
+ m_tilemap.index() + 1, m_machine.tilemap().count(),
+ m_tilemap.flags());
// if the mouse pointer is over a tile, add some info about its coordinates and color
- int32_t mouse_target_x, mouse_target_y;
float mouse_x, mouse_y;
- bool mouse_button;
- render_target *const mouse_target = mui.machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
- if (mouse_target != nullptr && mouse_target->map_point_container(mouse_target_x, mouse_target_y, container, mouse_x, mouse_y)
- && mapboxbounds.x0 <= mouse_x && mapboxbounds.x1 > mouse_x
- && mapboxbounds.y0 <= mouse_y && mapboxbounds.y1 > mouse_y)
+ if (map_mouse(container, mapboxbounds, mouse_x, mouse_y))
{
int xpixel = (mouse_x - mapboxbounds.x0) * targwidth;
int ypixel = (mouse_y - mapboxbounds.y0) * targheight;
- if (state.tilemap.rotate & ORIENTATION_FLIP_X)
+ if (m_tilemap.rotate() & ORIENTATION_FLIP_X)
xpixel = (mapboxwidth - 1) - xpixel;
- if (state.tilemap.rotate & ORIENTATION_FLIP_Y)
+ if (m_tilemap.rotate() & ORIENTATION_FLIP_Y)
ypixel = (mapboxheight - 1) - ypixel;
- if (state.tilemap.rotate & ORIENTATION_SWAP_XY)
+ if (m_tilemap.rotate() & ORIENTATION_SWAP_XY)
std::swap(xpixel, ypixel);
- uint32_t col = ((std::lround(xpixel / pixelscale) + state.tilemap.xoffs) / tilemap->tilewidth()) % tilemap->cols();
- uint32_t row = ((std::lround(ypixel / pixelscale) + state.tilemap.yoffs) / tilemap->tileheight()) % tilemap->rows();
+ uint32_t const col = ((std::lround(xpixel / pixelscale) + m_tilemap.xoffs()) / tilemap.tilewidth()) % tilemap.cols();
+ uint32_t const row = ((std::lround(ypixel / pixelscale) + m_tilemap.yoffs()) / tilemap.tileheight()) % tilemap.rows();
uint8_t gfxnum;
uint32_t code, color;
- tilemap->get_info_debug(col, row, gfxnum, code, color);
- util::stream_format(title_buf, " @ %d,%d = GFX%d #%X:%X",
- col * tilemap->tilewidth(), row * tilemap->tileheight(),
- int(gfxnum), code, color);
+ tilemap.get_info_debug(col, row, gfxnum, code, color);
+ util::stream_format(title_buf,
+ _("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
- util::stream_format(title_buf, " %dx%d OFFS %d,%d", tilemap->width(), tilemap->height(), state.tilemap.xoffs, state.tilemap.yoffs);
-
- if (state.tilemap.flags != TILEMAP_DRAW_ALL_CATEGORIES)
- util::stream_format(title_buf, " CAT %d", state.tilemap.flags);
+ {
+ util::stream_format(title_buf,
+ _("gfxview", u8" %1$d\u00d7%2$d origin (%3$d %4$d)"),
+ tilemap.width(), tilemap.height(),
+ m_tilemap.xoffs(), m_tilemap.yoffs());
+ }
// expand the outer box to fit the title
- const std::string title = title_buf.str();
- titlewidth = ui_font->string_width(chheight, aspect, title);
+ std::string const title = std::move(title_buf).str();
+ float const titlewidth = ui_font->string_width(chheight, aspect, title);
if (boxbounds.x1 - boxbounds.x0 < titlewidth + chwidth)
{
boxbounds.x0 = 0.5f - 0.5f * (titlewidth + chwidth);
@@ -1147,250 +1460,158 @@ static void tilemap_handler(mame_ui_manager &mui, render_container &container, u
mui.draw_outlined_box(container, boxbounds.x0, boxbounds.y0, boxbounds.x1, boxbounds.y1, mui.colors().gfxviewer_bg_color());
// draw the title
- x0 = 0.5f - 0.5f * titlewidth;
- y0 = boxbounds.y0 + 0.5f * chheight;
- for (auto ch : title)
- {
- container.add_char(x0, y0, chheight, aspect, rgb_t::white(), *ui_font, ch);
- x0 += ui_font->char_width(chheight, aspect, ch);
- }
+ draw_text(mui, container, title, 0.5f - 0.5f * titlewidth, boxbounds.y0 + 0.5f * chheight);
// update the bitmap
- tilemap_update_bitmap(mui.machine(), state, std::lround(mapboxwidth / pixelscale), std::lround(mapboxheight / pixelscale));
+ update_tilemap_bitmap(std::lround(mapboxwidth / pixelscale), std::lround(mapboxheight / pixelscale));
// add the final quad
- container.add_quad(mapboxbounds.x0, mapboxbounds.y0,
- mapboxbounds.x1, mapboxbounds.y1,
- rgb_t::white(), state.texture,
- PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(state.tilemap.rotate));
+ container.add_quad(
+ mapboxbounds.x0, mapboxbounds.y0,
+ mapboxbounds.x1, mapboxbounds.y1,
+ rgb_t::white(), m_texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(m_tilemap.rotate()));
// handle keyboard input
- tilemap_handle_keys(mui.machine(), state, pixelscale);
+ if (m_tilemap.handle_keys(m_machine, pixelscale))
+ m_bitmap_dirty = true;
+ return handle_general_keys(uistate);
}
-//-------------------------------------------------
-// tilemap_handle_keys - handle keys for the
-// tilemap view
-//-------------------------------------------------
-
-static void tilemap_handle_keys(running_machine &machine, ui_gfx_state &state, float pixelscale)
+void gfx_viewer::update_gfxset_bitmap(int xcells, int ycells, gfx_element &gfx)
{
- // handle tilemap selection (open bracket,close bracket)
- if (machine.ui_input().pressed(IPT_UI_PREV_GROUP) && state.tilemap.which > 0)
- { state.tilemap.which--; state.bitmap_dirty = true; }
- if (machine.ui_input().pressed(IPT_UI_NEXT_GROUP) && state.tilemap.which < machine.tilemap().count() - 1)
- { state.tilemap.which++; state.bitmap_dirty = true; }
+ auto const &info = m_gfxset.m_devices[m_gfxset.m_device];
+ auto const &set = info.set(m_gfxset.m_set);
- // cache some info in locals
- tilemap_t *tilemap = machine.tilemap().find(state.tilemap.which);
- uint32_t mapwidth = tilemap->width();
- uint32_t mapheight = tilemap->height();
+ // compute the number of source pixels in a cell
+ int const cellxpix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width());
+ int const cellypix = 1 + ((set.m_rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height());
- const bool at_max_zoom = !state.tilemap.auto_zoom && !state.tilemap.zoom_frac && state.tilemap.zoom == MAX_ZOOM_LEVEL;
- const bool at_min_zoom = !state.tilemap.auto_zoom && state.tilemap.zoom_frac && state.tilemap.zoom == MIN_ZOOM_LEVEL;
+ // reallocate the bitmap if it is too small
+ resize_bitmap(cellxpix * xcells, cellypix * ycells);
- // handle zoom (minus,plus)
- if (machine.ui_input().pressed(IPT_UI_ZOOM_OUT) && !at_min_zoom)
+ // handle the redraw
+ if (m_bitmap_dirty)
{
- if (state.tilemap.auto_zoom)
- {
- // auto zoom never uses fractional factors
- state.tilemap.zoom = std::lround(pixelscale) - 1;
- state.tilemap.zoom_frac = !state.tilemap.zoom;
- if (state.tilemap.zoom_frac)
- state.tilemap.zoom = 2;
- state.tilemap.auto_zoom = false;
- }
- else if (state.tilemap.zoom_frac)
- {
- // remaining in fractional zoom range
- state.tilemap.zoom++;
- }
- else if (state.tilemap.zoom == 1)
- {
- // entering fractional zoom range
- state.tilemap.zoom++;
- state.tilemap.zoom_frac = true;
- }
- else
+ // pre-fill with transparency
+ m_bitmap.fill(0);
+
+ // loop over rows
+ for (int y = 0, index = set.m_offset; y < ycells; y++)
{
- // remaining in integer zoom range
- state.tilemap.zoom--;
- }
+ // make a rectangle that covers this row
+ rectangle cellbounds(0, m_bitmap.width() - 1, y * cellypix, (y + 1) * cellypix - 1);
+
+ // only display if there is data to show
+ if (index < gfx.elements())
+ {
+ // draw the individual cells
+ for (int x = 0; x < xcells; x++, index++)
+ {
+ // update the bounds for this cell
+ cellbounds.min_x = x * cellxpix;
+ cellbounds.max_x = (x + 1) * cellxpix - 1;
- state.bitmap_dirty = true;
+ if (index < gfx.elements()) // only render if there is data
+ gfxset_draw_item(gfx, index, cellbounds.min_x, cellbounds.min_y, set);
+ else // otherwise, fill with transparency
+ m_bitmap.fill(0, cellbounds);
+ }
+ }
+ }
- machine.popmessage(state.tilemap.zoom_frac ? _("Zoom = 1/%1$d") : _("Zoom = %1$d"), state.tilemap.zoom);
+ // reset the texture to force an update
+ m_texture->set_bitmap(m_bitmap, m_bitmap.cliprect(), TEXFORMAT_ARGB32);
+ m_bitmap_dirty = false;
}
+}
- if (machine.ui_input().pressed(IPT_UI_ZOOM_IN) && !at_max_zoom)
- {
- if (state.tilemap.auto_zoom)
- {
- // auto zoom never uses fractional factors
- state.tilemap.zoom = std::min<int>(std::lround(pixelscale) + 1, MAX_ZOOM_LEVEL);
- state.tilemap.zoom_frac = false;
- state.tilemap.auto_zoom = false;
- }
- else if (!state.tilemap.zoom_frac)
- {
- // remaining in integer zoom range
- state.tilemap.zoom++;
- }
- else if (state.tilemap.zoom == 2)
- {
- // entering integer zoom range
- state.tilemap.zoom--;
- state.tilemap.zoom_frac = false;
- }
- else
- {
- // remaining in fractional zoom range
- state.tilemap.zoom--;
- }
- state.bitmap_dirty = true;
+void gfx_viewer::update_tilemap_bitmap(int width, int height)
+{
+ // swap the coordinates back if they were talking about a rotated surface
+ if (m_tilemap.rotate() & ORIENTATION_SWAP_XY)
+ std::swap(width, height);
- machine.popmessage(state.tilemap.zoom_frac ? _("Zoom = 1/%1$d") : _("Zoom = %1$d"), state.tilemap.zoom);
- }
+ // reallocate the bitmap if it is too small
+ resize_bitmap(width, height);
- if (machine.ui_input().pressed(IPT_UI_ZOOM_DEFAULT) && !state.tilemap.auto_zoom)
+ // handle the redraw
+ if (m_bitmap_dirty)
{
- state.tilemap.auto_zoom = true;
+ m_bitmap.fill(0);
+ tilemap_t &tilemap = *m_machine.tilemap().find(m_tilemap.index());
+ screen_device *const first_screen = screen_device_enumerator(m_machine.root_device()).first();
+ if (first_screen)
+ tilemap.draw_debug(*first_screen, m_bitmap, m_tilemap.xoffs(), m_tilemap.yoffs(), m_tilemap.flags());
- machine.popmessage(_("Expand to fit"));
+ // reset the texture to force an update
+ m_texture->set_bitmap(m_bitmap, m_bitmap.cliprect(), TEXFORMAT_RGB32);
+ m_bitmap_dirty = false;
}
+}
- // handle rotation (R)
- if (machine.ui_input().pressed(IPT_UI_ROTATE))
- {
- state.tilemap.rotate = orientation_add(ROT90, state.tilemap.rotate);
- state.bitmap_dirty = true;
- }
- // return to (0,0) (HOME)
- if (machine.ui_input().pressed(IPT_UI_HOME))
- {
- state.tilemap.xoffs = 0;
- state.tilemap.yoffs = 0;
- state.bitmap_dirty = true;
- }
+void gfx_viewer::gfxset_draw_item(gfx_element &gfx, int index, int dstx, int dsty, gfxset::setinfo const &info)
+{
+ int const width = (info.m_rotate & ORIENTATION_SWAP_XY) ? gfx.height() : gfx.width();
+ int const height = (info.m_rotate & ORIENTATION_SWAP_XY) ? gfx.width() : gfx.height();
+ rgb_t const *const palette = info.m_palette->palette()->entry_list_raw() + gfx.colorbase() + info.m_color * gfx.granularity();
+ uint8_t const *const src = gfx.get_data(index);
- // handle flags (category)
- if (machine.ui_input().pressed(IPT_UI_PAGE_UP) && state.tilemap.flags != TILEMAP_DRAW_ALL_CATEGORIES)
+ // loop over rows in the cell
+ for (int y = 0; y < height; y++)
{
- if (state.tilemap.flags > 0)
- {
- state.tilemap.flags--;
- machine.popmessage("Category = %d", state.tilemap.flags);
- }
- else
+ uint32_t *dest = &m_bitmap.pix(dsty + y, dstx);
+
+ // loop over columns in the cell
+ for (int x = 0; x < width; x++)
{
- state.tilemap.flags = TILEMAP_DRAW_ALL_CATEGORIES;
- machine.popmessage("Category All");
+ // compute effective x,y values after rotation
+ int effx = x, effy = y;
+ if (!(info.m_rotate & ORIENTATION_SWAP_XY))
+ {
+ if (info.m_rotate & ORIENTATION_FLIP_X)
+ effx = gfx.width() - 1 - effx;
+ if (info.m_rotate & ORIENTATION_FLIP_Y)
+ effy = gfx.height() - 1 - effy;
+ }
+ else
+ {
+ if (info.m_rotate & ORIENTATION_FLIP_X)
+ effx = gfx.height() - 1 - effx;
+ if (info.m_rotate & ORIENTATION_FLIP_Y)
+ effy = gfx.width() - 1 - effy;
+ std::swap(effx, effy);
+ }
+
+ // get a pointer to the start of this source row
+ uint8_t const *const s = src + (effy * gfx.rowbytes());
+
+ // extract the pixel
+ *dest++ = 0xff000000 | palette[s[effx]];
}
- state.bitmap_dirty = true;
- }
- if (machine.ui_input().pressed(IPT_UI_PAGE_DOWN) && (state.tilemap.flags < TILEMAP_DRAW_CATEGORY_MASK || (state.tilemap.flags == TILEMAP_DRAW_ALL_CATEGORIES)))
- {
- if (state.tilemap.flags == TILEMAP_DRAW_ALL_CATEGORIES)
- state.tilemap.flags = 0;
- else
- state.tilemap.flags++;
- state.bitmap_dirty = true;
- machine.popmessage("Category = %d", state.tilemap.flags);
}
+}
- // handle navigation (up,down,left,right), taking orientation into account
- int step = 8; // this may be applied more than once if multiple directions are pressed
- if (machine.input().code_pressed(KEYCODE_LSHIFT)) step = 1;
- if (machine.input().code_pressed(KEYCODE_LCONTROL)) step = 64;
- if (machine.ui_input().pressed_repeat(IPT_UI_UP, 4))
- {
- if (state.tilemap.rotate & ORIENTATION_SWAP_XY)
- state.tilemap.xoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step;
- else
- state.tilemap.yoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step;
- state.bitmap_dirty = true;
- }
- if (machine.ui_input().pressed_repeat(IPT_UI_DOWN, 4))
- {
- if (state.tilemap.rotate & ORIENTATION_SWAP_XY)
- state.tilemap.xoffs += (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step;
- else
- state.tilemap.yoffs += (state.tilemap.rotate & ORIENTATION_FLIP_Y) ? -step : step;
- state.bitmap_dirty = true;
- }
- if (machine.ui_input().pressed_repeat(IPT_UI_LEFT, 6))
- {
- if (state.tilemap.rotate & ORIENTATION_SWAP_XY)
- state.tilemap.yoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step;
- else
- state.tilemap.xoffs -= (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step;
- state.bitmap_dirty = true;
- }
- if (machine.ui_input().pressed_repeat(IPT_UI_RIGHT, 6))
- {
- if (state.tilemap.rotate & ORIENTATION_SWAP_XY)
- state.tilemap.yoffs += (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step;
- else
- state.tilemap.xoffs += (state.tilemap.rotate & ORIENTATION_FLIP_X) ? -step : step;
- state.bitmap_dirty = true;
- }
+} // anonymous namespace
- // clamp within range
- while (state.tilemap.xoffs < 0)
- state.tilemap.xoffs += mapwidth;
- while (state.tilemap.xoffs >= mapwidth)
- state.tilemap.xoffs -= mapwidth;
- while (state.tilemap.yoffs < 0)
- state.tilemap.yoffs += mapheight;
- while (state.tilemap.yoffs >= mapheight)
- state.tilemap.yoffs -= mapheight;
-}
+/***************************************************************************
+ MAIN ENTRY POINT
+***************************************************************************/
+
//-------------------------------------------------
-// tilemap_update_bitmap - update the bitmap
-// for the tilemap view
+// ui_gfx_ui_handler - primary UI handler
+//
+// NOTE: this must not be called before machine
+// initialization is complete, as some drivers
+// create or modify gfx sets in VIDEO_START
//-------------------------------------------------
-static void tilemap_update_bitmap(running_machine &machine, ui_gfx_state &state, int width, int height)
+uint32_t ui_gfx_ui_handler(render_container &container, mame_ui_manager &mui, bool uistate)
{
- // swap the coordinates back if they were talking about a rotated surface
- if (state.tilemap.rotate & ORIENTATION_SWAP_XY)
- std::swap(width, height);
-
- // realloc the bitmap if it is too small
- if (!state.bitmap.valid() || !state.texture || state.bitmap.width() != width || state.bitmap.height() != height)
- {
- // free the old stuff
- machine.render().texture_free(state.texture);
- state.bitmap.reset();
-
- // allocate new stuff
- state.bitmap.allocate(width, height);
- state.texture = machine.render().texture_alloc();
- state.texture->set_bitmap(state.bitmap, state.bitmap.cliprect(), TEXFORMAT_RGB32);
-
- // force a redraw
- state.bitmap_dirty = true;
- }
-
- // handle the redraw
- if (state.bitmap_dirty)
- {
- state.bitmap.fill(0);
- tilemap_t *tilemap = machine.tilemap().find(state.tilemap.which);
- screen_device *first_screen = screen_device_enumerator(machine.root_device()).first();
- if (first_screen)
- {
- tilemap->draw_debug(*first_screen, state.bitmap, state.tilemap.xoffs, state.tilemap.yoffs, state.tilemap.flags);
- }
-
- // reset the texture to force an update
- state.texture->set_bitmap(state.bitmap, state.bitmap.cliprect(), TEXFORMAT_RGB32);
- state.bitmap_dirty = false;
- }
+ return mui.get_session_data<gfx_viewer, gfx_viewer>(mui.machine()).handle(mui, container, uistate);
}
diff --git a/src/frontend/mame/ui/viewgfx.h b/src/frontend/mame/ui/viewgfx.h
index 765384b4bea..cb368efc91c 100644
--- a/src/frontend/mame/ui/viewgfx.h
+++ b/src/frontend/mame/ui/viewgfx.h
@@ -20,12 +20,6 @@
FUNCTION PROTOTYPES
***************************************************************************/
-// initialization
-void ui_gfx_init(running_machine &machine);
-
-// returns 'true' if the internal graphics viewer has relevance
-bool ui_gfx_is_relevant(running_machine &machine);
-
// master handler
uint32_t ui_gfx_ui_handler(render_container &container, mame_ui_manager &mui, bool uistate);