mirror of
https://github.com/holub/mame
synced 2025-04-24 09:20:02 +03:00
UI cleanup:
* Make input mapping menus more efficient - most of the properties of a field won't change * Get rid of the pool allocator in base menu class - it was encouraging bad design
This commit is contained in:
parent
0dc8480a28
commit
7880de4d32
@ -66,63 +66,57 @@ void menu_input_groups::handle()
|
||||
input menu
|
||||
-------------------------------------------------*/
|
||||
|
||||
menu_input_general::menu_input_general(mame_ui_manager &mui, render_container &container, int _group) : menu_input(mui, container)
|
||||
menu_input_general::menu_input_general(mame_ui_manager &mui, render_container &container, int _group)
|
||||
: menu_input(mui, container)
|
||||
, group(_group)
|
||||
{
|
||||
group = _group;
|
||||
}
|
||||
|
||||
void menu_input_general::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
input_item_data *itemlist = nullptr;
|
||||
if (data.empty())
|
||||
{
|
||||
assert(!pollingitem);
|
||||
|
||||
/* iterate over the input ports and add menu items */
|
||||
for (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() != nullptr && entry.name()[0] != 0)
|
||||
// iterate over the input ports and add menu items
|
||||
for (input_type_entry &entry : machine().ioport().types())
|
||||
{
|
||||
input_seq_type seqtype;
|
||||
|
||||
/* loop over all sequence types */
|
||||
for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
// add if we match the group and we have a valid name
|
||||
if ((entry.group() == group) && entry.name() && entry.name()[0])
|
||||
{
|
||||
/* build an entry for the standard sequence */
|
||||
input_item_data *item = (input_item_data *)m_pool_alloc(sizeof(*item));
|
||||
memset(item, 0, sizeof(*item));
|
||||
item->ref = &entry;
|
||||
if(pollingitem && pollingref == &entry && pollingseq == seqtype)
|
||||
pollingitem = item;
|
||||
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 = entry.name();
|
||||
item->owner_name = nullptr;
|
||||
item->next = itemlist;
|
||||
itemlist = item;
|
||||
// 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(data.end()));
|
||||
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 = entry.name();
|
||||
item.owner_name = nullptr;
|
||||
|
||||
/* stop after one, unless we're analog */
|
||||
if (item->type == INPUT_TYPE_DIGITAL)
|
||||
break;
|
||||
// stop after one, unless we're analog
|
||||
if (item.type == INPUT_TYPE_DIGITAL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// first count the number of items
|
||||
int numitems = 0;
|
||||
for (input_item_data const *item = itemlist; item != nullptr; item = item->next)
|
||||
numitems++;
|
||||
|
||||
// now allocate an array of items and fill it up
|
||||
std::vector<input_item_data *> itemarray(numitems);
|
||||
int curitem = numitems;
|
||||
for (input_item_data *item = itemlist; item != nullptr; item = item->next)
|
||||
itemarray[--curitem] = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (input_item_data &item : data)
|
||||
{
|
||||
const input_type_entry &entry(*reinterpret_cast<const input_type_entry *>(item.ref));
|
||||
item.seq = machine().ioport().type_seq(entry.type(), entry.player(), item.seqtype);
|
||||
}
|
||||
}
|
||||
|
||||
// populate the menu in a standard fashion
|
||||
populate_sorted(std::move(itemarray));
|
||||
populate_sorted();
|
||||
}
|
||||
|
||||
menu_input_general::~menu_input_general()
|
||||
@ -140,90 +134,89 @@ menu_input_specific::menu_input_specific(mame_ui_manager &mui, render_container
|
||||
|
||||
void menu_input_specific::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
input_item_data *itemlist = nullptr;
|
||||
|
||||
/* iterate over the input ports and add menu items */
|
||||
for (auto &port : machine().ioport().ports())
|
||||
if (data.empty())
|
||||
{
|
||||
for (ioport_field &field : port.second->fields())
|
||||
assert(!pollingitem);
|
||||
|
||||
// iterate over the input ports and add menu items
|
||||
for (auto &port : machine().ioport().ports())
|
||||
{
|
||||
ioport_type_class type_class = field.type_class();
|
||||
|
||||
/* add if we match the group and we have a valid name */
|
||||
if (field.enabled() && (type_class == INPUT_CLASS_CONTROLLER || type_class == INPUT_CLASS_MISC || type_class == INPUT_CLASS_KEYBOARD))
|
||||
for (ioport_field &field : port.second->fields())
|
||||
{
|
||||
/* 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 = (input_item_data *)m_pool_alloc(sizeof(*item));
|
||||
memset(item, 0, sizeof(*item));
|
||||
item->ref = &field;
|
||||
item->seqtype = seqtype;
|
||||
if(pollingitem && pollingref == item->ref && pollingseq == seqtype)
|
||||
pollingitem = item;
|
||||
item->seq = field.seq(seqtype);
|
||||
item->defseq = &field.defseq(seqtype);
|
||||
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 = field.name();
|
||||
item->owner_name = field.device().tag();
|
||||
item->next = itemlist;
|
||||
itemlist = item;
|
||||
const ioport_type_class type_class = field.type_class();
|
||||
|
||||
/* stop after one, unless we're analog */
|
||||
if (item->type == INPUT_TYPE_DIGITAL)
|
||||
break;
|
||||
// add if it's enabled and it's a system-specific class
|
||||
if (field.enabled() && (type_class == INPUT_CLASS_CONTROLLER || type_class == INPUT_CLASS_MISC || type_class == INPUT_CLASS_KEYBOARD))
|
||||
{
|
||||
// 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(data.end()));
|
||||
item.ref = &field;
|
||||
item.seqtype = seqtype;
|
||||
item.seq = field.seq(seqtype);
|
||||
item.defseq = &field.defseq(seqtype);
|
||||
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 = field.name();
|
||||
item.owner_name = field.device().tag();
|
||||
|
||||
// stop after one, unless we're analog
|
||||
if (item.type == INPUT_TYPE_DIGITAL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort it
|
||||
std::sort(
|
||||
data.begin(),
|
||||
data.end(),
|
||||
[] (const input_item_data &i1, const input_item_data &i2)
|
||||
{
|
||||
int cmp = strcmp(i1.owner_name, i2.owner_name);
|
||||
if (cmp < 0)
|
||||
return true;
|
||||
if (cmp > 0)
|
||||
return false;
|
||||
if (i1.group < i2.group)
|
||||
return true;
|
||||
if (i1.group > i2.group)
|
||||
return false;
|
||||
const ioport_field &field1 = *reinterpret_cast<const ioport_field *>(i1.ref);
|
||||
const ioport_field &field2 = *reinterpret_cast<const ioport_field *>(i2.ref);
|
||||
if (field1.type() < field2.type())
|
||||
return true;
|
||||
if (field1.type() > field2.type())
|
||||
return false;
|
||||
std::vector<char32_t> codes1 = field1.keyboard_codes(0);
|
||||
std::vector<char32_t> codes2 = field2.keyboard_codes(0);
|
||||
if (!codes1.empty() && (codes2.empty() || codes1[0] < codes2[0]))
|
||||
return true;
|
||||
if (!codes2.empty() && (codes1.empty() || codes1[0] > codes2[0]))
|
||||
return false;
|
||||
cmp = strcmp(i1.name, i2.name);
|
||||
if (cmp < 0)
|
||||
return true;
|
||||
if (cmp > 0)
|
||||
return false;
|
||||
return i1.type < i2.type;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
for (input_item_data &item : data)
|
||||
{
|
||||
const ioport_field &field(*reinterpret_cast<const ioport_field *>(item.ref));
|
||||
item.seq = field.seq(item.seqtype);
|
||||
}
|
||||
}
|
||||
|
||||
// first count the number of items
|
||||
int numitems = 0;
|
||||
for (input_item_data const *item = itemlist; item != nullptr; item = item->next)
|
||||
numitems++;
|
||||
|
||||
// now allocate an array of items and fill it up
|
||||
std::vector<input_item_data *> itemarray(numitems);
|
||||
int curitem = 0;
|
||||
for (input_item_data *item = itemlist; item != nullptr; item = item->next)
|
||||
itemarray[curitem++] = item;
|
||||
|
||||
// sort it
|
||||
std::sort(itemarray.begin(), itemarray.end(), [](const input_item_data *i1, const input_item_data *i2) {
|
||||
int cmp = strcmp(i1->owner_name, i2->owner_name);
|
||||
if (cmp < 0)
|
||||
return true;
|
||||
if (cmp > 0)
|
||||
return false;
|
||||
if (i1->group < i2->group)
|
||||
return true;
|
||||
if (i1->group > i2->group)
|
||||
return false;
|
||||
const ioport_field &field1 = *reinterpret_cast<const ioport_field *>(i1->ref);
|
||||
const ioport_field &field2 = *reinterpret_cast<const ioport_field *>(i2->ref);
|
||||
if (field1.type() < field2.type())
|
||||
return true;
|
||||
if (field1.type() > field2.type())
|
||||
return false;
|
||||
std::vector<char32_t> codes1 = field1.keyboard_codes(0);
|
||||
std::vector<char32_t> codes2 = field2.keyboard_codes(0);
|
||||
if (!codes1.empty() && (codes2.empty() || codes1[0] < codes2[0]))
|
||||
return true;
|
||||
if (!codes2.empty() && (codes1.empty() || codes1[0] > codes2[0]))
|
||||
return false;
|
||||
cmp = strcmp(i1->name, i2->name);
|
||||
if (cmp < 0)
|
||||
return true;
|
||||
if (cmp > 0)
|
||||
return false;
|
||||
return i1->type < i2->type;
|
||||
});
|
||||
|
||||
// populate the menu in a standard fashion
|
||||
populate_sorted(std::move(itemarray));
|
||||
populate_sorted();
|
||||
}
|
||||
|
||||
menu_input_specific::~menu_input_specific()
|
||||
@ -367,43 +360,43 @@ void menu_input_specific::update_input(struct input_item_data *seqchangeditem)
|
||||
// menu from them
|
||||
//-------------------------------------------------
|
||||
|
||||
void menu_input::populate_sorted(std::vector<input_item_data *> &&itemarray)
|
||||
void menu_input::populate_sorted()
|
||||
{
|
||||
const char *nameformat[INPUT_TYPE_TOTAL] = { nullptr };
|
||||
std::string subtext;
|
||||
std::string prev_owner;
|
||||
bool first_entry = true;
|
||||
|
||||
/* create a mini lookup table for name format based on type */
|
||||
// create a mini lookup table for name format based on type
|
||||
nameformat[INPUT_TYPE_DIGITAL] = "%s";
|
||||
nameformat[INPUT_TYPE_ANALOG] = "%s Analog";
|
||||
nameformat[INPUT_TYPE_ANALOG_INC] = "%s Analog Inc";
|
||||
nameformat[INPUT_TYPE_ANALOG_DEC] = "%s Analog Dec";
|
||||
|
||||
/* build the menu */
|
||||
for (input_item_data *item : itemarray)
|
||||
// build the menu
|
||||
for (input_item_data &item : data)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
||||
/* generate the name of the item itself, based off the base name and the type */
|
||||
assert(nameformat[item->type] != nullptr);
|
||||
// generate the name of the item itself, based off the base name and the type
|
||||
assert(nameformat[item.type] != nullptr);
|
||||
|
||||
if (item->owner_name && strcmp(item->owner_name, prev_owner.c_str()) != 0)
|
||||
if (item.owner_name && strcmp(item.owner_name, prev_owner.c_str()) != 0)
|
||||
{
|
||||
if (first_entry)
|
||||
first_entry = false;
|
||||
else
|
||||
item_append(menu_item_type::SEPARATOR);
|
||||
item_append(string_format("[root%s]", item->owner_name), "", 0, nullptr);
|
||||
prev_owner.assign(item->owner_name);
|
||||
item_append(string_format("[root%s]", item.owner_name), "", 0, nullptr);
|
||||
prev_owner.assign(item.owner_name);
|
||||
}
|
||||
|
||||
std::string text = string_format(nameformat[item->type], item->name);
|
||||
if (item->is_optional)
|
||||
std::string text = string_format(nameformat[item.type], item.name);
|
||||
if (item.is_optional)
|
||||
text = "(" + text + ")";
|
||||
|
||||
/* if we're polling this item, use some spaces with left/right arrows */
|
||||
if (pollingref == item->ref && pollingseq == item->seqtype)
|
||||
if (pollingref == item.ref && pollingseq == item.seqtype)
|
||||
{
|
||||
subtext.assign(" ");
|
||||
flags |= FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW;
|
||||
@ -412,12 +405,12 @@ void menu_input::populate_sorted(std::vector<input_item_data *> &&itemarray)
|
||||
/* otherwise, generate the sequence name and invert it if different from the default */
|
||||
else
|
||||
{
|
||||
subtext = machine().input().seq_name(item->seq);
|
||||
flags |= (item->seq != *item->defseq) ? FLAG_INVERT : 0;
|
||||
subtext = machine().input().seq_name(item.seq);
|
||||
flags |= (item.seq != *item.defseq) ? FLAG_INVERT : 0;
|
||||
}
|
||||
|
||||
/* add the item */
|
||||
item_append(text, subtext, flags, item);
|
||||
item_append(text, subtext, flags, &item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "ui/menu.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -31,7 +33,6 @@ private:
|
||||
class menu_input : public menu
|
||||
{
|
||||
public:
|
||||
menu_input(mame_ui_manager &mui, render_container &container);
|
||||
virtual ~menu_input() override;
|
||||
|
||||
protected:
|
||||
@ -43,24 +44,26 @@ protected:
|
||||
INPUT_TYPE_TOTAL = INPUT_TYPE_ANALOG + SEQ_TYPE_TOTAL
|
||||
};
|
||||
|
||||
/* internal input menu item data */
|
||||
// internal input menu item data
|
||||
struct input_item_data
|
||||
{
|
||||
input_item_data * next; /* pointer to next item in the list */
|
||||
const void * ref; /* reference to type description for global inputs or field for game inputs */
|
||||
input_seq_type seqtype; /* sequence type */
|
||||
input_seq seq; /* copy of the live sequence */
|
||||
const input_seq * defseq; /* pointer to the default sequence */
|
||||
const char * name; /* pointer to the base name of the item */
|
||||
const char * owner_name; /* pointer to the name of the owner of the item */
|
||||
ioport_group group; /* group type */
|
||||
uint8_t type; /* type of port */
|
||||
bool is_optional; /* true if this input is considered optional */
|
||||
const void * ref = nullptr; // reference to type description for global inputs or field for game inputs
|
||||
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
|
||||
const char * owner_name = nullptr; // pointer to the name of the owner of the item
|
||||
ioport_group group = IPG_INVALID; // group type
|
||||
uint8_t type = 0U; // type of port
|
||||
bool is_optional = false; // true if this input is considered optional
|
||||
};
|
||||
using data_vector = std::vector<input_item_data>;
|
||||
|
||||
void populate_sorted(std::vector<input_item_data *> &&itemarray);
|
||||
menu_input(mame_ui_manager &mui, render_container &container);
|
||||
void populate_sorted();
|
||||
void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq);
|
||||
|
||||
data_vector data;
|
||||
const void * pollingref;
|
||||
input_seq_type pollingseq;
|
||||
input_item_data * pollingitem;
|
||||
@ -84,7 +87,7 @@ private:
|
||||
virtual void populate(float &customtop, float &custombottom) override;
|
||||
virtual void update_input(struct input_item_data *seqchangeditem) override;
|
||||
|
||||
int group;
|
||||
const int group;
|
||||
};
|
||||
|
||||
class menu_input_specific : public menu_input
|
||||
|
@ -32,12 +32,6 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
/***************************************************************************
|
||||
CONSTANTS
|
||||
***************************************************************************/
|
||||
|
||||
#define UI_MENU_POOL_SIZE 65536
|
||||
|
||||
/***************************************************************************
|
||||
GLOBAL VARIABLES
|
||||
***************************************************************************/
|
||||
@ -242,7 +236,6 @@ menu::menu(mame_ui_manager &mui, render_container &container)
|
||||
, m_container(container)
|
||||
, m_parent()
|
||||
, m_event()
|
||||
, m_pool(nullptr)
|
||||
, m_customtop(0.0f)
|
||||
, m_custombottom(0.0f)
|
||||
, m_resetpos(0)
|
||||
@ -266,19 +259,11 @@ menu::menu(mame_ui_manager &mui, render_container &container)
|
||||
|
||||
menu::~menu()
|
||||
{
|
||||
// free the pools
|
||||
while (m_pool)
|
||||
{
|
||||
pool *const ppool = m_pool;
|
||||
m_pool = m_pool->next;
|
||||
global_free_array(ppool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// reset - free all items in the menu,
|
||||
// and all memory allocated from the memory pool
|
||||
// reset - free all items in the menu
|
||||
//-------------------------------------------------
|
||||
|
||||
void menu::reset(reset_options options)
|
||||
@ -291,9 +276,7 @@ void menu::reset(reset_options options)
|
||||
else if (options == reset_options::REMEMBER_REF)
|
||||
m_resetref = get_selection_ref();
|
||||
|
||||
// reset all the pools and the item count back to 0
|
||||
for (pool *ppool = m_pool; ppool != nullptr; ppool = ppool->next)
|
||||
ppool->top = (uint8_t *)(ppool + 1);
|
||||
// reset the item count back to 0
|
||||
m_items.clear();
|
||||
m_visible_items = 0;
|
||||
m_selected = 0;
|
||||
@ -485,38 +468,6 @@ const menu::event *menu::process(uint32_t flags, float x0, float y0)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// m_pool_alloc - allocate temporary memory
|
||||
// from the menu's memory pool
|
||||
//-------------------------------------------------
|
||||
|
||||
void *menu::m_pool_alloc(size_t size)
|
||||
{
|
||||
assert(size < UI_MENU_POOL_SIZE);
|
||||
|
||||
// find a pool with enough room
|
||||
for (pool *ppool = m_pool; ppool != nullptr; ppool = ppool->next)
|
||||
{
|
||||
if (ppool->end - ppool->top >= size)
|
||||
{
|
||||
void *result = ppool->top;
|
||||
ppool->top += size;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// allocate a new pool
|
||||
pool *ppool = (pool *)global_alloc_array_clear<uint8_t>(sizeof(*ppool) + UI_MENU_POOL_SIZE);
|
||||
|
||||
// wire it up
|
||||
ppool->next = m_pool;
|
||||
m_pool = ppool;
|
||||
ppool->top = (uint8_t *)(ppool + 1);
|
||||
ppool->end = ppool->top + UI_MENU_POOL_SIZE;
|
||||
return m_pool_alloc(size);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// set_selection - changes the index
|
||||
// of the currently selected menu item
|
||||
|
@ -7,7 +7,6 @@
|
||||
Internal MAME menus for the user interface.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MAME_FRONTEND_UI_MENU_H
|
||||
#define MAME_FRONTEND_UI_MENU_H
|
||||
|
||||
@ -136,9 +135,6 @@ protected:
|
||||
running_machine &machine() const { return m_ui.machine(); }
|
||||
render_container &container() const { return m_container; }
|
||||
|
||||
// allocate temporary memory from the menu's memory pool
|
||||
void *m_pool_alloc(size_t size);
|
||||
|
||||
void reset(reset_options options);
|
||||
void reset_parent(reset_options options) { m_parent->reset(options); }
|
||||
|
||||
@ -334,13 +330,6 @@ private:
|
||||
using global_state_ptr = std::shared_ptr<global_state>;
|
||||
using global_state_map = std::map<running_machine *, global_state_ptr>;
|
||||
|
||||
struct pool
|
||||
{
|
||||
pool *next; // chain to next one
|
||||
uint8_t *top; // top of the pool
|
||||
uint8_t *end; // end of the pool
|
||||
};
|
||||
|
||||
// request the specific handling of the game selection main menu
|
||||
bool is_special_main_menu() const;
|
||||
void set_special_main_menu(bool disable);
|
||||
@ -381,7 +370,6 @@ private:
|
||||
render_container &m_container; // render_container we render to
|
||||
std::unique_ptr<menu> m_parent; // pointer to parent menu
|
||||
event m_event; // the UI event that occurred
|
||||
pool *m_pool; // list of memory pools
|
||||
|
||||
float m_customtop; // amount of extra height to add at the top
|
||||
float m_custombottom; // amount of extra height to add at the bottom
|
||||
|
Loading…
Reference in New Issue
Block a user