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
item_append(_("Return to Previous Menu"), "", 0, nullptr);
}
}

View File

@ -9,6 +9,13 @@
*********************************************************************/
#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 "osdnet.h"
#include "mameopts.h"
@ -18,14 +25,14 @@
#include "romload.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 <iterator>
namespace ui {
@ -271,93 +278,99 @@ void menu_bookkeeping::populate(float &customtop, float &custombottom)
void menu_crosshair::handle()
{
/* process the menu */
const event *menu_event = process(PROCESS_LR_REPEAT);
// process the menu
event const *const menu_event(process(PROCESS_LR_REPEAT));
/* handle events */
if (menu_event != nullptr && menu_event->itemref != nullptr)
// handle events
if (menu_event && menu_event->itemref)
{
crosshair_item_data *data = (crosshair_item_data *)menu_event->itemref;
bool changed = false;
//int set_def = false;
int newval = data->cur;
/* retreive the user settings */
render_crosshair &crosshair = machine().crosshair().get_crosshair(data->player);
crosshair_item_data &data(*reinterpret_cast<crosshair_item_data *>(menu_event->itemref));
bool changed(false);
int newval(data.cur);
switch (menu_event->iptkey)
{
/* if selected, reset to default value */
case IPT_UI_SELECT:
newval = data->defvalue;
//set_def = true;
break;
// if selected, reset to default value
case IPT_UI_SELECT:
newval = data.defvalue;
break;
/* left decrements */
case IPT_UI_LEFT:
newval -= machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
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;
// right increments
case IPT_UI_RIGHT:
newval += machine().input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
break;
}
/* clamp to range */
if (newval < data->min)
newval = data->min;
if (newval > data->max)
newval = data->max;
// clamp to range
if (newval < data.min)
newval = data.min;
if (newval > data.max)
newval = data.max;
/* if things changed, update */
if (newval != data->cur)
// if things changed, update
if (newval != data.cur)
{
switch (data->type)
switch (data.type)
{
/* visibility state */
case CROSSHAIR_ITEM_VIS:
crosshair.set_mode(newval);
// set visibility as specified by mode - auto mode starts with visibility off
crosshair.set_visible(newval == CROSSHAIR_VISIBILITY_ON);
changed = true;
break;
// visibility state
case CROSSHAIR_ITEM_VIS:
data.crosshair->set_mode(newval);
// set visibility as specified by mode - auto mode starts with visibility off
data.crosshair->set_visible(newval == CROSSHAIR_VISIBILITY_ON);
changed = true;
break;
/* auto time */
case CROSSHAIR_ITEM_AUTO_TIME:
machine().crosshair().set_auto_time(newval);
changed = true;
break;
// auto time
case CROSSHAIR_ITEM_AUTO_TIME:
machine().crosshair().set_auto_time(newval);
changed = true;
break;
}
}
/* crosshair graphic name */
if (data->type == CROSSHAIR_ITEM_PIC)
// crosshair graphic name
if (data.type == CROSSHAIR_ITEM_PIC)
{
switch (menu_event->iptkey)
{
case IPT_UI_SELECT:
crosshair.set_default_bitmap();
changed = true;
break;
case IPT_UI_SELECT:
{
std::vector<std::string> sel;
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:
crosshair.set_bitmap_name(data->last_name);
changed = true;
break;
case IPT_UI_LEFT:
data.crosshair->set_bitmap_name(data.last_name.c_str());
changed = true;
break;
case IPT_UI_RIGHT:
crosshair.set_bitmap_name(data->next_name);
changed = true;
break;
case IPT_UI_RIGHT:
data.crosshair->set_bitmap_name(data.next_name.c_str());
changed = true;
break;
}
}
if (changed)
{
/* rebuild the menu */
reset(reset_options::REMEMBER_POSITION);
}
reset(reset_options::REMEMBER_REF); // rebuild the menu
}
}
@ -373,142 +386,165 @@ menu_crosshair::menu_crosshair(mame_ui_manager &mui, render_container &container
void menu_crosshair::populate(float &customtop, float &custombottom)
{
crosshair_item_data *data;
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++)
if (m_data.empty())
{
/* get the user settings */
render_crosshair &crosshair = machine().crosshair().get_crosshair(player);
/* add menu items for usable crosshairs */
if (crosshair.is_used())
// loop over player and add the manual items
for (int player = 0; player < MAX_PLAYERS; player++)
{
/* Make sure to keep these matched to the CROSSHAIR_VISIBILITY_xxx types */
static const char *const vis_text[] = { "Off", "On", "Auto" };
// get the user settings
render_crosshair &crosshair(machine().crosshair().get_crosshair(player));
/* track if we need the auto time menu */
if (crosshair.mode() == CROSSHAIR_VISIBILITY_AUTO) use_auto = true;
/* 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)
// add menu items for usable crosshairs
if (crosshair.is_used())
{
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*/
if ((length > 4) && (length <= CROSSHAIR_PIC_NAME_LENGTH + 4) && core_filename_ends_with(dir->name, ".png"))
// CROSSHAIR_ITEM_PIC - allocate a data item and fill it
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 */
length -= 4;
// if we are using the default, then we just need to find the first in the list
if (found || using_default)
{
/* get the next name */
strncpy(data->next_name, dir->name, length);
data->next_name[length] = 0;
// get the next name
data.next_name = *it;
finished = true;
}
else if (!strncmp(dir->name, crosshair.bitmap_name(), length))
else if (data.crosshair->bitmap_name() == *it)
{
/* we found the current name */
/* so loop once more to find the next name */
// we found the current name so loop once more to find the next name
found = true;
}
else
/* remember last name */
/* we will do it here in case files get added to the directory */
{
strncpy(data->last_name, dir->name, length);
data->last_name[length] = 0;
// remember last name - we will do it here in case files get added to the directory
++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 */
sprintf(temp_text, "P%d Crosshair", player + 1);
item_append(temp_text, using_default ? "DEFAULT" : crosshair.bitmap_name(), flags, data);
// if name not found then next item is DEFAULT
if (!found && !using_default)
{
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()

View File

@ -13,12 +13,15 @@
#pragma once
#include "ui/menu.h"
#include "crsshair.h"
#include "emuopts.h"
#include <utility>
#include <vector>
namespace ui {
class menu_keyboard_mode : public menu
@ -69,23 +72,24 @@ private:
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 */
struct crosshair_item_data
{
uint8_t type;
uint8_t player;
uint8_t min, max;
uint8_t cur;
uint8_t defvalue;
char last_name[CROSSHAIR_PIC_NAME_LENGTH + 1];
char next_name[CROSSHAIR_PIC_NAME_LENGTH + 1];
render_crosshair *crosshair = nullptr;
uint8_t type = 0U;
uint8_t player = 0U;
uint8_t min = 0U, max = 0U;
uint32_t cur = 0U;
uint8_t defvalue = 0U;
std::string last_name;
std::string next_name;
};
virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override;
std::vector<crosshair_item_data> m_data;
std::vector<std::string> m_pics;
};
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)
{
m_entries.clear();
if (m_other_opt)
{
software_part_menu_entry *entry1 = (software_part_menu_entry *) m_pool_alloc(sizeof(*entry1));
entry1->type = result::EMPTY;
entry1->part = nullptr;
item_append(_("[empty slot]"), "", 0, entry1);
software_part_menu_entry &entry1(*m_entries.emplace(m_entries.end()));
entry1.type = result::EMPTY;
entry1.part = nullptr;
item_append(_("[empty slot]"), "", 0, &entry1);
software_part_menu_entry *entry2 = (software_part_menu_entry *) m_pool_alloc(sizeof(*entry2));
entry2->type = result::FMGR;
entry2->part = nullptr;
item_append(_("[file manager]"), "", 0, entry2);
software_part_menu_entry &entry2(*m_entries.emplace(m_entries.end()));
entry2.type = result::FMGR;
entry2.part = nullptr;
item_append(_("[file manager]"), "", 0, &entry2);
software_part_menu_entry *entry3 = (software_part_menu_entry *) m_pool_alloc(sizeof(*entry3));
entry3->type = result::SWLIST;
entry3->part = nullptr;
item_append(_("[software list]"), "", 0, entry3);
software_part_menu_entry &entry3(*m_entries.emplace(m_entries.end()));
entry3.type = result::SWLIST;
entry3.part = nullptr;
item_append(_("[software list]"), "", 0, &entry3);
}
for (const software_part &swpart : m_info->parts())
{
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.)
// if not, we simply display "part_name"; if yes we display "part_name (part_id)"
std::string menu_part_name(swpart.name());
if (swpart.feature("part_id") != nullptr)
menu_part_name.append(" (").append(swpart.feature("part_id")).append(")");
entry->type = result::ENTRY;
entry->part = &swpart;
item_append(m_info->shortname(), menu_part_name, 0, entry);
entry.type = result::ENTRY;
entry.part = &swpart;
item_append(m_info->shortname(), menu_part_name, 0, &entry);
}
}
}

View File

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