Cleaned up save/load state menu code.

* Fixed display names for states saved with keyboard keys
* Wait for at least one frame with no keys pressed before
  saving/loading.
This commit is contained in:
Vas Crabb 2020-08-22 21:12:38 +10:00
parent 90beaec1c6
commit f5fb44a9db
3 changed files with 106 additions and 87 deletions

View File

@ -9,13 +9,14 @@
***************************************************************************/ ***************************************************************************/
#pragma once
#ifndef MAME_FRONTEND_UI_MENUITEM_H #ifndef MAME_FRONTEND_UI_MENUITEM_H
#define MAME_FRONTEND_UI_MENUITEM_H #define MAME_FRONTEND_UI_MENUITEM_H
#pragma once
namespace ui { namespace ui {
// special menu item for separators // special menu item for separators
#define MENU_SEPARATOR_ITEM "---" #define MENU_SEPARATOR_ITEM "---"
@ -38,7 +39,7 @@ public:
std::string text; std::string text;
std::string subtext; std::string subtext;
uint32_t flags; uint32_t flags;
void *ref; void *ref;
menu_item_type type; // item type (eventually will go away when itemref is proper ui_menu_item class rather than void*) menu_item_type type; // item type (eventually will go away when itemref is proper ui_menu_item class rather than void*)
}; };

View File

@ -10,7 +10,9 @@
#include "emu.h" #include "emu.h"
#include "ui/state.h" #include "ui/state.h"
#include "emuopts.h" #include "emuopts.h"
#include "inputdev.h"
namespace ui { namespace ui {
@ -21,20 +23,6 @@ namespace ui {
namespace { namespace {
const int MAX_SAVED_STATE_JOYSTICK = 4;
//-------------------------------------------------
// keyboard_code
//-------------------------------------------------
input_code keyboard_code(input_item_id id)
{
// only supported for A-Z|0-9
assert((id >= ITEM_ID_A && id <= ITEM_ID_Z) || (id >= ITEM_ID_0 && id <= ITEM_ID_9));
return input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id);
}
//------------------------------------------------- //-------------------------------------------------
// keyboard_input_item_name // keyboard_input_item_name
//------------------------------------------------- //-------------------------------------------------
@ -57,20 +45,15 @@ std::string keyboard_input_item_name(input_item_id id)
std::pair<std::string, std::string> code_item_pair(const running_machine &machine, input_item_id id) std::pair<std::string, std::string> code_item_pair(const running_machine &machine, input_item_id id)
{ {
// identify the input code name (translated appropriately) // only supported for A-Z|0-9
input_code code = keyboard_code(id); assert((id >= ITEM_ID_A && id <= ITEM_ID_Z) || (id >= ITEM_ID_0 && id <= ITEM_ID_9));
std::string code_name = machine.input().code_name(code); input_code const code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id);
strmakelower(code_name);
// identify the keyboard item name return std::make_pair(keyboard_input_item_name(id), machine.input().code_name(code));
std::string input_item_name = keyboard_input_item_name(id);
// return them
return std::make_pair(code_name, input_item_name);
} }
} // anonymous namespace
};
/*************************************************************************** /***************************************************************************
FILE ENTRY FILE ENTRY
@ -104,8 +87,8 @@ menu_load_save_state_base::menu_load_save_state_base(mame_ui_manager &mui, rende
, m_header(header) , m_header(header)
, m_footer(footer) , m_footer(footer)
, m_must_exist(must_exist) , m_must_exist(must_exist)
, m_pause_checked(false)
, m_was_paused(false) , m_was_paused(false)
, m_keys_released(false)
{ {
} }
@ -139,11 +122,33 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
m_filename_to_code_map.emplace(code_item_pair(machine(), id)); m_filename_to_code_map.emplace(code_item_pair(machine(), id));
for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; id++) for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; id++)
m_filename_to_code_map.emplace(code_item_pair(machine(), id)); m_filename_to_code_map.emplace(code_item_pair(machine(), id));
// do joysticks
input_class const &sticks = machine().input().device_class(DEVICE_CLASS_JOYSTICK);
if (sticks.enabled())
{
for (int i = 0; sticks.maxindex() >= i; ++i)
{
input_device const *const stick = sticks.device(i);
if (stick)
{
for (input_item_id j = ITEM_ID_BUTTON1; (ITEM_ID_BUTTON32 >= j) && (stick->maxitem() >= j); ++j)
{
input_device_item const *const item = stick->item(j);
if (item && (item->itemclass() == ITEM_CLASS_SWITCH))
{
m_filename_to_code_map.emplace(
util::string_format("joy%i-%i", i, j - ITEM_ID_BUTTON1 + 1),
machine().input().code_name(item->code()));
}
}
}
}
}
} }
// open the state directory // open the state directory
std::string statedir = state_directory(); osd::directory::ptr dir = osd::directory::open(state_directory());
osd::directory::ptr dir = osd::directory::open(statedir);
// create a separate vector, so we can add sorted entries to the menu // create a separate vector, so we can add sorted entries to the menu
std::vector<const file_entry *> m_entries_vec; std::vector<const file_entry *> m_entries_vec;
@ -173,12 +178,12 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
// sort the vector; put recently modified state files at the top // sort the vector; put recently modified state files at the top
std::sort( std::sort(
m_entries_vec.begin(), m_entries_vec.begin(),
m_entries_vec.end(), m_entries_vec.end(),
[](const file_entry *a, const file_entry *b) [] (const file_entry *a, const file_entry *b)
{ {
return a->last_modified() > b->last_modified(); return a->last_modified() > b->last_modified();
}); });
// add the entries // add the entries
for (const file_entry *entry : m_entries_vec) for (const file_entry *entry : m_entries_vec)
@ -190,11 +195,11 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
// format the text // format the text
std::string text = util::string_format("%s: %s", std::string text = util::string_format("%s: %s",
entry->visible_name(), entry->visible_name(),
time_string); time_string);
// append the menu item // append the menu item
void *itemref = itemref_from_file_entry(*entry); void *const itemref = itemref_from_file_entry(*entry);
item_append(std::move(text), std::string(), 0, itemref); item_append(std::move(text), std::string(), 0, itemref);
// is this item selected? // is this item selected?
@ -202,18 +207,25 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
set_selection(itemref); set_selection(itemref);
} }
if (m_entries_vec.empty())
{
item_append(_("No save states found"), std::string(), 0, nullptr);
set_selection(nullptr);
}
item_append(menu_item_type::SEPARATOR);
// set up custom render proc // set up custom render proc
customtop = ui().get_line_height() + 3.0f * ui().box_tb_border(); customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border(); custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
// pause if appropriate // pause if appropriate
if (!m_pause_checked) m_was_paused = machine().paused();
{ if (!m_was_paused)
m_was_paused = machine().paused(); machine().pause();
if (!m_was_paused)
machine().pause(); // get ready to poll inputs
m_pause_checked = true; machine().input().reset_polling();
} m_keys_released = false;
} }
@ -224,14 +236,17 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
void menu_load_save_state_base::handle() void menu_load_save_state_base::handle()
{ {
// process the menu // process the menu
const event *event = process(0); event const *const event = process(0);
// process the event // process the event
if (event && (event->iptkey == IPT_UI_SELECT)) if (event && (event->iptkey == IPT_UI_SELECT))
{ {
// user selected one of the entries if (event->itemref)
const file_entry &entry = file_entry_from_itemref(event->itemref); {
slot_selected(std::string(entry.file_name())); // user selected one of the entries
file_entry const &entry = file_entry_from_itemref(event->itemref);
slot_selected(std::string(entry.file_name()));
}
} }
else else
{ {
@ -249,12 +264,9 @@ void menu_load_save_state_base::handle()
std::string menu_load_save_state_base::get_visible_name(const std::string &file_name) std::string menu_load_save_state_base::get_visible_name(const std::string &file_name)
{ {
if (file_name.size() == 1) auto const iter = m_filename_to_code_map.find(file_name);
{ if (iter != m_filename_to_code_map.end())
auto iter = m_filename_to_code_map.find(file_name); return iter->second;
if (iter != m_filename_to_code_map.end())
return iter->second;
}
// otherwise these are the same // otherwise these are the same
return file_name; return file_name;
@ -267,28 +279,22 @@ std::string menu_load_save_state_base::get_visible_name(const std::string &file_
std::string menu_load_save_state_base::poll_inputs() std::string menu_load_save_state_base::poll_inputs()
{ {
// poll A-Z input_code const code = machine().input().poll_switches();
for (input_item_id id = ITEM_ID_A; id <= ITEM_ID_Z; id++) if (INPUT_CODE_INVALID == code)
{ {
if (machine().input().code_pressed_once(keyboard_code(id))) m_keys_released = true;
return keyboard_input_item_name(id);
} }
else if (m_keys_released)
// poll 0-9
for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; id++)
{ {
if (machine().input().code_pressed_once(keyboard_code(id))) input_item_id const id = code.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)))
return keyboard_input_item_name(id); return keyboard_input_item_name(id);
}
// poll joysticks // joystick buttons
for (int joy_index = 0; joy_index <= MAX_SAVED_STATE_JOYSTICK; joy_index++) 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);
for (input_item_id id = ITEM_ID_BUTTON1; id <= ITEM_ID_BUTTON32; ++id)
{
if (machine().input().code_pressed_once(input_code(DEVICE_CLASS_JOYSTICK, joy_index, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
return util::string_format("joy%i-%i", joy_index, id - ITEM_ID_BUTTON1 + 1);
}
} }
return ""; return "";
} }
@ -328,7 +334,16 @@ void menu_load_save_state_base::slot_selected(std::string &&name)
void menu_load_save_state_base::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) void menu_load_save_state_base::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{ {
extra_text_render(top, bottom, origx1, origy1, origx2, origy2, m_header, m_footer); extra_text_render(top, bottom, origx1, origy1, origx2, origy2, m_header, nullptr);
if (m_footer)
{
char const *const text[] = { m_footer };
draw_text_box(
std::begin(text), std::end(text),
origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
ui::text_layout::CENTER, ui::text_layout::NEVER, false,
ui().colors().text_color(), ui().colors().background_color(), 1.0f);
}
} }
@ -360,9 +375,9 @@ std::string menu_load_save_state_base::state_directory() const
{ {
const char *stateopt = machine().options().state_name(); const char *stateopt = machine().options().state_name();
return util::string_format("%s%s%s", return util::string_format("%s%s%s",
machine().options().state_directory(), machine().options().state_directory(),
PATH_SEPARATOR, PATH_SEPARATOR,
machine().get_statename(stateopt)); machine().get_statename(stateopt));
} }
@ -385,7 +400,7 @@ bool menu_load_save_state_base::is_present(const std::string &name) const
//------------------------------------------------- //-------------------------------------------------
menu_load_state::menu_load_state(mame_ui_manager &mui, render_container &container) menu_load_state::menu_load_state(mame_ui_manager &mui, render_container &container)
: menu_load_save_state_base(mui, container, _("Load State"), _("Select position to load from"), true) : menu_load_save_state_base(mui, container, _("Load State"), _("Select state to load"), true)
{ {
} }
@ -409,7 +424,7 @@ void menu_load_state::process_file(std::string &&file_name)
//------------------------------------------------- //-------------------------------------------------
menu_save_state::menu_save_state(mame_ui_manager &mui, render_container &container) menu_save_state::menu_save_state(mame_ui_manager &mui, render_container &container)
: menu_load_save_state_base(mui, container, _("Save State"), _("Select position to save to"), false) : menu_load_save_state_base(mui, container, _("Save State"), _("Press a key or joystick button, or select state to overwrite"), false)
{ {
} }

View File

@ -7,14 +7,16 @@
Menus for saving and loading state Menus for saving and loading state
***************************************************************************/ ***************************************************************************/
#pragma once
#ifndef MAME_FRONTEND_UI_STATE_H #ifndef MAME_FRONTEND_UI_STATE_H
#define MAME_FRONTEND_UI_STATE_H #define MAME_FRONTEND_UI_STATE_H
#pragma once
#include "ui/menu.h" #include "ui/menu.h"
#include <chrono>
#include <unordered_map>
namespace ui { namespace ui {
// ======================> menu_load_save_state_base // ======================> menu_load_save_state_base
@ -54,14 +56,15 @@ private:
std::unordered_map<std::string, file_entry> m_file_entries; std::unordered_map<std::string, file_entry> m_file_entries;
std::unordered_map<std::string, std::string> m_filename_to_code_map; std::unordered_map<std::string, std::string> m_filename_to_code_map;
const char * m_header; char const *const m_header;
const char * m_footer; char const *const m_footer;
bool m_must_exist; bool const m_must_exist;
bool m_pause_checked;
bool m_was_paused; bool m_was_paused;
bool m_keys_released;
static void *itemref_from_file_entry(const file_entry &entry); static void *itemref_from_file_entry(const file_entry &entry);
static const file_entry &file_entry_from_itemref(void *itemref); static const file_entry &file_entry_from_itemref(void *itemref);
void try_select_slot(std::string &&name); void try_select_slot(std::string &&name);
void slot_selected(std::string &&name); void slot_selected(std::string &&name);
std::string state_directory() const; std::string state_directory() const;
@ -92,6 +95,6 @@ protected:
virtual void process_file(std::string &&file_name) override; virtual void process_file(std::string &&file_name) override;
}; };
}; } // namespace ui
#endif // MAME_FRONTEND_UI_STATE_H #endif // MAME_FRONTEND_UI_STATE_H