UI updates:

* Cleaned up memory management in some more menus
* Don't walk the directory every time the crosshair menu is refreshed
* Sort crosshair pictures
* Show a selector menu when crosshair picture item is selected
This commit is contained in:
Vas Crabb 2019-11-17 02:01:39 +11:00
parent 173bcb30b6
commit bb68893deb
5 changed files with 259 additions and 212 deletions

View File

@ -317,7 +317,6 @@ void menu::reset(reset_options options)
else else
item_append(_("Return to Previous Menu"), "", 0, nullptr); item_append(_("Return to Previous Menu"), "", 0, nullptr);
} }
} }

View File

@ -9,6 +9,13 @@
*********************************************************************/ *********************************************************************/
#include "emu.h" #include "emu.h"
#include "ui/miscmenu.h"
#include "ui/inifile.h"
#include "ui/selector.h"
#include "ui/submenu.h"
#include "ui/ui.h"
#include "mame.h" #include "mame.h"
#include "osdnet.h" #include "osdnet.h"
#include "mameopts.h" #include "mameopts.h"
@ -18,14 +25,14 @@
#include "romload.h" #include "romload.h"
#include "uiinput.h" #include "uiinput.h"
#include "ui/ui.h"
#include "ui/menu.h"
#include "ui/miscmenu.h"
#include "../info.h"
#include "ui/inifile.h"
#include "ui/submenu.h"
#include "../info.h"
#include <algorithm>
#include <cstring>
#include <fstream> #include <fstream>
#include <iterator>
namespace ui { namespace ui {
@ -271,93 +278,99 @@ void menu_bookkeeping::populate(float &customtop, float &custombottom)
void menu_crosshair::handle() void menu_crosshair::handle()
{ {
/* process the menu */ // process the menu
const event *menu_event = process(PROCESS_LR_REPEAT); event const *const menu_event(process(PROCESS_LR_REPEAT));
/* handle events */ // handle events
if (menu_event != nullptr && menu_event->itemref != nullptr) if (menu_event && menu_event->itemref)
{ {
crosshair_item_data *data = (crosshair_item_data *)menu_event->itemref; crosshair_item_data &data(*reinterpret_cast<crosshair_item_data *>(menu_event->itemref));
bool changed = false; bool changed(false);
//int set_def = false; int newval(data.cur);
int newval = data->cur;
/* retreive the user settings */
render_crosshair &crosshair = machine().crosshair().get_crosshair(data->player);
switch (menu_event->iptkey) switch (menu_event->iptkey)
{ {
/* if selected, reset to default value */ // if selected, reset to default value
case IPT_UI_SELECT: case IPT_UI_SELECT:
newval = data->defvalue; newval = data.defvalue;
//set_def = true; break;
break;
/* left decrements */ // left decrements
case IPT_UI_LEFT: case IPT_UI_LEFT:
newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
break; break;
/* right increments */ // right increments
case IPT_UI_RIGHT: case IPT_UI_RIGHT:
newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1; newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
break; break;
} }
/* clamp to range */ // clamp to range
if (newval < data->min) if (newval < data.min)
newval = data->min; newval = data.min;
if (newval > data->max) if (newval > data.max)
newval = data->max; newval = data.max;
/* if things changed, update */ // if things changed, update
if (newval != data->cur) if (newval != data.cur)
{ {
switch (data->type) switch (data.type)
{ {
/* visibility state */ // visibility state
case CROSSHAIR_ITEM_VIS: case CROSSHAIR_ITEM_VIS:
crosshair.set_mode(newval); data.crosshair->set_mode(newval);
// set visibility as specified by mode - auto mode starts with visibility off // set visibility as specified by mode - auto mode starts with visibility off
crosshair.set_visible(newval == CROSSHAIR_VISIBILITY_ON); data.crosshair->set_visible(newval == CROSSHAIR_VISIBILITY_ON);
changed = true; changed = true;
break; break;
/* auto time */ // auto time
case CROSSHAIR_ITEM_AUTO_TIME: case CROSSHAIR_ITEM_AUTO_TIME:
machine().crosshair().set_auto_time(newval); machine().crosshair().set_auto_time(newval);
changed = true; changed = true;
break; break;
} }
} }
/* crosshair graphic name */ // crosshair graphic name
if (data->type == CROSSHAIR_ITEM_PIC) if (data.type == CROSSHAIR_ITEM_PIC)
{ {
switch (menu_event->iptkey) switch (menu_event->iptkey)
{ {
case IPT_UI_SELECT: case IPT_UI_SELECT:
crosshair.set_default_bitmap(); {
changed = true; std::vector<std::string> sel;
break; sel.reserve(m_pics.size() + 1);
sel.push_back("DEFAULT");
std::copy(m_pics.begin(), m_pics.end(), std::back_inserter(sel));
menu::stack_push<menu_selector>(
ui(), container(), std::move(sel), data.cur,
[this, &data] (int selection)
{
if (!selection)
data.crosshair->set_default_bitmap();
else
data.crosshair->set_bitmap_name(m_pics[selection - 1].c_str());
reset(reset_options::REMEMBER_REF);
});
}
break;
case IPT_UI_LEFT: case IPT_UI_LEFT:
crosshair.set_bitmap_name(data->last_name); data.crosshair->set_bitmap_name(data.last_name.c_str());
changed = true; changed = true;
break; break;
case IPT_UI_RIGHT: case IPT_UI_RIGHT:
crosshair.set_bitmap_name(data->next_name); data.crosshair->set_bitmap_name(data.next_name.c_str());
changed = true; changed = true;
break; break;
} }
} }
if (changed) if (changed)
{ reset(reset_options::REMEMBER_REF); // rebuild the menu
/* rebuild the menu */
reset(reset_options::REMEMBER_POSITION);
}
} }
} }
@ -373,142 +386,165 @@ menu_crosshair::menu_crosshair(mame_ui_manager &mui, render_container &container
void menu_crosshair::populate(float &customtop, float &custombottom) void menu_crosshair::populate(float &customtop, float &custombottom)
{ {
crosshair_item_data *data; if (m_data.empty())
char temp_text[16];
int player;
uint8_t use_auto = false;
uint32_t flags = 0;
/* loop over player and add the manual items */
for (player = 0; player < MAX_PLAYERS; player++)
{ {
/* get the user settings */ // loop over player and add the manual items
render_crosshair &crosshair = machine().crosshair().get_crosshair(player); for (int player = 0; player < MAX_PLAYERS; player++)
/* add menu items for usable crosshairs */
if (crosshair.is_used())
{ {
/* Make sure to keep these matched to the CROSSHAIR_VISIBILITY_xxx types */ // get the user settings
static const char *const vis_text[] = { "Off", "On", "Auto" }; render_crosshair &crosshair(machine().crosshair().get_crosshair(player));
/* track if we need the auto time menu */ // add menu items for usable crosshairs
if (crosshair.mode() == CROSSHAIR_VISIBILITY_AUTO) use_auto = true; if (crosshair.is_used())
/* CROSSHAIR_ITEM_VIS - allocate a data item and fill it */
data = (crosshair_item_data *)m_pool_alloc(sizeof(*data));
data->type = CROSSHAIR_ITEM_VIS;
data->player = player;
data->min = CROSSHAIR_VISIBILITY_OFF;
data->max = CROSSHAIR_VISIBILITY_AUTO;
data->defvalue = CROSSHAIR_VISIBILITY_DEFAULT;
data->cur = crosshair.mode();
/* put on arrows */
if (data->cur > data->min)
flags |= FLAG_LEFT_ARROW;
if (data->cur < data->max)
flags |= FLAG_RIGHT_ARROW;
/* add CROSSHAIR_ITEM_VIS menu */
sprintf(temp_text, "P%d Visibility", player + 1);
item_append(temp_text, vis_text[crosshair.mode()], flags, data);
/* CROSSHAIR_ITEM_PIC - allocate a data item and fill it */
data = (crosshair_item_data *)m_pool_alloc(sizeof(*data));
data->type = CROSSHAIR_ITEM_PIC;
data->player = player;
data->last_name[0] = 0;
/* other data item not used by this menu */
/* search for crosshair graphics */
/* open a path to the crosshairs */
file_enumerator path(machine().options().crosshair_path());
const osd::directory::entry *dir;
/* reset search flags */
bool using_default = false;
bool finished = false;
bool found = false;
/* if we are using the default, then we just need to find the first in the list */
if (*crosshair.bitmap_name() == '\0')
using_default = true;
/* look for the current name, then remember the name before */
/* and find the next name */
while (((dir = path.next()) != nullptr) && !finished)
{ {
int length = strlen(dir->name); // CROSSHAIR_ITEM_VIS - allocate a data item and fill it
crosshair_item_data &visdata(*m_data.emplace(m_data.end()));
visdata.crosshair = &crosshair;
visdata.type = CROSSHAIR_ITEM_VIS;
visdata.player = player;
visdata.min = CROSSHAIR_VISIBILITY_OFF;
visdata.max = CROSSHAIR_VISIBILITY_AUTO;
visdata.defvalue = CROSSHAIR_VISIBILITY_DEFAULT;
/* look for files ending in .png with a name not larger then 9 chars*/ // CROSSHAIR_ITEM_PIC - allocate a data item and fill it
if ((length > 4) && (length <= CROSSHAIR_PIC_NAME_LENGTH + 4) && core_filename_ends_with(dir->name, ".png")) crosshair_item_data &picdata(*m_data.emplace(m_data.end()));
picdata.crosshair = &crosshair;
picdata.type = CROSSHAIR_ITEM_PIC;
picdata.player = player;
// other data item not used by this menu
}
}
// CROSSHAIR_ITEM_AUTO_TIME - allocate a data item and fill it
crosshair_item_data &timedata(*m_data.emplace(m_data.end()));
timedata.type = CROSSHAIR_ITEM_AUTO_TIME;
timedata.min = CROSSHAIR_VISIBILITY_AUTOTIME_MIN;
timedata.max = CROSSHAIR_VISIBILITY_AUTOTIME_MAX;
timedata.defvalue = CROSSHAIR_VISIBILITY_AUTOTIME_DEFAULT;
}
if (m_pics.empty())
{
// open a path to the crosshairs
file_enumerator path(machine().options().crosshair_path());
for (osd::directory::entry const *dir = path.next(); dir; dir = path.next())
{
// look for files ending in .png
size_t const length(std::strlen(dir->name));
if ((length > 4) && core_filename_ends_with(dir->name, ".png"))
m_pics.emplace_back(dir->name, length - 4);
}
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()); });
}
// Make sure to keep these matched to the CROSSHAIR_VISIBILITY_xxx types
static char const *const vis_text[] = { "Off", "On", "Auto" };
bool use_auto = false;
for (crosshair_item_data &data : m_data)
{
switch (data.type)
{
case CROSSHAIR_ITEM_VIS:
{
// track if we need the auto time menu
if (data.crosshair->mode() == CROSSHAIR_VISIBILITY_AUTO)
use_auto = true;
data.cur = data.crosshair->mode();
// put on arrows
uint32_t flags(0U);
if (data.cur > data.min)
flags |= FLAG_LEFT_ARROW;
if (data.cur < data.max)
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);
}
break;
case CROSSHAIR_ITEM_PIC:
// search for crosshair graphics
{
// reset search flags
bool const using_default(*data.crosshair->bitmap_name() == '\0');
bool finished(false);
bool found(false);
data.cur = using_default ? 0U : 1U;
data.last_name.clear();
data.next_name.clear();
// look for the current name, then remember the name before and find the next name
for (auto it = m_pics.begin(); it != m_pics.end() && !finished; ++it)
{ {
/* remove .png from length */ // if we are using the default, then we just need to find the first in the list
length -= 4;
if (found || using_default) if (found || using_default)
{ {
/* get the next name */ // get the next name
strncpy(data->next_name, dir->name, length); data.next_name = *it;
data->next_name[length] = 0;
finished = true; finished = true;
} }
else if (!strncmp(dir->name, crosshair.bitmap_name(), length)) else if (data.crosshair->bitmap_name() == *it)
{ {
/* we found the current name */ // we found the current name so loop once more to find the next name
/* so loop once more to find the next name */
found = true; found = true;
} }
else else
/* remember last name */
/* we will do it here in case files get added to the directory */
{ {
strncpy(data->last_name, dir->name, length); // remember last name - we will do it here in case files get added to the directory
data->last_name[length] = 0; ++data.cur;
data.last_name = *it;
} }
} }
}
/* if name not found then next item is DEFAULT */
if (!found && !using_default)
{
data->next_name[0] = 0;
finished = true;
}
/* setup the selection flags */
flags = 0;
if (finished)
flags |= FLAG_RIGHT_ARROW;
if (found)
flags |= FLAG_LEFT_ARROW;
/* add CROSSHAIR_ITEM_PIC menu */ // if name not found then next item is DEFAULT
sprintf(temp_text, "P%d Crosshair", player + 1); if (!found && !using_default)
item_append(temp_text, using_default ? "DEFAULT" : crosshair.bitmap_name(), flags, data); {
data.cur = 0U;
data.next_name.clear();
finished = true;
}
// set up the selection flags
uint32_t flags(0U);
if (finished)
flags |= FLAG_RIGHT_ARROW;
if (found)
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);
}
break;
case CROSSHAIR_ITEM_AUTO_TIME:
if (use_auto)
{
data.cur = machine().crosshair().auto_time();
// put on arrows in visible menu
uint32_t flags(0U);
if (data.cur > data.min)
flags |= FLAG_LEFT_ARROW;
if (data.cur < data.max)
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);
}
break;
} }
} }
if (use_auto)
{
/* CROSSHAIR_ITEM_AUTO_TIME - allocate a data item and fill it */
data = (crosshair_item_data *)m_pool_alloc(sizeof(*data));
data->type = CROSSHAIR_ITEM_AUTO_TIME;
data->min = CROSSHAIR_VISIBILITY_AUTOTIME_MIN;
data->max = CROSSHAIR_VISIBILITY_AUTOTIME_MAX;
data->defvalue = CROSSHAIR_VISIBILITY_AUTOTIME_DEFAULT;
data->cur = machine().crosshair().auto_time();
/* put on arrows in visible menu */
if (data->cur > data->min)
flags |= FLAG_LEFT_ARROW;
if (data->cur < data->max)
flags |= FLAG_RIGHT_ARROW;
/* add CROSSHAIR_ITEM_AUTO_TIME menu */
sprintf(temp_text, "%d", machine().crosshair().auto_time());
item_append(_("Visible Delay"), temp_text, flags, data);
}
// else
// /* leave a blank filler line when not in auto time so size does not rescale */
// item_append("", "", nullptr, nullptr);
} }
menu_crosshair::~menu_crosshair() menu_crosshair::~menu_crosshair()

View File

@ -13,12 +13,15 @@
#pragma once #pragma once
#include "ui/menu.h"
#include "crsshair.h" #include "crsshair.h"
#include "emuopts.h" #include "emuopts.h"
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace ui { namespace ui {
class menu_keyboard_mode : public menu class menu_keyboard_mode : public menu
@ -69,23 +72,24 @@ private:
CROSSHAIR_ITEM_AUTO_TIME CROSSHAIR_ITEM_AUTO_TIME
}; };
// FIXME: use std::string instead of fixed-length arrays
constexpr static int CROSSHAIR_PIC_NAME_LENGTH = 12;
/* internal crosshair menu item data */ /* internal crosshair menu item data */
struct crosshair_item_data struct crosshair_item_data
{ {
uint8_t type; render_crosshair *crosshair = nullptr;
uint8_t player; uint8_t type = 0U;
uint8_t min, max; uint8_t player = 0U;
uint8_t cur; uint8_t min = 0U, max = 0U;
uint8_t defvalue; uint32_t cur = 0U;
char last_name[CROSSHAIR_PIC_NAME_LENGTH + 1]; uint8_t defvalue = 0U;
char next_name[CROSSHAIR_PIC_NAME_LENGTH + 1]; std::string last_name;
std::string next_name;
}; };
virtual void populate(float &customtop, float &custombottom) override; virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override; virtual void handle() override;
std::vector<crosshair_item_data> m_data;
std::vector<std::string> m_pics;
}; };
class menu_quit_game : public menu class menu_quit_game : public menu

View File

@ -75,38 +75,40 @@ menu_software_parts::~menu_software_parts()
void menu_software_parts::populate(float &customtop, float &custombottom) void menu_software_parts::populate(float &customtop, float &custombottom)
{ {
m_entries.clear();
if (m_other_opt) if (m_other_opt)
{ {
software_part_menu_entry *entry1 = (software_part_menu_entry *) m_pool_alloc(sizeof(*entry1)); software_part_menu_entry &entry1(*m_entries.emplace(m_entries.end()));
entry1->type = result::EMPTY; entry1.type = result::EMPTY;
entry1->part = nullptr; entry1.part = nullptr;
item_append(_("[empty slot]"), "", 0, entry1); item_append(_("[empty slot]"), "", 0, &entry1);
software_part_menu_entry *entry2 = (software_part_menu_entry *) m_pool_alloc(sizeof(*entry2)); software_part_menu_entry &entry2(*m_entries.emplace(m_entries.end()));
entry2->type = result::FMGR; entry2.type = result::FMGR;
entry2->part = nullptr; entry2.part = nullptr;
item_append(_("[file manager]"), "", 0, entry2); item_append(_("[file manager]"), "", 0, &entry2);
software_part_menu_entry *entry3 = (software_part_menu_entry *) m_pool_alloc(sizeof(*entry3)); software_part_menu_entry &entry3(*m_entries.emplace(m_entries.end()));
entry3->type = result::SWLIST; entry3.type = result::SWLIST;
entry3->part = nullptr; entry3.part = nullptr;
item_append(_("[software list]"), "", 0, entry3); item_append(_("[software list]"), "", 0, &entry3);
} }
for (const software_part &swpart : m_info->parts()) for (const software_part &swpart : m_info->parts())
{ {
if (swpart.matches_interface(m_interface)) if (swpart.matches_interface(m_interface))
{ {
software_part_menu_entry *entry = (software_part_menu_entry *) m_pool_alloc(sizeof(*entry)); software_part_menu_entry &entry(*m_entries.emplace(m_entries.end()));
// check if the available parts have specific part_id to be displayed (e.g. "Map Disc", "Bonus Disc", etc.) // check if the available parts have specific part_id to be displayed (e.g. "Map Disc", "Bonus Disc", etc.)
// if not, we simply display "part_name"; if yes we display "part_name (part_id)" // if not, we simply display "part_name"; if yes we display "part_name (part_id)"
std::string menu_part_name(swpart.name()); std::string menu_part_name(swpart.name());
if (swpart.feature("part_id") != nullptr) if (swpart.feature("part_id") != nullptr)
menu_part_name.append(" (").append(swpart.feature("part_id")).append(")"); menu_part_name.append(" (").append(swpart.feature("part_id")).append(")");
entry->type = result::ENTRY; entry.type = result::ENTRY;
entry->part = &swpart; entry.part = &swpart;
item_append(m_info->shortname(), menu_part_name, 0, entry); item_append(m_info->shortname(), menu_part_name, 0, &entry);
} }
} }
} }

View File

@ -13,8 +13,11 @@
#include "ui/menu.h" #include "ui/menu.h"
#include <list>
namespace ui { namespace ui {
// ======================> menu_software_parts // ======================> menu_software_parts
class menu_software_parts : public menu class menu_software_parts : public menu
@ -33,15 +36,18 @@ public:
virtual ~menu_software_parts() override; virtual ~menu_software_parts() override;
private: private:
struct software_part_menu_entry { struct software_part_menu_entry
{
result type; result type;
const software_part *part; const software_part *part;
}; };
using entry_list = std::list<software_part_menu_entry>;
virtual void populate(float &customtop, float &custombottom) override; virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override; virtual void handle() override;
// variables // variables
entry_list m_entries;
const software_info * m_info; const software_info * m_info;
const char * m_interface; const char * m_interface;
const software_part ** m_selected_part; const software_part ** m_selected_part;