ui: improve layout of art/info box, support mousewheel scroll in info

text, allow home/end to jump to beginning/end of filter list,
consolidate logic

(nw) A number of vestigial constants have been removed.  Some hacky
input types that were just used as a trick to pass information between
menu functions are gone.  MACHINE_NO_STANDALONE is a relic from when
drivers were used as arbitrary ROM containers, and in a world of
first-class devices this is no longer necessary.
This commit is contained in:
Vas Crabb 2017-08-10 15:03:52 +10:00
parent 80b2ca6088
commit 54ff6183fc
17 changed files with 466 additions and 510 deletions

View File

@ -308,7 +308,7 @@ void driver_enumerator::find_approximate_matches(const char *string, std::size_t
for (int index = 0; index < s_driver_count; index++)
{
// skip things that can't run
if (m_included[index] && !(s_drivers_sorted[index]->flags & MACHINE_NO_STANDALONE))
if (m_included[index])
{
// pick the best match between driver name and description
int curpenalty = penalty_compare(string, s_drivers_sorted[index]->type.fullname());

View File

@ -47,13 +47,12 @@ struct machine_flags
SUPPORTS_SAVE = 0x00000080, // system supports save states
NO_COCKTAIL = 0x00000100, // screen flip support is missing
IS_BIOS_ROOT = 0x00000200, // this driver entry is a BIOS root
NO_STANDALONE = 0x00000400, // this driver cannot stand alone
REQUIRES_ARTWORK = 0x00000800, // requires external artwork for key game elements
CLICKABLE_ARTWORK = 0x00001000, // artwork is clickable and requires mouse cursor
UNOFFICIAL = 0x00002000, // unofficial hardware modification
NO_SOUND_HW = 0x00004000, // system has no sound output
MECHANICAL = 0x00008000, // contains mechanical parts (pinball, redemption games, ...)
IS_INCOMPLETE = 0x00010000 // official system with blatantly incomplete hardware/software
REQUIRES_ARTWORK = 0x00000400, // requires external artwork for key game elements
CLICKABLE_ARTWORK = 0x00000800, // artwork is clickable and requires mouse cursor
UNOFFICIAL = 0x00001000, // unofficial hardware modification
NO_SOUND_HW = 0x00002000, // system has no sound output
MECHANICAL = 0x00004000, // contains mechanical parts (pinball, redemption games, ...)
IS_INCOMPLETE = 0x00008000 // official system with blatantly incomplete hardware/software
};
};
@ -69,7 +68,6 @@ constexpr u64 MACHINE_NOT_WORKING = machine_flags::NOT_WORKING;
constexpr u64 MACHINE_SUPPORTS_SAVE = machine_flags::SUPPORTS_SAVE;
constexpr u64 MACHINE_NO_COCKTAIL = machine_flags::NO_COCKTAIL;
constexpr u64 MACHINE_IS_BIOS_ROOT = machine_flags::IS_BIOS_ROOT;
constexpr u64 MACHINE_NO_STANDALONE = machine_flags::NO_STANDALONE;
constexpr u64 MACHINE_REQUIRES_ARTWORK = machine_flags::REQUIRES_ARTWORK;
constexpr u64 MACHINE_CLICKABLE_ARTWORK = machine_flags::CLICKABLE_ARTWORK;
constexpr u64 MACHINE_UNOFFICIAL = machine_flags::UNOFFICIAL;

View File

@ -865,12 +865,6 @@ inline void construct_core_types_UI(simple_list<input_type_entry> &typelist)
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TAPE_STOP, "UI (First) Tape Stop", input_seq(KEYCODE_F2, KEYCODE_LSHIFT) )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DATS, "UI External DAT View", input_seq(KEYCODE_LALT, KEYCODE_D) )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FAVORITES, "UI Add/Remove favorites",input_seq(KEYCODE_LALT, KEYCODE_F) )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_UP_FILTER, nullptr, input_seq() )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DOWN_FILTER, nullptr, input_seq() )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_LEFT_PANEL, nullptr, input_seq() )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RIGHT_PANEL, nullptr, input_seq() )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_UP_PANEL, nullptr, input_seq() )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DOWN_PANEL, nullptr, input_seq() )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_EXPORT, "UI Export list", input_seq(KEYCODE_LALT, KEYCODE_E) )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_AUDIT_FAST, "UI Audit Unavailable", input_seq(KEYCODE_F1, input_seq::not_code, KEYCODE_LSHIFT) )
INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_AUDIT_ALL, "UI Audit All", input_seq(KEYCODE_F1, KEYCODE_LSHIFT) )

View File

@ -361,12 +361,6 @@ enum ioport_type
IPT_UI_TAPE_STOP,
IPT_UI_DATS,
IPT_UI_FAVORITES,
IPT_UI_UP_FILTER,
IPT_UI_DOWN_FILTER,
IPT_UI_LEFT_PANEL,
IPT_UI_RIGHT_PANEL,
IPT_UI_UP_PANEL,
IPT_UI_DOWN_PANEL,
IPT_UI_EXPORT,
IPT_UI_AUDIT_FAST,
IPT_UI_AUDIT_ALL,

View File

@ -360,8 +360,7 @@ void cli_frontend::listfull(const std::vector<std::string> &args)
// iterate through drivers and output the info
while (drivlist.next())
if ((drivlist.driver().flags & machine_flags::NO_STANDALONE) == 0)
osd_printf_info("%-18s\"%s\"\n", drivlist.driver().name, drivlist.driver().type.fullname());
osd_printf_info("%-18s\"%s\"\n", drivlist.driver().name, drivlist.driver().type.fullname());
}

View File

@ -443,8 +443,6 @@ void info_xml_creator::output_one(driver_enumerator &drivlist, device_type_set *
// append bios and runnable flags
if (driver.flags & machine_flags::IS_BIOS_ROOT)
fprintf(m_output, " isbios=\"yes\"");
if (driver.flags & machine_flags::NO_STANDALONE)
fprintf(m_output, " runnable=\"no\"");
if (driver.flags & machine_flags::MECHANICAL)
fprintf(m_output, " ismechanical=\"yes\"");

View File

@ -241,7 +241,7 @@ void favorite_manager::add_favorite_game()
// remove a favorite from list
//-------------------------------------------------
void favorite_manager::remove_favorite_game(ui_software_info &swinfo)
void favorite_manager::remove_favorite_game(ui_software_info const &swinfo)
{
for (auto e = m_list.begin(); e != m_list.end(); ++e)
if (e->second == swinfo)

View File

@ -113,7 +113,7 @@ public:
// remove
void remove_favorite_game();
void remove_favorite_game(ui_software_info &swinfo);
void remove_favorite_game(ui_software_info const &swinfo);
private:
const char *favorite_filename = "favorites.ini";

View File

@ -635,8 +635,7 @@ void menu_export::handle()
// iterate through drivers and output the info
while (drvlist.next())
if (!(drvlist.driver().flags & machine_flags::NO_STANDALONE))
util::stream_format(buffer, "%-18s\"%s\"\n", drvlist.driver().name, drvlist.driver().type.fullname());
util::stream_format(buffer, "%-18s\"%s\"\n", drvlist.driver().name, drvlist.driver().type.fullname());
file.puts(buffer.str().c_str());
file.close();
machine().popmessage(_("%s.txt saved under ui folder."), filename.c_str());

View File

@ -196,220 +196,182 @@ void menu_select_game::handle()
// process the menu
const event *menu_event = process(PROCESS_LR_REPEAT);
if (menu_event && menu_event->itemref)
if (menu_event)
{
if (dismiss_error())
{
// reset the error on any future menu_event
}
else if (menu_event->iptkey == IPT_UI_SELECT)
else switch (menu_event->iptkey)
{
// handle selections
if (get_focus() == focused_menu::MAIN)
{
if (isfavorite())
inkey_select_favorite(menu_event);
else
inkey_select(menu_event);
}
else if (get_focus() == focused_menu::LEFT)
{
l_hover = highlight;
check_filter = true;
m_prev_selected = nullptr;
}
}
else if (menu_event->iptkey == IPT_CUSTOM)
{
// handle IPT_CUSTOM (mouse right click)
if (!isfavorite())
{
menu::stack_push<menu_machine_configure>(
ui(), container(),
reinterpret_cast<const game_driver *>(m_prev_selected),
menu_event->mouse.x0, menu_event->mouse.y0);
}
else
{
ui_software_info *sw = reinterpret_cast<ui_software_info *>(m_prev_selected);
menu::stack_push<menu_machine_configure>(
ui(), container(),
(const game_driver *)sw->driver,
menu_event->mouse.x0, menu_event->mouse.y0);
}
}
else if (menu_event->iptkey == IPT_UI_LEFT)
{
// handle UI_LEFT
case IPT_UI_UP:
if ((get_focus() == focused_menu::LEFT) && (machine_filter::FIRST < highlight))
--highlight;
break;
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view > FIRST_VIEW)
{
// Images
ui_globals::curimage_view--;
ui_globals::switch_image = true;
ui_globals::default_image = false;
}
else if (ui_globals::rpanel == RP_INFOS)
{
// Infos
change_info_pane(-1);
}
}
else if (menu_event->iptkey == IPT_UI_RIGHT)
{
// handle UI_RIGHT
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view < LAST_VIEW)
{
// Images
ui_globals::curimage_view++;
ui_globals::switch_image = true;
ui_globals::default_image = false;
}
else if (ui_globals::rpanel == RP_INFOS)
{
// Infos
change_info_pane(1);
}
}
else if (menu_event->iptkey == IPT_UI_UP_FILTER && highlight > machine_filter::FIRST)
{
// handle UI_UP_FILTER
highlight--;
}
else if (menu_event->iptkey == IPT_UI_DOWN_FILTER && highlight < machine_filter::LAST)
{
// handle UI_DOWN_FILTER
highlight++;
}
else if (menu_event->iptkey == IPT_UI_LEFT_PANEL)
{
// handle UI_LEFT_PANEL
ui_globals::rpanel = RP_IMAGES;
}
else if (menu_event->iptkey == IPT_UI_RIGHT_PANEL)
{
// handle UI_RIGHT_PANEL
ui_globals::rpanel = RP_INFOS;
}
else if (menu_event->iptkey == IPT_UI_CANCEL && !m_search.empty())
{
// escape pressed with non-empty text clears the text
m_search.clear();
reset(reset_options::SELECT_FIRST);
}
else if (menu_event->iptkey == IPT_UI_DATS)
{
// handle UI_DATS
if (!isfavorite())
{
const game_driver *driver = (const game_driver *)menu_event->itemref;
if ((uintptr_t)driver > skip_main_items && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", driver->name, true))
menu::stack_push<menu_dats_view>(ui(), container(), driver);
}
else
{
ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref;
case IPT_UI_DOWN:
if ((get_focus() == focused_menu::LEFT) && (machine_filter::LAST > highlight))
highlight++;
break;
if ((uintptr_t)ui_swinfo > skip_main_items)
{
if (ui_swinfo->startempty == 1 && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", ui_swinfo->driver->name, true))
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo->driver);
else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(ui_swinfo->shortname).append(1, ',').append(ui_swinfo->listname).c_str()) || !ui_swinfo->usage.empty())
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo);
}
}
}
else if (menu_event->iptkey == IPT_UI_FAVORITES)
{
// handle UI_FAVORITES
if (!isfavorite())
{
const game_driver *driver = (const game_driver *)menu_event->itemref;
if ((uintptr_t)driver > skip_main_items)
{
favorite_manager &mfav = mame_machine_manager::instance()->favorite();
if (!mfav.isgame_favorite(driver))
{
mfav.add_favorite_game(driver);
machine().popmessage(_("%s\n added to favorites list."), driver->type.fullname());
}
case IPT_UI_HOME:
if (get_focus() == focused_menu::LEFT)
highlight = machine_filter::FIRST;
break;
else
{
mfav.remove_favorite_game();
machine().popmessage(_("%s\n removed from favorites list."), driver->type.fullname());
}
}
}
else
{
ui_software_info *swinfo = (ui_software_info *)menu_event->itemref;
if ((uintptr_t)swinfo > skip_main_items)
{
machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname.c_str());
mame_machine_manager::instance()->favorite().remove_favorite_game(*swinfo);
reset(reset_options::SELECT_FIRST);
}
}
}
else if (menu_event->iptkey == IPT_UI_EXPORT)
{
// handle UI_EXPORT
inkey_export();
}
else if (menu_event->iptkey == IPT_UI_AUDIT_FAST && !m_unavailsortedlist.empty())
{
// handle UI_AUDIT_FAST
menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, m_unavailsortedlist, 1);
}
else if (menu_event->iptkey == IPT_UI_AUDIT_ALL)
{
// handle UI_AUDIT_ALL
menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, m_unavailsortedlist, 2);
}
else if (menu_event->iptkey == IPT_SPECIAL)
{
case IPT_UI_END:
if (get_focus() == focused_menu::LEFT)
highlight = machine_filter::LAST;
break;
case IPT_SPECIAL:
// typed characters append to the buffer
inkey_special(menu_event);
}
else if (menu_event->iptkey == IPT_UI_CONFIGURE)
{
inkey_navigation();
}
else if (menu_event->iptkey == IPT_OTHER)
{
break;
case IPT_OTHER:
// this is generated when something in the left box is clicked
m_prev_selected = nullptr;
check_filter = true;
highlight = l_hover;
}
}
break;
if (menu_event && !menu_event->itemref)
{
if (menu_event->iptkey == IPT_SPECIAL)
{
inkey_special(menu_event);
}
else if (menu_event->iptkey == IPT_UI_CONFIGURE)
{
case IPT_UI_CONFIGURE:
inkey_navigation();
}
else if (menu_event->iptkey == IPT_OTHER)
{
set_focus(focused_menu::LEFT);
m_prev_selected = nullptr;
l_hover = highlight;
check_filter = true;
}
else if (menu_event->iptkey == IPT_UI_UP_FILTER && highlight > machine_filter::FIRST)
{
// handle UI_UP_FILTER
highlight--;
}
else if (menu_event->iptkey == IPT_UI_DOWN_FILTER && highlight < machine_filter::LAST)
{
// handle UI_DOWN_FILTER
highlight++;
break;
default:
if (menu_event->itemref)
{
switch (menu_event->iptkey)
{
case IPT_UI_SELECT:
if (get_focus() == focused_menu::MAIN)
{
if (isfavorite())
inkey_select_favorite(menu_event);
else
inkey_select(menu_event);
}
else if (get_focus() == focused_menu::LEFT)
{
l_hover = highlight;
check_filter = true;
m_prev_selected = nullptr;
}
break;
case IPT_CUSTOM:
// handle IPT_CUSTOM (mouse right click)
if (!isfavorite())
{
menu::stack_push<menu_machine_configure>(
ui(), container(),
reinterpret_cast<const game_driver *>(m_prev_selected),
menu_event->mouse.x0, menu_event->mouse.y0);
}
else
{
ui_software_info *sw = reinterpret_cast<ui_software_info *>(m_prev_selected);
menu::stack_push<menu_machine_configure>(
ui(), container(),
(const game_driver *)sw->driver,
menu_event->mouse.x0, menu_event->mouse.y0);
}
break;
case IPT_UI_LEFT:
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view > FIRST_VIEW)
{
// Images
ui_globals::curimage_view--;
ui_globals::switch_image = true;
ui_globals::default_image = false;
}
else if (ui_globals::rpanel == RP_INFOS)
{
// Infos
change_info_pane(-1);
}
break;
case IPT_UI_RIGHT:
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view < LAST_VIEW)
{
// Images
ui_globals::curimage_view++;
ui_globals::switch_image = true;
ui_globals::default_image = false;
}
else if (ui_globals::rpanel == RP_INFOS)
{
// Infos
change_info_pane(1);
}
break;
case IPT_UI_DATS:
if (!isfavorite())
{
const game_driver *driver = (const game_driver *)menu_event->itemref;
if ((uintptr_t)driver > skip_main_items && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", driver->name, true))
menu::stack_push<menu_dats_view>(ui(), container(), driver);
}
else
{
ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref;
if ((uintptr_t)ui_swinfo > skip_main_items)
{
if (ui_swinfo->startempty == 1 && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", ui_swinfo->driver->name, true))
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo->driver);
else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(ui_swinfo->shortname).append(1, ',').append(ui_swinfo->listname).c_str()) || !ui_swinfo->usage.empty())
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo);
}
}
case IPT_UI_FAVORITES:
if (uintptr_t(menu_event->itemref) > skip_main_items)
{
favorite_manager &mfav(mame_machine_manager::instance()->favorite());
if (!isfavorite())
{
game_driver const *const driver(reinterpret_cast<game_driver const *>(menu_event->itemref));
if (!mfav.isgame_favorite(driver))
{
mfav.add_favorite_game(driver);
machine().popmessage(_("%s\n added to favorites list."), driver->type.fullname());
}
else
{
mfav.remove_favorite_game();
machine().popmessage(_("%s\n removed from favorites list."), driver->type.fullname());
}
}
else
{
ui_software_info const *const swinfo(reinterpret_cast<ui_software_info const *>(menu_event->itemref));
machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname);
mfav.remove_favorite_game(*swinfo);
reset(reset_options::SELECT_FIRST);
}
}
break;
case IPT_UI_EXPORT:
inkey_export();
break;
case IPT_UI_AUDIT_FAST:
if (!m_unavailsortedlist.empty())
menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, m_unavailsortedlist, 1);
break;
case IPT_UI_AUDIT_ALL:
menu::stack_push<menu_audit>(ui(), container(), m_availsortedlist, m_unavailsortedlist, 2);
break;
}
}
}
}

View File

@ -28,9 +28,6 @@ public:
// force game select menu
static void force_game_select(mame_ui_manager &mui, render_container &container);
protected:
virtual bool menu_has_search_active() override { return !m_search.empty(); }
private:
enum
{
@ -40,7 +37,6 @@ private:
};
enum { VISIBLE_GAMES_IN_SEARCH = 200 };
std::string m_search;
static bool first_start;
static int m_isabios;
int highlight;

View File

@ -1106,11 +1106,23 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
return;
}
// hitting cancel also pops the stack
if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
{
if (!m_ui_error && !menu_has_search_active())
if (m_ui_error)
{
// dismiss error
}
else if (menu_has_search_active())
{
// escape pressed with non-empty search text clears it
m_search.clear();
reset(reset_options::SELECT_FIRST);
}
else
{
// otherwise pop the stack
stack_pop();
}
return;
}
@ -1118,17 +1130,17 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
validate_selection(1);
// swallow left/right keys if they are not appropriate
bool ignoreleft = ((selected_item().flags & FLAG_LEFT_ARROW) == 0);
bool ignoreright = ((selected_item().flags & FLAG_RIGHT_ARROW) == 0);
bool leftclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_LEFT_PANEL);
bool rightclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL);
bool const ignoreleft = ((selected_item().flags & FLAG_LEFT_ARROW) == 0);
bool const ignoreright = ((selected_item().flags & FLAG_RIGHT_ARROW) == 0);
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);
// accept left/right keys as-is with repeat
if (!ignoreleft && exclusive_input_pressed(iptkey, IPT_UI_LEFT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
{
// Swap the right panel
if (m_focus == focused_menu::RIGHTTOP)
iptkey = IPT_UI_LEFT_PANEL;
ui_globals::rpanel = RP_IMAGES;
return;
}
@ -1136,30 +1148,26 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
{
// Swap the right panel
if (m_focus == focused_menu::RIGHTTOP)
iptkey = IPT_UI_RIGHT_PANEL;
ui_globals::rpanel = RP_INFOS;
return;
}
// up backs up by one item
if (exclusive_input_pressed(iptkey, IPT_UI_UP, 6))
{
// Filter
if (!leftclose && m_focus == focused_menu::LEFT)
{
iptkey = IPT_UI_UP_FILTER;
return;
}
// Infos
if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
{
iptkey = IPT_UI_UP_PANEL;
m_topline_datsview--;
return;
}
if (selected == visible_items + 1 || is_first_selected() || m_ui_error)
else if (selected == visible_items + 1 || is_first_selected() || m_ui_error)
{
return;
}
selected--;
@ -1170,26 +1178,21 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
// down advances by one item
if (exclusive_input_pressed(iptkey, IPT_UI_DOWN, 6))
{
// Filter
if (!leftclose && m_focus == focused_menu::LEFT)
{
iptkey = IPT_UI_DOWN_FILTER;
return;
}
// Infos
if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
{
iptkey = IPT_UI_DOWN_PANEL;
m_topline_datsview++;
return;
}
if (is_last_selected() || selected == visible_items - 1 || m_ui_error)
else if (is_last_selected() || selected == visible_items - 1 || m_ui_error)
{
return;
}
selected++;
if (selected == top_line + m_visible_items + (top_line != 0))
top_line++;
}
@ -1200,7 +1203,6 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
// Infos
if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
{
iptkey = IPT_UI_DOWN_PANEL;
m_topline_datsview -= m_right_visible_lines - 1;
return;
}
@ -1222,7 +1224,6 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
// Infos
if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
{
iptkey = IPT_UI_DOWN_PANEL;
m_topline_datsview += m_right_visible_lines - 1;
return;
}
@ -1241,10 +1242,12 @@ 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))
{
// Infos
if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
if (!leftclose && m_focus == focused_menu::LEFT)
{
return;
}
else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
{
iptkey = IPT_UI_DOWN_PANEL;
m_topline_datsview = 0;
return;
}
@ -1259,10 +1262,12 @@ void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
// end goes to the last
if (exclusive_input_pressed(iptkey, IPT_UI_END, 0))
{
// Infos
if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
if (!leftclose && m_focus == focused_menu::LEFT)
{
return;
}
else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
{
iptkey = IPT_UI_DOWN_PANEL;
m_topline_datsview = m_total_lines;
return;
}
@ -1320,7 +1325,9 @@ void menu_select_launch::handle_events(uint32_t flags, event &ev)
machine().ui_input().push_mouse_down_event(mouse_target, target_x, target_y);
}
else
{
reset_pressed();
}
}
// loop while we have interesting events
@ -1328,177 +1335,182 @@ void menu_select_launch::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_MOUSE_DOWN:
// if we are hovering over a valid item, select it with a single click
case UI_EVENT_MOUSE_DOWN:
if (m_ui_error)
{
if (m_ui_error)
ev.iptkey = IPT_OTHER;
stop = true;
}
else
{
if (hover >= 0 && hover < item.size())
{
if (hover >= visible_items - 1 && selected < visible_items)
m_prev_selected = get_selection_ref();
selected = hover;
m_focus = focused_menu::MAIN;
}
else if (hover == HOVER_ARROW_UP)
{
selected -= m_visible_items;
if (selected < 0)
selected = 0;
top_line -= m_visible_items - (top_line + m_visible_lines == visible_items);
set_pressed();
}
else if (hover == HOVER_ARROW_DOWN)
{
selected += m_visible_lines - 2 + (selected == 0);
if (selected >= visible_items)
selected = visible_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 - 1;
else if (hover == HOVER_DAT_UP)
m_topline_datsview -= m_right_visible_lines - 1;
else if (hover == HOVER_LPANEL_ARROW)
{
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;
}
else if (hover == HOVER_RPANEL_ARROW)
{
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;
}
else if (hover == HOVER_B_FAV)
{
ev.iptkey = IPT_UI_FAVORITES;
stop = true;
}
else if (hover == HOVER_B_EXPORT)
{
ev.iptkey = IPT_UI_EXPORT;
stop = true;
}
else if (hover == HOVER_B_DATS)
{
ev.iptkey = IPT_UI_DATS;
stop = true;
}
else if (hover >= HOVER_RP_FIRST && hover <= HOVER_RP_LAST)
{
ui_globals::rpanel = (HOVER_RP_FIRST - hover) * (-1);
stop = true;
}
else if (hover >= HOVER_SW_FILTER_FIRST && hover <= HOVER_SW_FILTER_LAST)
{
l_sw_hover = (HOVER_SW_FILTER_FIRST - hover) * (-1);
ev.iptkey = IPT_OTHER;
stop = true;
}
else
else if (hover >= HOVER_FILTER_FIRST && hover <= HOVER_FILTER_LAST)
{
if (hover >= 0 && hover < item.size())
{
if (hover >= visible_items - 1 && selected < visible_items)
m_prev_selected = get_selection_ref();
selected = hover;
m_focus = focused_menu::MAIN;
}
else if (hover == HOVER_ARROW_UP)
{
selected -= m_visible_items;
if (selected < 0)
selected = 0;
top_line -= m_visible_items - (top_line + m_visible_lines == visible_items);
set_pressed();
}
else if (hover == HOVER_ARROW_DOWN)
{
selected += m_visible_lines - 2 + (selected == 0);
if (selected >= visible_items)
selected = visible_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 - 1;
else if (hover == HOVER_DAT_UP)
m_topline_datsview -= m_right_visible_lines - 1;
else if (hover == HOVER_LPANEL_ARROW)
{
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;
}
else if (hover == HOVER_RPANEL_ARROW)
{
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;
}
else if (hover == HOVER_B_FAV)
{
ev.iptkey = IPT_UI_FAVORITES;
stop = true;
}
else if (hover == HOVER_B_EXPORT)
{
ev.iptkey = IPT_UI_EXPORT;
stop = true;
}
else if (hover == HOVER_B_DATS)
{
ev.iptkey = IPT_UI_DATS;
stop = true;
}
else if (hover >= HOVER_RP_FIRST && hover <= HOVER_RP_LAST)
{
ui_globals::rpanel = (HOVER_RP_FIRST - hover) * (-1);
stop = true;
}
else if (hover >= HOVER_SW_FILTER_FIRST && hover <= HOVER_SW_FILTER_LAST)
{
l_sw_hover = (HOVER_SW_FILTER_FIRST - hover) * (-1);
ev.iptkey = IPT_OTHER;
stop = true;
}
else if (hover >= HOVER_FILTER_FIRST && hover <= HOVER_FILTER_LAST)
{
l_hover = (HOVER_FILTER_FIRST - hover) * (-1);
ev.iptkey = IPT_OTHER;
stop = true;
}
l_hover = (HOVER_FILTER_FIRST - hover) * (-1);
ev.iptkey = IPT_OTHER;
stop = true;
}
break;
}
break;
// if we are hovering over a valid item, fake a UI_SELECT with a double-click
case UI_EVENT_MOUSE_DOUBLE_CLICK:
if (hover >= 0 && hover < item.size())
{
selected = hover;
ev.iptkey = IPT_UI_SELECT;
}
// if we are hovering over a valid item, fake a UI_SELECT with a double-click
case UI_EVENT_MOUSE_DOUBLE_CLICK:
if (hover >= 0 && hover < item.size())
{
selected = hover;
ev.iptkey = IPT_UI_SELECT;
}
if (is_last_selected())
{
ev.iptkey = IPT_UI_CANCEL;
stack_pop();
}
stop = true;
break;
if (is_last_selected())
// caught scroll event
case UI_EVENT_MOUSE_WHEEL:
if (hover >= 0 && hover < item.size() - skip_main_items - 1)
{
if (local_menu_event.zdelta > 0)
{
ev.iptkey = IPT_UI_CANCEL;
stack_pop();
}
stop = true;
break;
// caught scroll event
case UI_EVENT_MOUSE_WHEEL:
if (hover >= 0 && hover < item.size() - skip_main_items - 1)
{
if (local_menu_event.zdelta > 0)
{
if (selected >= visible_items || is_first_selected() || m_ui_error)
break;
selected -= local_menu_event.num_lines;
if (selected < top_line + (top_line != 0))
top_line -= local_menu_event.num_lines;
}
else
{
if (selected >= visible_items - 1 || m_ui_error)
break;
selected += local_menu_event.num_lines;
if (selected > visible_items - 1)
selected = visible_items - 1;
if (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_CHAR:
if (exclusive_input_pressed(ev.iptkey, IPT_UI_CONFIGURE, 0))
{
ev.iptkey = IPT_UI_CONFIGURE;
stop = true;
if (selected >= visible_items || is_first_selected() || m_ui_error)
break;
selected -= local_menu_event.num_lines;
if (selected < top_line + (top_line != 0))
top_line -= local_menu_event.num_lines;
}
else
{
ev.iptkey = IPT_SPECIAL;
ev.unichar = local_menu_event.ch;
stop = true;
if (selected >= visible_items - 1 || m_ui_error)
break;
selected += local_menu_event.num_lines;
if (selected > visible_items - 1)
selected = visible_items - 1;
if (selected >= top_line + m_visible_items + (top_line != 0))
top_line += local_menu_event.num_lines;
}
break;
}
else if (hover == HOVER_INFO_TEXT)
{
if (local_menu_event.zdelta > 0)
m_topline_datsview -= local_menu_event.num_lines;
else
m_topline_datsview += local_menu_event.num_lines;
}
break;
case UI_EVENT_MOUSE_RDOWN:
if (hover >= 0 && hover < item.size() - skip_main_items - 1)
{
selected = 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;
}
break;
// translate CHAR events into specials
case UI_EVENT_CHAR:
if (exclusive_input_pressed(ev.iptkey, IPT_UI_CONFIGURE, 0))
{
ev.iptkey = IPT_UI_CONFIGURE;
stop = true;
}
else
{
ev.iptkey = IPT_SPECIAL;
ev.unichar = local_menu_event.ch;
stop = true;
}
break;
// ignore everything else
default:
break;
case UI_EVENT_MOUSE_RDOWN:
if (hover >= 0 && hover < item.size() - skip_main_items - 1)
{
selected = 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;
}
break;
// ignore everything else
default:
break;
}
}
}
@ -2060,16 +2072,20 @@ std::string menu_select_launch::arts_render_common(float origx1, float origy1, f
if (bgcolor != UI_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(43, 43, 43), hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
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.c_str(), origx1, origy1, origx2 - origx1,
snaptext.c_str(), origx1, origy1 + UI_BOX_TB_BORDER, origx2 - origx1,
ui::text_layout::CENTER, ui::text_layout::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor,
nullptr, nullptr, tmp_size);
draw_common_arrow(origx1, origy1, origx2, origy2, ui_globals::curimage_view, FIRST_VIEW, LAST_VIEW, title_size);
draw_common_arrow(origx1, origy1 + UI_BOX_TB_BORDER, origx2, origy2, ui_globals::curimage_view, FIRST_VIEW, LAST_VIEW, title_size);
return searchstr;
}
@ -2101,7 +2117,7 @@ void menu_select_launch::arts_render_images(bitmap_argb32 *tmp_bitmap, float ori
if (tmp_bitmap->valid())
{
float panel_width = origx2 - origx1 - 0.02f;
float panel_height = origy2 - origy1 - 0.02f - (2.0f * UI_BOX_TB_BORDER) - (2.0f * line_height);
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();
@ -2182,7 +2198,7 @@ void menu_select_launch::draw_snapx(float origx1, float origy1, float origx2, fl
float const line_height = ui().get_line_height();
float const x1 = origx1 + 0.01f;
float const x2 = origx2 - 0.01f;
float const y1 = origy1 + UI_BOX_TB_BORDER + line_height;
float const y1 = origy1 + (2.0f * UI_BOX_TB_BORDER) + line_height;
float const y2 = origy2 - UI_BOX_TB_BORDER - line_height;
// apply texture
@ -2325,6 +2341,7 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2,
else
return;
origy1 += UI_BOX_TB_BORDER;
float gutter_width = 0.4f * line_height * machine().render().ui_aspect() * 1.3f;
float ud_arrow_width = line_height * machine().render().ui_aspect();
float oy1 = origy1 + line_height;
@ -2409,7 +2426,10 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2,
if (m_topline_datsview + r_visible_lines >= m_total_lines)
m_topline_datsview = m_total_lines - r_visible_lines;
sc = origx2 - origx1 - (2.0f * UI_BOX_LR_BORDER);
if (mouse_in_rect(origx1 + gutter_width, oy1, origx2 - gutter_width, origy2))
hover = HOVER_INFO_TEXT;
sc = origx2 - origx1 - (2.0f * gutter_width);
for (int r = 0; r < r_visible_lines; ++r)
{
int itemline = r + m_topline_datsview;

View File

@ -117,11 +117,12 @@ protected:
template <typename T> bool select_bios(T const &driver, bool inlist);
bool select_part(software_info const &info, ui_software_info const &ui_info);
int l_hover, l_sw_hover;
int visible_items;
void *m_prev_selected;
int m_total_lines;
int m_topline_datsview; // right box top line
int l_hover, l_sw_hover;
int visible_items;
void *m_prev_selected;
int m_total_lines;
int m_topline_datsview; // right box top line
std::string m_search;
static char const *const s_info_titles[];
@ -225,6 +226,9 @@ private:
// handle mouse
virtual void handle_events(uint32_t flags, event &ev) override;
// live search active?
virtual bool menu_has_search_active() override { return !m_search.empty(); }
// draw game list
virtual void draw(uint32_t flags) override;

View File

@ -132,9 +132,9 @@ void menu_select_software::handle()
{
// reset the error on any future event
}
else if (menu_event->iptkey == IPT_UI_SELECT)
else switch (menu_event->iptkey)
{
// handle selections
case IPT_UI_SELECT:
if (get_focus() == focused_menu::LEFT)
{
l_sw_hover = highlight;
@ -145,10 +145,9 @@ void menu_select_software::handle()
{
inkey_select(menu_event);
}
}
else if (menu_event->iptkey == IPT_UI_LEFT)
{
// handle UI_LEFT
break;
case IPT_UI_LEFT:
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view > FIRST_VIEW)
{
// Images
@ -162,10 +161,9 @@ void menu_select_software::handle()
ui_globals::cur_sw_dats_view--;
m_topline_datsview = 0;
}
}
else if (menu_event->iptkey == IPT_UI_RIGHT)
{
// handle UI_RIGHT
break;
case IPT_UI_RIGHT:
if (ui_globals::rpanel == RP_IMAGES && ui_globals::curimage_view < LAST_VIEW)
{
// Images
@ -179,80 +177,78 @@ void menu_select_software::handle()
ui_globals::cur_sw_dats_view++;
m_topline_datsview = 0;
}
}
else if (menu_event->iptkey == IPT_UI_LEFT_PANEL)
{
// handle UI_LEFT_PANEL
ui_globals::rpanel = RP_IMAGES;
}
else if (menu_event->iptkey == IPT_UI_RIGHT_PANEL)
{
// handle UI_RIGHT_PANEL
ui_globals::rpanel = RP_INFOS;
}
else if (menu_event->iptkey == IPT_UI_UP_FILTER && highlight > software_filter::FIRST)
{
// handle UI_UP_FILTER
highlight--;
}
else if (menu_event->iptkey == IPT_UI_DOWN_FILTER && highlight < software_filter::LAST)
{
// handle UI_DOWN_FILTER
highlight++;
}
else if (menu_event->iptkey == IPT_OTHER)
{
break;
case IPT_UI_UP:
if ((get_focus() == focused_menu::LEFT) && (software_filter::FIRST < highlight))
--highlight;
break;
case IPT_UI_DOWN:
if ((get_focus() == focused_menu::LEFT) && (software_filter::LAST > highlight))
++highlight;
break;
case IPT_UI_HOME:
if (get_focus() == focused_menu::LEFT)
highlight = software_filter::FIRST;
break;
case IPT_UI_END:
if (get_focus() == focused_menu::LEFT)
highlight = software_filter::LAST;
break;
case IPT_OTHER:
// this is generated when something in the left box is clicked
highlight = l_sw_hover;
check_filter = true;
m_prev_selected = nullptr;
}
else if (menu_event->iptkey == IPT_UI_CONFIGURE)
{
break;
case IPT_UI_CONFIGURE:
inkey_navigation();
}
else if (menu_event->itemref)
{
if (menu_event->iptkey == IPT_UI_DATS)
{
// handle UI_DATS
ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref;
break;
if (ui_swinfo->startempty == 1 && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", ui_swinfo->driver->name, true))
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo->driver);
else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(ui_swinfo->shortname).append(1, ',').append(ui_swinfo->listname).c_str()) || !ui_swinfo->usage.empty())
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo);
}
else if (menu_event->iptkey == IPT_UI_CANCEL && !m_search.empty())
default:
if (menu_event->itemref)
{
// escape pressed with non-empty text clears the text
m_search.clear();
reset(reset_options::SELECT_FIRST);
}
else if (menu_event->iptkey == IPT_UI_FAVORITES)
{
// handle UI_FAVORITES
ui_software_info *swinfo = (ui_software_info *)menu_event->itemref;
if ((uintptr_t)swinfo > 2)
if (menu_event->iptkey == IPT_UI_DATS)
{
favorite_manager &mfav = mame_machine_manager::instance()->favorite();
if (!mfav.isgame_favorite(*swinfo))
{
mfav.add_favorite_game(*swinfo);
machine().popmessage(_("%s\n added to favorites list."), swinfo->longname.c_str());
}
// handle UI_DATS
ui_software_info *ui_swinfo = (ui_software_info *)menu_event->itemref;
else
if (ui_swinfo->startempty == 1 && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", ui_swinfo->driver->name, true))
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo->driver);
else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(ui_swinfo->shortname).append(1, ',').append(ui_swinfo->listname).c_str()) || !ui_swinfo->usage.empty())
menu::stack_push<menu_dats_view>(ui(), container(), ui_swinfo);
}
else if (menu_event->iptkey == IPT_UI_FAVORITES)
{
// handle UI_FAVORITES
ui_software_info *swinfo = (ui_software_info *)menu_event->itemref;
if ((uintptr_t)swinfo > 2)
{
machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname.c_str());
mfav.remove_favorite_game();
favorite_manager &mfav = mame_machine_manager::instance()->favorite();
if (!mfav.isgame_favorite(*swinfo))
{
mfav.add_favorite_game(*swinfo);
machine().popmessage(_("%s\n added to favorites list."), swinfo->longname.c_str());
}
else
{
machine().popmessage(_("%s\n removed from favorites list."), swinfo->longname.c_str());
mfav.remove_favorite_game();
}
}
}
}
else if (menu_event->iptkey == IPT_SPECIAL)
{
// typed characters append to the buffer
inkey_special(menu_event);
else if (menu_event->iptkey == IPT_SPECIAL)
{
// typed characters append to the buffer
inkey_special(menu_event);
}
}
}
}

View File

@ -26,15 +26,11 @@ public:
menu_select_software(mame_ui_manager &mui, render_container &container, const game_driver *driver);
virtual ~menu_select_software() override;
protected:
virtual bool menu_has_search_active() override { return !m_search.empty(); }
private:
enum { VISIBLE_GAMES_IN_SEARCH = 200 };
typedef std::map<software_filter::type, software_filter::ptr> filter_map;
std::string m_search;
const game_driver *m_driver;
bool m_has_empty_start;
s_filter m_filter_data;

View File

@ -232,8 +232,7 @@ void simple_menu_select_game::populate(float &customtop, float &custombottom)
int curitem;
for (curitem = matchcount = 0; m_driverlist[curitem] != nullptr && matchcount < VISIBLE_GAMES_IN_LIST; curitem++)
if (!(m_driverlist[curitem]->flags & machine_flags::NO_STANDALONE))
matchcount++;
matchcount++;
// if nothing there, add a single multiline item and return
if (matchcount == 0)

View File

@ -276,7 +276,8 @@ enum
HOVER_SW_FILTER_FIRST,
HOVER_SW_FILTER_LAST = (HOVER_SW_FILTER_FIRST) + ui::software_filter::COUNT,
HOVER_RP_FIRST,
HOVER_RP_LAST = (HOVER_RP_FIRST) + 1 + RP_LAST
HOVER_RP_LAST = (HOVER_RP_FIRST) + 1 + RP_LAST,
HOVER_INFO_TEXT
};
// FIXME: this stuff shouldn't all be globals