mirror of
https://github.com/holub/mame
synced 2025-06-07 05:13:46 +03:00

Removed unnecessary icons from the toolbar (performed the same actions of entries already in the menu). Proper handling the export of the list. Updated the .po files.
2827 lines
92 KiB
C++
2827 lines
92 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Maurizio Petrarota
|
|
/*********************************************************************
|
|
|
|
ui/menu.cpp
|
|
|
|
Internal MAME menus for the user interface.
|
|
|
|
*********************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "rendutil.h"
|
|
#include "cheat.h"
|
|
#include "uiinput.h"
|
|
#include "ui/ui.h"
|
|
#include "ui/menu.h"
|
|
#include "ui/mainmenu.h"
|
|
#include "ui/utils.h"
|
|
#include "ui/defimg.h"
|
|
#include "ui/starimg.h"
|
|
#include "ui/optsmenu.h"
|
|
#include "ui/datfile.h"
|
|
#include "rendfont.h"
|
|
#include "ui/custmenu.h"
|
|
#include "ui/icorender.h"
|
|
#include "ui/toolbar.h"
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
#define UI_MENU_POOL_SIZE 65536
|
|
#define MAX_ICONS_RENDER 40
|
|
|
|
struct ui_arts_info
|
|
{
|
|
const char *title, *path, *addpath;
|
|
};
|
|
|
|
static const ui_arts_info arts_info[] =
|
|
{
|
|
{ "Snapshots", OPTION_SNAPSHOT_DIRECTORY, "snap" },
|
|
{ "Cabinets", OPTION_CABINETS_PATH, "cabinets;cabdevs" },
|
|
{ "Control Panels", OPTION_CPANELS_PATH, "cpanel" },
|
|
{ "PCBs", OPTION_PCBS_PATH, "pcb" },
|
|
{ "Flyers", OPTION_FLYERS_PATH, "flyers" },
|
|
{ "Titles", OPTION_TITLES_PATH, "titles" },
|
|
{ "Ends", OPTION_ENDS_PATH, "ends" },
|
|
{ "Artwork Preview", OPTION_ARTPREV_PATH, "artwork preview" },
|
|
{ "Bosses", OPTION_BOSSES_PATH, "bosses" },
|
|
{ "Logos", OPTION_LOGOS_PATH, "logo" },
|
|
{ "Versus", OPTION_VERSUS_PATH, "versus" },
|
|
{ "Game Over", OPTION_GAMEOVER_PATH, "gameover" },
|
|
{ "HowTo", OPTION_HOWTO_PATH, "howto" },
|
|
{ "Scores", OPTION_SCORES_PATH, "scores" },
|
|
{ "Select", OPTION_SELECT_PATH, "select" },
|
|
{ "Marquees", OPTION_MARQUEES_PATH, "marquees" },
|
|
{ nullptr }
|
|
};
|
|
|
|
static const char *hover_msg[] = {
|
|
"Add or remove favorites",
|
|
"Export displayed list to file",
|
|
"Show DATs view",
|
|
};
|
|
|
|
/***************************************************************************
|
|
GLOBAL VARIABLES
|
|
***************************************************************************/
|
|
|
|
ui_menu *ui_menu::menu_stack;
|
|
ui_menu *ui_menu::menu_free;
|
|
std::unique_ptr<bitmap_rgb32> ui_menu::hilight_bitmap;
|
|
render_texture *ui_menu::hilight_texture;
|
|
render_texture *ui_menu::arrow_texture;
|
|
render_texture *ui_menu::snapx_texture;
|
|
render_texture *ui_menu::hilight_main_texture;
|
|
render_texture *ui_menu::bgrnd_texture;
|
|
render_texture *ui_menu::star_texture;
|
|
render_texture *ui_menu::toolbar_texture[UI_TOOLBAR_BUTTONS];
|
|
render_texture *ui_menu::sw_toolbar_texture[UI_TOOLBAR_BUTTONS];
|
|
render_texture *ui_menu::icons_texture[MAX_ICONS_RENDER];
|
|
std::unique_ptr<bitmap_argb32> ui_menu::snapx_bitmap;
|
|
std::unique_ptr<bitmap_argb32> ui_menu::no_avail_bitmap;
|
|
std::unique_ptr<bitmap_argb32> ui_menu::star_bitmap;
|
|
std::unique_ptr<bitmap_argb32> ui_menu::bgrnd_bitmap;
|
|
bitmap_argb32 *ui_menu::icons_bitmap[MAX_ICONS_RENDER];
|
|
std::unique_ptr<bitmap_rgb32> ui_menu::hilight_main_bitmap;
|
|
bitmap_argb32 *ui_menu::toolbar_bitmap[UI_TOOLBAR_BUTTONS];
|
|
bitmap_argb32 *ui_menu::sw_toolbar_bitmap[UI_TOOLBAR_BUTTONS];
|
|
|
|
/***************************************************************************
|
|
INLINE FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// is_selectable - return TRUE if the given
|
|
// item is selectable
|
|
//-------------------------------------------------
|
|
|
|
inline bool ui_menu_item::is_selectable() const
|
|
{
|
|
return ((flags & (MENU_FLAG_MULTILINE | MENU_FLAG_DISABLE)) == 0 && strcmp(text, MENU_SEPARATOR_ITEM) != 0);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// exclusive_input_pressed - return TRUE if the
|
|
// given key is pressed and we haven't already
|
|
// reported a key
|
|
//-------------------------------------------------
|
|
|
|
inline bool ui_menu::exclusive_input_pressed(int key, int repeat)
|
|
{
|
|
if (menu_event.iptkey == IPT_INVALID && machine().ui_input().pressed_repeat(key, repeat))
|
|
{
|
|
menu_event.iptkey = key;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CORE SYSTEM MANAGEMENT
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// init - initialize the menu system
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::init(running_machine &machine)
|
|
{
|
|
// initialize the menu stack
|
|
ui_menu::stack_reset(machine);
|
|
|
|
// create a texture for hilighting items
|
|
hilight_bitmap = std::make_unique<bitmap_rgb32>(256, 1);
|
|
for (int x = 0; x < 256; x++)
|
|
{
|
|
int alpha = 0xff;
|
|
if (x < 25) alpha = 0xff * x / 25;
|
|
if (x > 256 - 25) alpha = 0xff * (255 - x) / 25;
|
|
hilight_bitmap->pix32(0, x) = rgb_t(alpha,0xff,0xff,0xff);
|
|
}
|
|
hilight_texture = machine.render().texture_alloc();
|
|
hilight_texture->set_bitmap(*hilight_bitmap, hilight_bitmap->cliprect(), TEXFORMAT_ARGB32);
|
|
|
|
// create a texture for arrow icons
|
|
arrow_texture = machine.render().texture_alloc(render_triangle);
|
|
|
|
// initialize ui
|
|
init_ui(machine);
|
|
|
|
// add an exit callback to free memory
|
|
machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(ui_menu::exit), &machine));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// exit - clean up after ourselves
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::exit(running_machine &machine)
|
|
{
|
|
// free menus
|
|
ui_menu::stack_reset(machine);
|
|
ui_menu::clear_free_list(machine);
|
|
|
|
// free textures
|
|
render_manager &mre = machine.render();
|
|
mre.texture_free(hilight_texture);
|
|
mre.texture_free(arrow_texture);
|
|
mre.texture_free(snapx_texture);
|
|
mre.texture_free(hilight_main_texture);
|
|
mre.texture_free(bgrnd_texture);
|
|
mre.texture_free(star_texture);
|
|
|
|
for (auto & elem : icons_texture)
|
|
mre.texture_free(elem);
|
|
|
|
for (int i = 0; i < UI_TOOLBAR_BUTTONS; i++)
|
|
{
|
|
mre.texture_free(sw_toolbar_texture[i]);
|
|
mre.texture_free(toolbar_texture[i]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CORE MENU MANAGEMENT
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// ui_menu - menu constructor
|
|
//-------------------------------------------------
|
|
|
|
ui_menu::ui_menu(running_machine &machine, render_container *_container) : m_machine(machine)
|
|
{
|
|
m_special_main_menu = false;
|
|
container = _container;
|
|
|
|
reset(UI_MENU_RESET_SELECT_FIRST);
|
|
|
|
top_line = 0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~ui_menu - menu destructor
|
|
//-------------------------------------------------
|
|
|
|
ui_menu::~ui_menu()
|
|
{
|
|
// free the pools
|
|
while (pool)
|
|
{
|
|
ui_menu_pool *ppool = pool;
|
|
pool = pool->next;
|
|
global_free(ppool);
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// reset - free all items in the menu,
|
|
// and all memory allocated from the memory pool
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::reset(ui_menu_reset_options options)
|
|
{
|
|
// based on the reset option, set the reset info
|
|
resetpos = 0;
|
|
resetref = nullptr;
|
|
if (options == UI_MENU_RESET_REMEMBER_POSITION)
|
|
resetpos = selected;
|
|
else if (options == UI_MENU_RESET_REMEMBER_REF)
|
|
resetref = item[selected].ref;
|
|
|
|
// reset all the pools and the item.size() back to 0
|
|
for (ui_menu_pool *ppool = pool; ppool != nullptr; ppool = ppool->next)
|
|
ppool->top = (UINT8 *)(ppool + 1);
|
|
item.clear();
|
|
visitems = 0;
|
|
selected = 0;
|
|
std::string backtext;
|
|
strprintf(backtext, "Return to Machine");
|
|
|
|
// add an item to return
|
|
if (parent == nullptr)
|
|
item_append(backtext.c_str(), nullptr, 0, nullptr);
|
|
else if (parent->is_special_main_menu())
|
|
{
|
|
if (strcmp(machine().options().ui(), "simple") == 0)
|
|
item_append("Exit", nullptr, 0, nullptr);
|
|
else
|
|
item_append("Exit", nullptr, MENU_FLAG_UI | MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW, nullptr);
|
|
}
|
|
else
|
|
{
|
|
if (strcmp(machine().options().ui(), "simple") != 0 && ui_menu::stack_has_special_main_menu())
|
|
item_append("Return to Previous Menu", nullptr, MENU_FLAG_UI | MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW, nullptr);
|
|
else
|
|
item_append("Return to Previous Menu", nullptr, 0, nullptr);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// is_special_main_menu - returns whether the
|
|
// menu has special needs
|
|
//-------------------------------------------------
|
|
|
|
bool ui_menu::is_special_main_menu() const
|
|
{
|
|
return m_special_main_menu;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_special_main_menu - set whether the
|
|
// menu has special needs
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::set_special_main_menu(bool special)
|
|
{
|
|
m_special_main_menu = special;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// item_append - append a new item to the
|
|
// end of the menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::item_append(const char *text, const char *subtext, UINT32 flags, void *ref)
|
|
{
|
|
// only allow multiline as the first item
|
|
if ((flags & MENU_FLAG_MULTILINE) != 0)
|
|
assert(item.size() == 1);
|
|
|
|
// only allow a single multi-line item
|
|
else if (item.size() >= 2)
|
|
assert((item[0].flags & MENU_FLAG_MULTILINE) == 0);
|
|
|
|
// allocate a new item and populate it
|
|
ui_menu_item pitem;
|
|
pitem.text = (text != nullptr) ? pool_strdup(text) : nullptr;
|
|
pitem.subtext = (subtext != nullptr) ? pool_strdup(subtext) : nullptr;
|
|
pitem.flags = flags;
|
|
pitem.ref = ref;
|
|
|
|
// append to array
|
|
int index = item.size();
|
|
if (!item.empty())
|
|
{
|
|
item.insert(item.end() - 1, pitem);
|
|
--index;
|
|
}
|
|
else
|
|
item.push_back(pitem);
|
|
|
|
// update the selection if we need to
|
|
if (resetpos == index || (resetref != nullptr && resetref == ref))
|
|
selected = index;
|
|
if (resetpos == item.size() - 1)
|
|
selected = item.size() - 1;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// process - process a menu, drawing it
|
|
// and returning any interesting events
|
|
//-------------------------------------------------
|
|
|
|
const ui_menu_event *ui_menu::process(UINT32 flags)
|
|
{
|
|
// reset the menu_event
|
|
menu_event.iptkey = IPT_INVALID;
|
|
|
|
// first make sure our selection is valid
|
|
// if (!(flags & UI_MENU_PROCESS_NOINPUT))
|
|
validate_selection(1);
|
|
|
|
// draw the menu
|
|
if (item.size() > 1 && (item[0].flags & MENU_FLAG_MULTILINE) != 0)
|
|
draw_text_box();
|
|
else if ((item[0].flags & MENU_FLAG_UI ) != 0 || (item[0].flags & MENU_FLAG_UI_SWLIST ) != 0)
|
|
draw_select_game(flags & UI_MENU_PROCESS_NOINPUT);
|
|
else if ((item[0].flags & MENU_FLAG_UI_PALETTE ) != 0)
|
|
draw_palette_menu();
|
|
else if ((item[0].flags & MENU_FLAG_UI_DATS) != 0)
|
|
draw_dats_menu();
|
|
else
|
|
draw(flags & UI_MENU_PROCESS_CUSTOM_ONLY, flags & UI_MENU_PROCESS_NOIMAGE, flags & UI_MENU_PROCESS_NOINPUT);
|
|
|
|
// process input
|
|
if (!(flags & UI_MENU_PROCESS_NOKEYS) && !(flags & UI_MENU_PROCESS_NOINPUT))
|
|
{
|
|
// read events
|
|
if ((item[0].flags & MENU_FLAG_UI ) != 0 || (item[0].flags & MENU_FLAG_UI_SWLIST ) != 0)
|
|
handle_main_events(flags);
|
|
else
|
|
handle_events(flags);
|
|
|
|
// handle the keys if we don't already have an menu_event
|
|
if (menu_event.iptkey == IPT_INVALID)
|
|
{
|
|
if ((item[0].flags & MENU_FLAG_UI ) != 0 || (item[0].flags & MENU_FLAG_UI_SWLIST ) != 0)
|
|
handle_main_keys(flags);
|
|
else
|
|
handle_keys(flags);
|
|
}
|
|
}
|
|
|
|
// update the selected item in the menu_event
|
|
if (menu_event.iptkey != IPT_INVALID && selected >= 0 && selected < item.size())
|
|
{
|
|
menu_event.itemref = item[selected].ref;
|
|
return &menu_event;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// m_pool_alloc - allocate temporary memory
|
|
// from the menu's memory pool
|
|
//-------------------------------------------------
|
|
|
|
void *ui_menu::m_pool_alloc(size_t size)
|
|
{
|
|
ui_menu_pool *ppool;
|
|
|
|
assert(size < UI_MENU_POOL_SIZE);
|
|
|
|
// find a pool with enough room
|
|
for (ppool = 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
|
|
ppool = (ui_menu_pool *)global_alloc_array_clear<UINT8>(sizeof(*ppool) + UI_MENU_POOL_SIZE);
|
|
|
|
// wire it up
|
|
ppool->next = pool;
|
|
pool = ppool;
|
|
ppool->top = (UINT8 *)(ppool + 1);
|
|
ppool->end = ppool->top + UI_MENU_POOL_SIZE;
|
|
return m_pool_alloc(size);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// pool_strdup - make a temporary string
|
|
// copy in the menu's memory pool
|
|
//-------------------------------------------------
|
|
|
|
const char *ui_menu::pool_strdup(const char *string)
|
|
{
|
|
return strcpy((char *)m_pool_alloc(strlen(string) + 1), string);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// get_selection - retrieves the index
|
|
// of the currently selected menu item
|
|
//-------------------------------------------------
|
|
|
|
void *ui_menu::get_selection()
|
|
{
|
|
return (selected >= 0 && selected < item.size()) ? item[selected].ref : nullptr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// set_selection - changes the index
|
|
// of the currently selected menu item
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::set_selection(void *selected_itemref)
|
|
{
|
|
selected = -1;
|
|
for (int itemnum = 0; itemnum < item.size(); itemnum++)
|
|
if (item[itemnum].ref == selected_itemref)
|
|
{
|
|
selected = itemnum;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
INTERNAL MENU PROCESSING
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// draw - draw a menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw(bool customonly, bool noimage, bool noinput)
|
|
{
|
|
// first draw the FPS counter
|
|
if (machine().ui().show_fps_counter())
|
|
{
|
|
machine().ui().draw_text_full(container, machine().video().speed_text().c_str(), 0.0f, 0.0f, 1.0f,
|
|
JUSTIFY_RIGHT, WRAP_WORD, DRAW_OPAQUE, ARGB_WHITE, ARGB_BLACK, nullptr, nullptr);
|
|
}
|
|
|
|
float line_height = machine().ui().get_line_height();
|
|
float lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect();
|
|
float ud_arrow_width = line_height * machine().render().ui_aspect();
|
|
float gutter_width = lr_arrow_width * 1.3f;
|
|
|
|
bool selected_subitem_too_big = false;
|
|
int itemnum, linenum;
|
|
bool mouse_hit, mouse_button;
|
|
float mouse_x = -1, mouse_y = -1;
|
|
|
|
if (machine().ui().options().use_background_image() && &machine().system() == &GAME_NAME(___empty) && bgrnd_bitmap->valid() && !noimage)
|
|
container->add_quad(0.0f, 0.0f, 1.0f, 1.0f, ARGB_WHITE, bgrnd_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
|
|
// compute the width and height of the full menu
|
|
float visible_width = 0;
|
|
float visible_main_menu_height = 0;
|
|
for (itemnum = 0; itemnum < item.size(); itemnum++)
|
|
{
|
|
const ui_menu_item &pitem = item[itemnum];
|
|
float total_width;
|
|
|
|
// compute width of left hand side
|
|
total_width = gutter_width + machine().ui().get_string_width(pitem.text) + gutter_width;
|
|
|
|
// add in width of right hand side
|
|
if (pitem.subtext)
|
|
total_width += 2.0f * gutter_width + machine().ui().get_string_width(pitem.subtext);
|
|
|
|
// track the maximum
|
|
if (total_width > visible_width)
|
|
visible_width = total_width;
|
|
|
|
// track the height as well
|
|
visible_main_menu_height += line_height;
|
|
}
|
|
|
|
// account for extra space at the top and bottom
|
|
float visible_extra_menu_height = customtop + custombottom;
|
|
|
|
// add a little bit of slop for rounding
|
|
visible_width += 0.01f;
|
|
visible_main_menu_height += 0.01f;
|
|
|
|
// if we are too wide or too tall, clamp it down
|
|
if (visible_width + 2.0f * UI_BOX_LR_BORDER > 1.0f)
|
|
visible_width = 1.0f - 2.0f * UI_BOX_LR_BORDER;
|
|
|
|
// if the menu and extra menu won't fit, take away part of the regular menu, it will scroll
|
|
if (visible_main_menu_height + visible_extra_menu_height + 2.0f * UI_BOX_TB_BORDER > 1.0f)
|
|
visible_main_menu_height = 1.0f - 2.0f * UI_BOX_TB_BORDER - visible_extra_menu_height;
|
|
|
|
visible_lines = floor(visible_main_menu_height / line_height);
|
|
visible_main_menu_height = (float)visible_lines * line_height;
|
|
|
|
// compute top/left of inner menu area by centering
|
|
float visible_left = (1.0f - visible_width) * 0.5f;
|
|
float visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f;
|
|
|
|
// if the menu is at the bottom of the extra, adjust
|
|
visible_top += customtop;
|
|
|
|
// first add us a box
|
|
float x1 = visible_left - UI_BOX_LR_BORDER;
|
|
float y1 = visible_top - UI_BOX_TB_BORDER;
|
|
float x2 = visible_left + visible_width + UI_BOX_LR_BORDER;
|
|
float y2 = visible_top + visible_main_menu_height + UI_BOX_TB_BORDER;
|
|
if (!customonly)
|
|
machine().ui().draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
|
|
// determine the first visible line based on the current selection
|
|
if (selected > top_line + visible_lines)
|
|
top_line = selected - (visible_lines / 2);
|
|
if (top_line < 0 || selected == 0)
|
|
top_line = 0;
|
|
if (top_line + visible_lines >= item.size())
|
|
top_line = item.size() - visible_lines;
|
|
|
|
// determine effective positions taking into account the hilighting arrows
|
|
float effective_width = visible_width - 2.0f * gutter_width;
|
|
float effective_left = visible_left + gutter_width;
|
|
|
|
// locate mouse
|
|
mouse_hit = false;
|
|
mouse_button = false;
|
|
if (!customonly && !noinput)
|
|
{
|
|
INT32 mouse_target_x, mouse_target_y;
|
|
render_target *mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
|
|
if (mouse_target != nullptr)
|
|
if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, *container, mouse_x, mouse_y))
|
|
mouse_hit = true;
|
|
}
|
|
|
|
// loop over visible lines
|
|
hover = item.size() + 1;
|
|
float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
|
|
float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
|
|
if (!customonly)
|
|
for (linenum = 0; linenum < visible_lines; linenum++)
|
|
{
|
|
float line_y = visible_top + (float)linenum * line_height;
|
|
itemnum = top_line + linenum;
|
|
const ui_menu_item &pitem = item[itemnum];
|
|
const char *itemtext = pitem.text;
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
rgb_t fgcolor2 = UI_SUBITEM_COLOR;
|
|
rgb_t fgcolor3 = UI_CLONE_COLOR;
|
|
float line_y0 = line_y;
|
|
float line_y1 = line_y + line_height;
|
|
|
|
// set the hover if this is our item
|
|
if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y && pitem.is_selectable())
|
|
hover = itemnum;
|
|
|
|
// if we're selected, draw with a different background
|
|
if (itemnum == selected)
|
|
{
|
|
fgcolor = UI_SELECTED_COLOR;
|
|
bgcolor = UI_SELECTED_BG_COLOR;
|
|
fgcolor2 = UI_SELECTED_COLOR;
|
|
fgcolor3 = UI_SELECTED_COLOR;
|
|
}
|
|
|
|
// else if the mouse is over this item, draw with a different background
|
|
else if (itemnum == hover)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
fgcolor2 = UI_MOUSEOVER_COLOR;
|
|
fgcolor3 = UI_MOUSEOVER_COLOR;
|
|
}
|
|
|
|
// if we have some background hilighting to do, add a quad behind everything else
|
|
if (bgcolor != UI_TEXT_BG_COLOR)
|
|
highlight(container, line_x0, line_y0, line_x1, line_y1, bgcolor);
|
|
|
|
// if we're on the top line, display the up arrow
|
|
if (linenum == 0 && top_line != 0)
|
|
{
|
|
draw_arrow(container,
|
|
0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
|
|
line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
|
|
line_y + 0.75f * line_height,
|
|
fgcolor,
|
|
ROT0);
|
|
if (hover == itemnum)
|
|
hover = HOVER_ARROW_UP;
|
|
}
|
|
|
|
// if we're on the bottom line, display the down arrow
|
|
else if (linenum == visible_lines - 1 && itemnum != item.size() - 1)
|
|
{
|
|
draw_arrow(container,
|
|
0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
|
|
line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
|
|
line_y + 0.75f * line_height,
|
|
fgcolor,
|
|
ROT0 ^ ORIENTATION_FLIP_Y);
|
|
if (hover == itemnum)
|
|
hover = HOVER_ARROW_DOWN;
|
|
}
|
|
|
|
// if we're just a divider, draw a line
|
|
else if (strcmp(itemtext, MENU_SEPARATOR_ITEM) == 0)
|
|
container->add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
|
|
// if we don't have a subitem, just draw the string centered
|
|
else if (pitem.subtext == nullptr)
|
|
machine().ui().draw_text_full(container, itemtext, effective_left, line_y, effective_width,
|
|
JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
|
|
// otherwise, draw the item on the left and the subitem text on the right
|
|
else
|
|
{
|
|
int subitem_invert = pitem.flags & MENU_FLAG_INVERT;
|
|
const char *subitem_text = pitem.subtext;
|
|
float item_width, subitem_width;
|
|
|
|
// draw the left-side text
|
|
machine().ui().draw_text_full(container, itemtext, effective_left, line_y, effective_width,
|
|
JUSTIFY_LEFT, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, &item_width, nullptr);
|
|
|
|
// give 2 spaces worth of padding
|
|
item_width += 2.0f * gutter_width;
|
|
|
|
// if the subitem doesn't fit here, display dots
|
|
if (machine().ui().get_string_width(subitem_text) > effective_width - item_width)
|
|
{
|
|
subitem_text = "...";
|
|
if (itemnum == selected)
|
|
selected_subitem_too_big = true;
|
|
}
|
|
|
|
// customize subitem text color
|
|
if (!core_stricmp(subitem_text, "On"))
|
|
fgcolor2 = rgb_t(0xff,0x00,0xff,0x00);
|
|
|
|
if (!core_stricmp(subitem_text, "Off"))
|
|
fgcolor2 = rgb_t(0xff,0xff,0x00,0x00);
|
|
|
|
if (!core_stricmp(subitem_text, "Auto"))
|
|
fgcolor2 = rgb_t(0xff,0xff,0xff,0x00);
|
|
|
|
// draw the subitem right-justified
|
|
machine().ui().draw_text_full(container, subitem_text, effective_left + item_width, line_y, effective_width - item_width,
|
|
JUSTIFY_RIGHT, WRAP_TRUNCATE, DRAW_NORMAL, subitem_invert ? fgcolor3 : fgcolor2, bgcolor, &subitem_width, nullptr);
|
|
|
|
// apply arrows
|
|
if (itemnum == selected && (pitem.flags & MENU_FLAG_LEFT_ARROW))
|
|
{
|
|
draw_arrow(container,
|
|
effective_left + effective_width - subitem_width - gutter_width,
|
|
line_y + 0.1f * line_height,
|
|
effective_left + effective_width - subitem_width - gutter_width + lr_arrow_width,
|
|
line_y + 0.9f * line_height,
|
|
fgcolor,
|
|
ROT90 ^ ORIENTATION_FLIP_X);
|
|
}
|
|
if (itemnum == selected && (pitem.flags & MENU_FLAG_RIGHT_ARROW))
|
|
{
|
|
draw_arrow(container,
|
|
effective_left + effective_width + gutter_width - lr_arrow_width,
|
|
line_y + 0.1f * line_height,
|
|
effective_left + effective_width + gutter_width,
|
|
line_y + 0.9f * line_height,
|
|
fgcolor,
|
|
ROT90);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the selected subitem is too big, display it in a separate offset box
|
|
if (selected_subitem_too_big)
|
|
{
|
|
const ui_menu_item &pitem = item[selected];
|
|
int subitem_invert = pitem.flags & MENU_FLAG_INVERT;
|
|
linenum = selected - top_line;
|
|
float line_y = visible_top + (float)linenum * line_height;
|
|
float target_width, target_height;
|
|
float target_x, target_y;
|
|
|
|
// compute the multi-line target width/height
|
|
machine().ui().draw_text_full(container, pitem.subtext, 0, 0, visible_width * 0.75f,
|
|
JUSTIFY_RIGHT, WRAP_WORD, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &target_width, &target_height);
|
|
|
|
// determine the target location
|
|
target_x = visible_left + visible_width - target_width - UI_BOX_LR_BORDER;
|
|
target_y = line_y + line_height + UI_BOX_TB_BORDER;
|
|
if (target_y + target_height + UI_BOX_TB_BORDER > visible_main_menu_height)
|
|
target_y = line_y - target_height - UI_BOX_TB_BORDER;
|
|
|
|
// add a box around that
|
|
machine().ui().draw_outlined_box(container, target_x - UI_BOX_LR_BORDER,
|
|
target_y - UI_BOX_TB_BORDER,
|
|
target_x + target_width + UI_BOX_LR_BORDER,
|
|
target_y + target_height + UI_BOX_TB_BORDER,
|
|
subitem_invert ? UI_SELECTED_BG_COLOR : UI_BACKGROUND_COLOR);
|
|
machine().ui().draw_text_full(container, pitem.subtext, target_x, target_y, target_width,
|
|
JUSTIFY_RIGHT, WRAP_WORD, DRAW_NORMAL, UI_SELECTED_COLOR, UI_SELECTED_BG_COLOR, nullptr, nullptr);
|
|
}
|
|
|
|
// if there is something special to add, do it by calling the virtual method
|
|
custom_render((selected >= 0 && selected < item.size()) ? item[selected].ref : nullptr, customtop, custombottom, x1, y1, x2, y2);
|
|
|
|
// return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
|
|
visitems = visible_lines - (top_line != 0) - (top_line + visible_lines != item.size());
|
|
}
|
|
|
|
void ui_menu::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw_text_box - draw a multiline
|
|
// word-wrapped text box with a menu item at the
|
|
// bottom
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_text_box()
|
|
{
|
|
const char *text = item[0].text;
|
|
const char *backtext = item[1].text;
|
|
float line_height = machine().ui().get_line_height();
|
|
float lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect();
|
|
float gutter_width = lr_arrow_width;
|
|
float target_width, target_height, prior_width;
|
|
float target_x, target_y;
|
|
|
|
// compute the multi-line target width/height
|
|
machine().ui().draw_text_full(container, text, 0, 0, 1.0f - 2.0f * UI_BOX_LR_BORDER - 2.0f * gutter_width,
|
|
JUSTIFY_LEFT, WRAP_WORD, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &target_width, &target_height);
|
|
target_height += 2.0f * line_height;
|
|
if (target_height > 1.0f - 2.0f * UI_BOX_TB_BORDER)
|
|
target_height = floorf((1.0f - 2.0f * UI_BOX_TB_BORDER) / line_height) * line_height;
|
|
|
|
// maximum against "return to prior menu" text
|
|
prior_width = machine().ui().get_string_width(backtext) + 2.0f * gutter_width;
|
|
target_width = MAX(target_width, prior_width);
|
|
|
|
// determine the target location
|
|
target_x = 0.5f - 0.5f * target_width;
|
|
target_y = 0.5f - 0.5f * target_height;
|
|
|
|
// make sure we stay on-screen
|
|
if (target_x < UI_BOX_LR_BORDER + gutter_width)
|
|
target_x = UI_BOX_LR_BORDER + gutter_width;
|
|
if (target_x + target_width + gutter_width + UI_BOX_LR_BORDER > 1.0f)
|
|
target_x = 1.0f - UI_BOX_LR_BORDER - gutter_width - target_width;
|
|
if (target_y < UI_BOX_TB_BORDER)
|
|
target_y = UI_BOX_TB_BORDER;
|
|
if (target_y + target_height + UI_BOX_TB_BORDER > 1.0f)
|
|
target_y = 1.0f - UI_BOX_TB_BORDER - target_height;
|
|
|
|
// add a box around that
|
|
machine().ui().draw_outlined_box(container, target_x - UI_BOX_LR_BORDER - gutter_width,
|
|
target_y - UI_BOX_TB_BORDER,
|
|
target_x + target_width + gutter_width + UI_BOX_LR_BORDER,
|
|
target_y + target_height + UI_BOX_TB_BORDER,
|
|
(item[0].flags & MENU_FLAG_REDTEXT) ? UI_RED_COLOR : UI_BACKGROUND_COLOR);
|
|
machine().ui().draw_text_full(container, text, target_x, target_y, target_width,
|
|
JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, nullptr, nullptr);
|
|
|
|
// draw the "return to prior menu" text with a hilight behind it
|
|
highlight(container,
|
|
target_x + 0.5f * UI_LINE_WIDTH,
|
|
target_y + target_height - line_height,
|
|
target_x + target_width - 0.5f * UI_LINE_WIDTH,
|
|
target_y + target_height,
|
|
UI_SELECTED_BG_COLOR);
|
|
machine().ui().draw_text_full(container, backtext, target_x, target_y + target_height - line_height, target_width,
|
|
JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, UI_SELECTED_COLOR, UI_SELECTED_BG_COLOR, nullptr, nullptr);
|
|
|
|
// artificially set the hover to the last item so a double-click exits
|
|
hover = item.size() - 1;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// handle_events - generically handle
|
|
// input events for a menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::handle_events(UINT32 flags)
|
|
{
|
|
bool stop = false;
|
|
ui_event local_menu_event;
|
|
|
|
// loop while we have interesting events
|
|
while (!stop && machine().ui_input().pop_event(&local_menu_event))
|
|
{
|
|
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 ((flags & UI_MENU_PROCESS_ONLYCHAR) == 0)
|
|
{
|
|
if (hover >= 0 && hover < item.size())
|
|
selected = hover;
|
|
else if (hover == HOVER_ARROW_UP)
|
|
{
|
|
if ((flags & MENU_FLAG_UI_DATS) != 0)
|
|
{
|
|
top_line -= visitems - (top_line + visible_lines == item.size() - 1);
|
|
return;
|
|
}
|
|
selected -= visitems;
|
|
if (selected < 0)
|
|
selected = 0;
|
|
top_line -= visitems - (top_line + visible_lines == item.size() - 1);
|
|
}
|
|
else if (hover == HOVER_ARROW_DOWN)
|
|
{
|
|
if ((flags & MENU_FLAG_UI_DATS) != 0)
|
|
{
|
|
top_line += visible_lines - 2;
|
|
return;
|
|
}
|
|
selected += visible_lines - 2 + (selected == 0);
|
|
if (selected > item.size() - 1)
|
|
selected = item.size() - 1;
|
|
top_line += visible_lines - 2;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// if we are hovering over a valid item, fake a UI_SELECT with a double-click
|
|
case UI_EVENT_MOUSE_DOUBLE_CLICK:
|
|
if ((flags & UI_MENU_PROCESS_ONLYCHAR) == 0 && hover >= 0 && hover < item.size())
|
|
{
|
|
selected = hover;
|
|
menu_event.iptkey = IPT_UI_SELECT;
|
|
if (selected == item.size() - 1)
|
|
{
|
|
menu_event.iptkey = IPT_UI_CANCEL;
|
|
ui_menu::stack_pop(machine());
|
|
}
|
|
stop = true;
|
|
}
|
|
break;
|
|
|
|
// caught scroll event
|
|
case UI_EVENT_MOUSE_WHEEL:
|
|
if ((flags & UI_MENU_PROCESS_ONLYCHAR) == 0)
|
|
{
|
|
if (local_menu_event.zdelta > 0)
|
|
{
|
|
if ((flags & MENU_FLAG_UI_DATS) != 0)
|
|
{
|
|
top_line -= local_menu_event.num_lines;
|
|
return;
|
|
}
|
|
selected -= local_menu_event.num_lines;
|
|
validate_selection(-1);
|
|
if (selected < top_line + (top_line != 0))
|
|
top_line -= local_menu_event.num_lines;
|
|
}
|
|
else
|
|
{
|
|
if ((flags & MENU_FLAG_UI_DATS) != 0)
|
|
{
|
|
top_line += local_menu_event.num_lines;
|
|
return;
|
|
}
|
|
selected += local_menu_event.num_lines;
|
|
validate_selection(1);
|
|
if (selected > item.size() - 1)
|
|
selected = item.size() - 1;
|
|
if (selected >= top_line + visitems + (top_line != 0))
|
|
top_line += local_menu_event.num_lines;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// translate CHAR events into specials
|
|
case UI_EVENT_CHAR:
|
|
menu_event.iptkey = IPT_SPECIAL;
|
|
menu_event.unichar = local_menu_event.ch;
|
|
stop = true;
|
|
break;
|
|
|
|
// ignore everything else
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// handle_keys - generically handle
|
|
// keys for a menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::handle_keys(UINT32 flags)
|
|
{
|
|
bool ignorepause = ui_menu::stack_has_special_main_menu();
|
|
int code;
|
|
|
|
// bail if no items
|
|
if (item.empty())
|
|
return;
|
|
|
|
// if we hit select, return TRUE or pop the stack, depending on the item
|
|
if (exclusive_input_pressed(IPT_UI_SELECT, 0))
|
|
{
|
|
if (selected == item.size() - 1)
|
|
{
|
|
menu_event.iptkey = IPT_UI_CANCEL;
|
|
ui_menu::stack_pop(machine());
|
|
}
|
|
return;
|
|
}
|
|
|
|
// bail out
|
|
if ((flags & UI_MENU_PROCESS_ONLYCHAR) != 0)
|
|
return;
|
|
|
|
// hitting cancel also pops the stack
|
|
if (exclusive_input_pressed(IPT_UI_CANCEL, 0))
|
|
{
|
|
if (!menu_has_search_active())
|
|
ui_menu::stack_pop(machine());
|
|
return;
|
|
}
|
|
|
|
// validate the current selection
|
|
validate_selection(1);
|
|
|
|
// swallow left/right keys if they are not appropriate
|
|
bool ignoreleft = ((item[selected].flags & MENU_FLAG_LEFT_ARROW) == 0);
|
|
bool ignoreright = ((item[selected].flags & MENU_FLAG_RIGHT_ARROW) == 0);
|
|
|
|
if ((item[0].flags & MENU_FLAG_UI_DATS) != 0)
|
|
ignoreleft = ignoreright = false;
|
|
|
|
// accept left/right keys as-is with repeat
|
|
if (!ignoreleft && exclusive_input_pressed(IPT_UI_LEFT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0))
|
|
return;
|
|
if (!ignoreright && exclusive_input_pressed(IPT_UI_RIGHT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0))
|
|
return;
|
|
|
|
// up backs up by one item
|
|
if (exclusive_input_pressed(IPT_UI_UP, 6))
|
|
{
|
|
if ((item[0].flags & MENU_FLAG_UI_DATS) != 0)
|
|
{
|
|
top_line--;
|
|
return;
|
|
}
|
|
(selected == 0) ? selected = top_line = item.size() - 1 : --selected;
|
|
validate_selection(-1);
|
|
top_line -= (selected == top_line && top_line != 0);
|
|
}
|
|
|
|
// down advances by one item
|
|
if (exclusive_input_pressed(IPT_UI_DOWN, 6))
|
|
{
|
|
if ((item[0].flags & MENU_FLAG_UI_DATS) != 0)
|
|
{
|
|
top_line++;
|
|
return;
|
|
}
|
|
(selected == item.size() - 1) ? selected = top_line = 0 : ++selected;
|
|
top_line += (selected == top_line + visitems + (top_line != 0));
|
|
}
|
|
|
|
// page up backs up by visitems
|
|
if (exclusive_input_pressed(IPT_UI_PAGE_UP, 6))
|
|
{
|
|
selected -= visitems;
|
|
top_line -= visitems - (top_line + visible_lines == item.size() - 1);
|
|
if (selected < 0)
|
|
selected = 0;
|
|
validate_selection(1);
|
|
}
|
|
|
|
// page down advances by visitems
|
|
if (exclusive_input_pressed(IPT_UI_PAGE_DOWN, 6))
|
|
{
|
|
selected += visible_lines - 2 + (selected == 0);
|
|
top_line += visible_lines - 2;
|
|
|
|
if (selected > item.size() - 1)
|
|
selected = item.size() - 1;
|
|
validate_selection(-1);
|
|
}
|
|
|
|
// home goes to the start
|
|
if (exclusive_input_pressed(IPT_UI_HOME, 0))
|
|
{
|
|
selected = top_line = 0;
|
|
validate_selection(1);
|
|
}
|
|
|
|
// end goes to the last
|
|
if (exclusive_input_pressed(IPT_UI_END, 0))
|
|
{
|
|
selected = top_line = item.size() - 1;
|
|
validate_selection(-1);
|
|
}
|
|
|
|
// pause enables/disables pause
|
|
if (!ignorepause && exclusive_input_pressed(IPT_UI_PAUSE, 0))
|
|
{
|
|
if (machine().paused())
|
|
machine().resume();
|
|
else
|
|
machine().pause();
|
|
}
|
|
|
|
// handle a toggle cheats request
|
|
if (machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0))
|
|
machine().cheat().set_enable(!machine().cheat().enabled());
|
|
|
|
// see if any other UI keys are pressed
|
|
if (menu_event.iptkey == IPT_INVALID)
|
|
for (code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++)
|
|
{
|
|
if (code == IPT_UI_CONFIGURE || (code == IPT_UI_LEFT && ignoreleft) || (code == IPT_UI_RIGHT && ignoreright) || (code == IPT_UI_PAUSE && ignorepause))
|
|
continue;
|
|
if (exclusive_input_pressed(code, 0))
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// validate_selection - validate the
|
|
// current selection and ensure it is on a
|
|
// correct item
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::validate_selection(int scandir)
|
|
{
|
|
// clamp to be in range
|
|
if (selected < 0)
|
|
selected = 0;
|
|
else if (selected >= item.size())
|
|
selected = item.size() - 1;
|
|
|
|
// skip past unselectable items
|
|
while (!item[selected].is_selectable())
|
|
selected = (selected + item.size() + scandir) % item.size();
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
// clear_free_list - clear out anything
|
|
// accumulated in the free list
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::clear_free_list(running_machine &machine)
|
|
{
|
|
while (menu_free != nullptr)
|
|
{
|
|
ui_menu *menu = menu_free;
|
|
menu_free = menu->parent;
|
|
global_free(menu);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
MENU STACK MANAGEMENT
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// ui_menu::stack_reset - reset the menu stack
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::stack_reset(running_machine &machine)
|
|
{
|
|
while (menu_stack != nullptr)
|
|
ui_menu::stack_pop(machine);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// stack_push - push a new menu onto the
|
|
// stack
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::stack_push(ui_menu *menu)
|
|
{
|
|
menu->parent = menu_stack;
|
|
menu_stack = menu;
|
|
menu->reset(UI_MENU_RESET_SELECT_FIRST);
|
|
menu->machine().ui_input().reset();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// stack_pop - pop a menu from the stack
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::stack_pop(running_machine &machine)
|
|
{
|
|
if (menu_stack != nullptr)
|
|
{
|
|
ui_menu *menu = menu_stack;
|
|
menu_stack = menu->parent;
|
|
menu->parent = menu_free;
|
|
menu_free = menu;
|
|
machine.ui_input().reset();
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ui_menu::stack_has_special_main_menu -
|
|
// check in the special main menu is in the stack
|
|
//-------------------------------------------------
|
|
|
|
bool ui_menu::stack_has_special_main_menu()
|
|
{
|
|
ui_menu *menu;
|
|
|
|
for (menu = menu_stack; menu != nullptr; menu = menu->parent)
|
|
if (menu->is_special_main_menu())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void ui_menu::do_handle()
|
|
{
|
|
if(item.size() < 2)
|
|
populate();
|
|
handle();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
UI SYSTEM INTERACTION
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// ui_menu_ui_handler - displays the current menu
|
|
// and calls the menu handler
|
|
//-------------------------------------------------
|
|
|
|
UINT32 ui_menu::ui_handler(running_machine &machine, render_container *container, UINT32 state)
|
|
{
|
|
// if we have no menus stacked up, start with the main menu
|
|
if (menu_stack == nullptr)
|
|
stack_push(global_alloc_clear<ui_menu_main>(machine, container));
|
|
|
|
// update the menu state
|
|
if (menu_stack != nullptr)
|
|
menu_stack->do_handle();
|
|
|
|
// clear up anything pending to be released
|
|
clear_free_list(machine);
|
|
|
|
// if the menus are to be hidden, return a cancel here
|
|
if (machine.ui().is_menu_active() && ((machine.ui_input().pressed(IPT_UI_CONFIGURE) && !stack_has_special_main_menu()) || menu_stack == nullptr))
|
|
return UI_HANDLER_CANCEL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
MENU HELPERS
|
|
***************************************************************************/
|
|
|
|
//-------------------------------------------------
|
|
// render_triangle - render a triangle that
|
|
// is used for up/down arrows and left/right
|
|
// indicators
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::render_triangle(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param)
|
|
{
|
|
int halfwidth = dest.width() / 2;
|
|
int height = dest.height();
|
|
int x, y;
|
|
|
|
// start with all-transparent
|
|
dest.fill(rgb_t(0x00,0x00,0x00,0x00));
|
|
|
|
// render from the tip to the bottom
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
int linewidth = (y * (halfwidth - 1) + (height / 2)) * 255 * 2 / height;
|
|
UINT32 *target = &dest.pix32(y, halfwidth);
|
|
|
|
// don't antialias if height < 12
|
|
if (dest.height() < 12)
|
|
{
|
|
int pixels = (linewidth + 254) / 255;
|
|
if (pixels % 2 == 0) pixels++;
|
|
linewidth = pixels * 255;
|
|
}
|
|
|
|
// loop while we still have data to generate
|
|
for (x = 0; linewidth > 0; x++)
|
|
{
|
|
int dalpha;
|
|
|
|
// first column we only consume one pixel
|
|
if (x == 0)
|
|
{
|
|
dalpha = MIN(0xff, linewidth);
|
|
target[x] = rgb_t(dalpha,0xff,0xff,0xff);
|
|
}
|
|
|
|
// remaining columns consume two pixels, one on each side
|
|
else
|
|
{
|
|
dalpha = MIN(0x1fe, linewidth);
|
|
target[x] = target[-x] = rgb_t(dalpha/2,0xff,0xff,0xff);
|
|
}
|
|
|
|
// account for the weight we consumed */
|
|
linewidth -= dalpha;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// highlight
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::highlight(render_container *container, float x0, float y0, float x1, float y1, rgb_t bgcolor)
|
|
{
|
|
container->add_quad(x0, y0, x1, y1, bgcolor, hilight_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE) | PRIMFLAG_PACKABLE);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw_arrow
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_arrow(render_container *container, float x0, float y0, float x1, float y1, rgb_t fgcolor, UINT32 orientation)
|
|
{
|
|
container->add_quad(x0, y0, x1, y1, fgcolor, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(orientation) | PRIMFLAG_PACKABLE);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// init - initialize the ui menu system
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::init_ui(running_machine &machine)
|
|
{
|
|
render_manager &mrender = machine.render();
|
|
// create a texture for hilighting items in main menu
|
|
hilight_main_bitmap = std::make_unique<bitmap_rgb32>(1, 26);
|
|
int r1 = 0, g1 = 169, b1 = 255; //Any start color
|
|
int r2 = 0, g2 = 39, b2 = 130; //Any stop color
|
|
for (int y = 0; y < 26; y++)
|
|
{
|
|
int r = r1 + (y * (r2 - r1) / 26);
|
|
int g = g1 + (y * (g2 - g1) / 26);
|
|
int b = b1 + (y * (b2 - b1) / 26);
|
|
hilight_main_bitmap->pix32(y, 0) = rgb_t(0xff, r, g, b);
|
|
}
|
|
|
|
hilight_main_texture = mrender.texture_alloc();
|
|
hilight_main_texture->set_bitmap(*hilight_main_bitmap, hilight_main_bitmap->cliprect(), TEXFORMAT_ARGB32);
|
|
|
|
// create a texture for snapshot
|
|
snapx_bitmap = std::make_unique<bitmap_argb32>(0, 0);
|
|
snapx_texture = mrender.texture_alloc(render_texture::hq_scale);
|
|
|
|
// allocates and sets the default "no available" image
|
|
no_avail_bitmap = std::make_unique<bitmap_argb32>(256, 256);
|
|
UINT32 *dst = &no_avail_bitmap->pix32(0);
|
|
memcpy(dst, no_avail_bmp, 256 * 256 * sizeof(UINT32));
|
|
|
|
// allocates and sets the favorites star image
|
|
star_bitmap = std::make_unique<bitmap_argb32>(32, 32);
|
|
dst = &star_bitmap->pix32(0);
|
|
memcpy(dst, favorite_star_bmp, 32 * 32 * sizeof(UINT32));
|
|
star_texture = mrender.texture_alloc();
|
|
star_texture->set_bitmap(*star_bitmap, star_bitmap->cliprect(), TEXFORMAT_ARGB32);
|
|
|
|
// allocate icons
|
|
for (int i = 0; i < MAX_ICONS_RENDER; i++)
|
|
{
|
|
icons_bitmap[i] = auto_alloc(machine, bitmap_argb32);
|
|
icons_texture[i] = mrender.texture_alloc();
|
|
}
|
|
|
|
// create a texture for main menu background
|
|
bgrnd_bitmap = std::make_unique<bitmap_argb32>(0, 0);
|
|
bgrnd_texture = mrender.texture_alloc(render_texture::hq_scale);
|
|
|
|
ui_options &mopt = machine.ui().options();
|
|
if (mopt.use_background_image() && &machine.system() == &GAME_NAME(___empty))
|
|
{
|
|
emu_file backgroundfile(".", OPEN_FLAG_READ);
|
|
render_load_jpeg(*bgrnd_bitmap, backgroundfile, nullptr, "background.jpg");
|
|
|
|
if (!bgrnd_bitmap->valid())
|
|
render_load_png(*bgrnd_bitmap, backgroundfile, nullptr, "background.png");
|
|
|
|
if (bgrnd_bitmap->valid())
|
|
bgrnd_texture->set_bitmap(*bgrnd_bitmap, bgrnd_bitmap->cliprect(), TEXFORMAT_ARGB32);
|
|
else
|
|
bgrnd_bitmap->reset();
|
|
}
|
|
else
|
|
bgrnd_bitmap->reset();
|
|
|
|
// create a texture for toolbar
|
|
for (int x = 0; x < UI_TOOLBAR_BUTTONS; ++x)
|
|
{
|
|
toolbar_bitmap[x] = auto_alloc(machine, bitmap_argb32(32, 32));
|
|
sw_toolbar_bitmap[x] = auto_alloc(machine, bitmap_argb32(32, 32));
|
|
toolbar_texture[x] = mrender.texture_alloc();
|
|
sw_toolbar_texture[x] = mrender.texture_alloc();
|
|
UINT32 *dst = &toolbar_bitmap[x]->pix32(0);
|
|
memcpy(dst, toolbar_bitmap_bmp[x], 32 * 32 * sizeof(UINT32));
|
|
if (toolbar_bitmap[x]->valid())
|
|
toolbar_texture[x]->set_bitmap(*toolbar_bitmap[x], toolbar_bitmap[x]->cliprect(), TEXFORMAT_ARGB32);
|
|
else
|
|
toolbar_bitmap[x]->reset();
|
|
|
|
if (x == 0 || x == 2)
|
|
{
|
|
dst = &sw_toolbar_bitmap[x]->pix32(0);
|
|
memcpy(dst, toolbar_bitmap_bmp[x], 32 * 32 * sizeof(UINT32));
|
|
if (sw_toolbar_bitmap[x]->valid())
|
|
sw_toolbar_texture[x]->set_bitmap(*sw_toolbar_bitmap[x], sw_toolbar_bitmap[x]->cliprect(), TEXFORMAT_ARGB32);
|
|
else
|
|
sw_toolbar_bitmap[x]->reset();
|
|
}
|
|
else
|
|
sw_toolbar_bitmap[x]->reset();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// draw main menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_select_game(bool noinput)
|
|
{
|
|
float line_height = machine().ui().get_line_height();
|
|
float ud_arrow_width = line_height * machine().render().ui_aspect();
|
|
float gutter_width = 0.52f * line_height * machine().render().ui_aspect();
|
|
mouse_x = -1, mouse_y = -1;
|
|
float right_panel_size = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL) ? 2.0f * UI_BOX_LR_BORDER : 0.3f;
|
|
float visible_width = 1.0f - 4.0f * UI_BOX_LR_BORDER;
|
|
float primary_left = (1.0f - visible_width) * 0.5f;
|
|
float primary_width = visible_width;
|
|
bool is_swlist = ((item[0].flags & MENU_FLAG_UI_SWLIST) != 0);
|
|
bool is_favorites = ((item[0].flags & MENU_FLAG_UI_FAVORITE) != 0);
|
|
ui_manager &mui = machine().ui();
|
|
|
|
// draw background image if available
|
|
if (machine().ui().options().use_background_image() && bgrnd_bitmap->valid())
|
|
container->add_quad(0.0f, 0.0f, 1.0f, 1.0f, ARGB_WHITE, bgrnd_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
|
|
hover = item.size() + 1;
|
|
visible_items = (is_swlist) ? item.size() - 2 : item.size() - 2 - skip_main_items;
|
|
float extra_height = (is_swlist) ? 2.0f * line_height : (2.0f + skip_main_items) * line_height;
|
|
float visible_extra_menu_height = customtop + custombottom + extra_height;
|
|
|
|
// locate mouse
|
|
mouse_hit = false;
|
|
mouse_button = false;
|
|
if (!noinput)
|
|
{
|
|
mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
|
|
if (mouse_target != nullptr)
|
|
if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, *container, mouse_x, mouse_y))
|
|
mouse_hit = true;
|
|
}
|
|
|
|
// account for extra space at the top and bottom
|
|
float visible_main_menu_height = 1.0f - 2.0f * UI_BOX_TB_BORDER - visible_extra_menu_height;
|
|
visible_lines = floor(visible_main_menu_height / line_height);
|
|
visible_main_menu_height = (float)(visible_lines * line_height);
|
|
|
|
if (!is_swlist)
|
|
ui_globals::visible_main_lines = visible_lines;
|
|
else
|
|
ui_globals::visible_sw_lines = visible_lines;
|
|
|
|
// compute top/left of inner menu area by centering
|
|
float visible_left = primary_left;
|
|
float visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f;
|
|
|
|
// if the menu is at the bottom of the extra, adjust
|
|
visible_top += customtop;
|
|
|
|
// compute left box size
|
|
float x1 = visible_left - UI_BOX_LR_BORDER;
|
|
float y1 = visible_top - UI_BOX_TB_BORDER;
|
|
float x2 = x1 + 2.0f * UI_BOX_LR_BORDER;
|
|
float y2 = visible_top + visible_main_menu_height + UI_BOX_TB_BORDER + extra_height;
|
|
|
|
// add left box
|
|
visible_left = draw_left_panel(x1, y1, x2, y2);
|
|
visible_width -= right_panel_size + visible_left - 2.0f * UI_BOX_LR_BORDER;
|
|
|
|
// compute and add main box
|
|
x1 = visible_left - UI_BOX_LR_BORDER;
|
|
x2 = visible_left + visible_width + UI_BOX_LR_BORDER;
|
|
float line = visible_top + (float)(visible_lines * line_height);
|
|
|
|
//machine().ui().draw_outlined_box(container, x1, y1, x2, y2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
if (visible_items < visible_lines)
|
|
visible_lines = visible_items;
|
|
if (top_line < 0 || selected == 0)
|
|
top_line = 0;
|
|
if (selected < visible_items && top_line + visible_lines >= visible_items)
|
|
top_line = visible_items - visible_lines;
|
|
|
|
// determine effective positions taking into account the hilighting arrows
|
|
float effective_width = visible_width - 2.0f * gutter_width;
|
|
float effective_left = visible_left + gutter_width;
|
|
|
|
int n_loop = (visible_items >= visible_lines) ? visible_lines : visible_items;
|
|
if (m_prev_selected != nullptr && m_focus == focused_menu::main && selected < visible_items)
|
|
m_prev_selected = nullptr;
|
|
|
|
for (int linenum = 0; linenum < n_loop; linenum++)
|
|
{
|
|
float line_y = visible_top + (float)linenum * line_height;
|
|
int itemnum = top_line + linenum;
|
|
const ui_menu_item &pitem = item[itemnum];
|
|
const char *itemtext = pitem.text;
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
rgb_t fgcolor3 = UI_CLONE_COLOR;
|
|
float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
|
|
float line_y0 = line_y;
|
|
float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
|
|
float line_y1 = line_y + line_height;
|
|
|
|
// set the hover if this is our item
|
|
if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y && pitem.is_selectable())
|
|
hover = itemnum;
|
|
|
|
// if we're selected, draw with a different background
|
|
if (itemnum == selected && m_focus == focused_menu::main)
|
|
{
|
|
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
|
|
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
fgcolor3 = rgb_t(0xff, 0xcc, 0xcc, 0x00);
|
|
mui.draw_textured_box(container, line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(255, 43, 43, 43),
|
|
hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
}
|
|
// else if the mouse is over this item, draw with a different background
|
|
else if (itemnum == hover)
|
|
{
|
|
fgcolor = fgcolor3 = UI_MOUSEOVER_COLOR;
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
highlight(container, line_x0, line_y0, line_x1, line_y1, bgcolor);
|
|
}
|
|
else if (pitem.ref == m_prev_selected)
|
|
{
|
|
fgcolor = fgcolor3 = UI_MOUSEOVER_COLOR;
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
mui.draw_textured_box(container, line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(255, 43, 43, 43),
|
|
hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
}
|
|
|
|
// if we're on the top line, display the up arrow
|
|
if (linenum == 0 && top_line != 0)
|
|
{
|
|
draw_arrow(container, 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0);
|
|
|
|
if (hover == itemnum)
|
|
hover = HOVER_ARROW_UP;
|
|
}
|
|
// if we're on the bottom line, display the down arrow
|
|
else if (linenum == visible_lines - 1 && itemnum != visible_items - 1)
|
|
{
|
|
draw_arrow(container, 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0 ^ ORIENTATION_FLIP_Y);
|
|
|
|
if (hover == itemnum)
|
|
hover = HOVER_ARROW_DOWN;
|
|
}
|
|
// if we're just a divider, draw a line
|
|
else if (strcmp(itemtext, MENU_SEPARATOR_ITEM) == 0)
|
|
container->add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height,
|
|
UI_LINE_WIDTH, UI_TEXT_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
// draw the item centered
|
|
else if (pitem.subtext == nullptr)
|
|
{
|
|
int item_invert = pitem.flags & MENU_FLAG_INVERT;
|
|
float space = 0.0f;
|
|
|
|
if (ui_globals::has_icons && !is_swlist)
|
|
{
|
|
if (is_favorites)
|
|
{
|
|
ui_software_info *soft = (ui_software_info *)item[itemnum].ref;
|
|
if (soft->startempty == 1)
|
|
draw_icon(linenum, (void *)soft->driver, effective_left, line_y);
|
|
}
|
|
else
|
|
draw_icon(linenum, item[itemnum].ref, effective_left, line_y);
|
|
|
|
space = mui.get_line_height() * container->manager().ui_aspect() * 1.5f;
|
|
}
|
|
mui.draw_text_full(container, itemtext, effective_left + space, line_y, effective_width - space, JUSTIFY_LEFT, WRAP_TRUNCATE,
|
|
DRAW_NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor, nullptr, nullptr);
|
|
}
|
|
else
|
|
{
|
|
int item_invert = pitem.flags & MENU_FLAG_INVERT;
|
|
const char *subitem_text = pitem.subtext;
|
|
float item_width, subitem_width;
|
|
|
|
// compute right space for subitem
|
|
mui.draw_text_full(container, subitem_text, effective_left, line_y, machine().ui().get_string_width(pitem.subtext),
|
|
JUSTIFY_RIGHT, WRAP_NEVER, DRAW_NONE, item_invert ? fgcolor3 : fgcolor, bgcolor, &subitem_width, nullptr);
|
|
subitem_width += gutter_width;
|
|
|
|
// draw the item left-justified
|
|
mui.draw_text_full(container, itemtext, effective_left, line_y, effective_width - subitem_width,
|
|
JUSTIFY_LEFT, WRAP_TRUNCATE, DRAW_NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor, &item_width, nullptr);
|
|
|
|
// draw the subitem right-justified
|
|
mui.draw_text_full(container, subitem_text, effective_left + item_width, line_y, effective_width - item_width,
|
|
JUSTIFY_RIGHT, WRAP_NEVER, DRAW_NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
for (size_t count = visible_items; count < item.size(); count++)
|
|
{
|
|
const ui_menu_item &pitem = item[count];
|
|
const char *itemtext = pitem.text;
|
|
float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
|
|
float line_y0 = line;
|
|
float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
|
|
float line_y1 = line + line_height;
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
|
|
if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y && pitem.is_selectable())
|
|
hover = count;
|
|
|
|
// if we're selected, draw with a different background
|
|
if (count == selected && m_focus == focused_menu::main)
|
|
{
|
|
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
|
|
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
}
|
|
// else if the mouse is over this item, draw with a different background
|
|
else if (count == hover)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
}
|
|
|
|
// if we have some background hilighting to do, add a quad behind everything else
|
|
if (bgcolor != UI_TEXT_BG_COLOR)
|
|
mui.draw_textured_box(container, line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(255, 43, 43, 43),
|
|
hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
|
|
if (strcmp(itemtext, MENU_SEPARATOR_ITEM) == 0)
|
|
container->add_line(visible_left, line + 0.5f * line_height, visible_left + visible_width, line + 0.5f * line_height,
|
|
UI_LINE_WIDTH, UI_TEXT_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
else
|
|
mui.draw_text_full(container, itemtext, effective_left, line, effective_width, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
line += line_height;
|
|
}
|
|
|
|
x1 = x2;
|
|
x2 += right_panel_size;
|
|
|
|
draw_right_panel((selected >= 0 && selected < item.size()) ? item[selected].ref : nullptr, x1, y1, x2, y2);
|
|
|
|
x1 = primary_left - UI_BOX_LR_BORDER;
|
|
x2 = primary_left + primary_width + UI_BOX_LR_BORDER;
|
|
|
|
// if there is something special to add, do it by calling the virtual method
|
|
custom_render((selected >= 0 && selected < item.size()) ? item[selected].ref : nullptr, customtop, custombottom, x1, y1, x2, y2);
|
|
|
|
// return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
|
|
visitems = visible_lines - (top_line != 0) - (top_line + visible_lines != visible_items);
|
|
|
|
// reset redraw icon stage
|
|
if (!is_swlist)
|
|
ui_globals::redraw_icon = false;
|
|
|
|
// noinput
|
|
if (noinput)
|
|
{
|
|
int alpha = (1.0f - machine().options().pause_brightness()) * 255.0f;
|
|
if (alpha > 255)
|
|
alpha = 255;
|
|
if (alpha >= 0)
|
|
container->add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(alpha, 0x00, 0x00, 0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// get title and search path for right panel
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::get_title_search(std::string &snaptext, std::string &searchstr)
|
|
{
|
|
// get arts title text
|
|
snaptext.assign(arts_info[ui_globals::curimage_view].title);
|
|
|
|
// get search path
|
|
if (ui_globals::curimage_view == SNAPSHOT_VIEW)
|
|
searchstr = machine().options().value(arts_info[ui_globals::curimage_view].path);
|
|
else
|
|
searchstr = machine().ui().options().value(arts_info[ui_globals::curimage_view].path);
|
|
|
|
std::string tmp(searchstr);
|
|
path_iterator path(tmp.c_str());
|
|
std::string curpath;
|
|
|
|
path_iterator path_iter(arts_info[ui_globals::curimage_view].addpath);
|
|
std::string c_path;
|
|
|
|
// iterate over path and add path for zipped formats
|
|
while (path.next(curpath))
|
|
{
|
|
path_iter.reset();
|
|
while (path_iter.next(c_path))
|
|
searchstr.append(";").append(curpath).append(PATH_SEPARATOR).append(c_path);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// handle keys for main menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::handle_main_keys(UINT32 flags)
|
|
{
|
|
bool ignorepause = ui_menu::stack_has_special_main_menu();
|
|
|
|
// bail if no items
|
|
if (item.size() == 0)
|
|
return;
|
|
|
|
// if we hit select, return TRUE or pop the stack, depending on the item
|
|
if (exclusive_input_pressed(IPT_UI_SELECT, 0))
|
|
{
|
|
if (selected == item.size() - 1 && m_focus == focused_menu::main)
|
|
{
|
|
menu_event.iptkey = IPT_UI_CANCEL;
|
|
ui_menu::stack_pop(machine());
|
|
}
|
|
return;
|
|
}
|
|
|
|
// hitting cancel also pops the stack
|
|
if (exclusive_input_pressed(IPT_UI_CANCEL, 0))
|
|
{
|
|
if (!menu_has_search_active())
|
|
ui_menu::stack_pop(machine());
|
|
// else if (!ui_error)
|
|
// ui_menu::stack_pop(machine()); TODO
|
|
return;
|
|
}
|
|
|
|
// validate the current selection
|
|
validate_selection(1);
|
|
|
|
// swallow left/right keys if they are not appropriate
|
|
bool ignoreleft = ((item[selected].flags & MENU_FLAG_LEFT_ARROW) == 0);
|
|
bool ignoreright = ((item[selected].flags & MENU_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);
|
|
|
|
// accept left/right keys as-is with repeat
|
|
if (!ignoreleft && exclusive_input_pressed(IPT_UI_LEFT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0))
|
|
{
|
|
// Swap the right panel
|
|
if (m_focus == focused_menu::righttop)
|
|
menu_event.iptkey = IPT_UI_LEFT_PANEL;
|
|
return;
|
|
}
|
|
|
|
if (!ignoreright && exclusive_input_pressed(IPT_UI_RIGHT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0))
|
|
{
|
|
// Swap the right panel
|
|
if (m_focus == focused_menu::righttop)
|
|
menu_event.iptkey = IPT_UI_RIGHT_PANEL;
|
|
return;
|
|
}
|
|
|
|
// up backs up by one item
|
|
if (exclusive_input_pressed(IPT_UI_UP, 6))
|
|
{
|
|
// Filter
|
|
if (!leftclose && m_focus == focused_menu::left)
|
|
{
|
|
menu_event.iptkey = IPT_UI_UP_FILTER;
|
|
return;
|
|
}
|
|
|
|
// Infos
|
|
if (!rightclose && m_focus == focused_menu::rightbottom)
|
|
{
|
|
menu_event.iptkey = IPT_UI_UP_PANEL;
|
|
topline_datsview--;
|
|
return;
|
|
}
|
|
|
|
if (selected == visible_items + 1 || selected == 0 || ui_error)
|
|
return;
|
|
|
|
selected--;
|
|
|
|
if (selected == top_line && top_line != 0)
|
|
top_line--;
|
|
}
|
|
|
|
// down advances by one item
|
|
if (exclusive_input_pressed(IPT_UI_DOWN, 6))
|
|
{
|
|
// Filter
|
|
if (!leftclose && m_focus == focused_menu::left)
|
|
{
|
|
menu_event.iptkey = IPT_UI_DOWN_FILTER;
|
|
return;
|
|
}
|
|
|
|
// Infos
|
|
if (!rightclose && m_focus == focused_menu::rightbottom)
|
|
{
|
|
menu_event.iptkey = IPT_UI_DOWN_PANEL;
|
|
topline_datsview++;
|
|
return;
|
|
}
|
|
|
|
if (selected == item.size() - 1 || selected == visible_items - 1 || ui_error)
|
|
return;
|
|
|
|
selected++;
|
|
|
|
if (selected == top_line + visitems + (top_line != 0))
|
|
top_line++;
|
|
}
|
|
|
|
// page up backs up by visitems
|
|
if (exclusive_input_pressed(IPT_UI_PAGE_UP, 6))
|
|
{
|
|
// Infos
|
|
if (!rightclose && m_focus == focused_menu::rightbottom)
|
|
{
|
|
menu_event.iptkey = IPT_UI_DOWN_PANEL;
|
|
topline_datsview -= right_visible_lines - 1;
|
|
return;
|
|
}
|
|
|
|
if (selected < visible_items && !ui_error)
|
|
{
|
|
selected -= visitems;
|
|
|
|
if (selected < 0)
|
|
selected = 0;
|
|
|
|
top_line -= visitems - (top_line + visible_lines == visible_items);
|
|
}
|
|
}
|
|
|
|
// page down advances by visitems
|
|
if (exclusive_input_pressed(IPT_UI_PAGE_DOWN, 6))
|
|
{
|
|
// Infos
|
|
if (!rightclose && m_focus == focused_menu::rightbottom)
|
|
{
|
|
menu_event.iptkey = IPT_UI_DOWN_PANEL;
|
|
topline_datsview += right_visible_lines - 1;
|
|
return;
|
|
}
|
|
|
|
if (selected < visible_items && !ui_error)
|
|
{
|
|
selected += visible_lines - 2 + (selected == 0);
|
|
|
|
if (selected >= visible_items)
|
|
selected = visible_items - 1;
|
|
|
|
top_line += visible_lines - 2;
|
|
}
|
|
}
|
|
|
|
// home goes to the start
|
|
if (exclusive_input_pressed(IPT_UI_HOME, 0))
|
|
{
|
|
// Infos
|
|
if (!rightclose && m_focus == focused_menu::rightbottom)
|
|
{
|
|
menu_event.iptkey = IPT_UI_DOWN_PANEL;
|
|
topline_datsview = 0;
|
|
return;
|
|
}
|
|
|
|
if (selected < visible_items && !ui_error)
|
|
{
|
|
selected = 0;
|
|
top_line = 0;
|
|
}
|
|
}
|
|
|
|
// end goes to the last
|
|
if (exclusive_input_pressed(IPT_UI_END, 0))
|
|
{
|
|
// Infos
|
|
if (!rightclose && m_focus == focused_menu::rightbottom)
|
|
{
|
|
menu_event.iptkey = IPT_UI_DOWN_PANEL;
|
|
topline_datsview = totallines;
|
|
return;
|
|
}
|
|
|
|
if (selected < visible_items && !ui_error)
|
|
selected = top_line = visible_items - 1;
|
|
}
|
|
|
|
// pause enables/disables pause
|
|
if (!ui_error && !ignorepause && exclusive_input_pressed(IPT_UI_PAUSE, 0))
|
|
{
|
|
if (machine().paused())
|
|
machine().resume();
|
|
else
|
|
machine().pause();
|
|
}
|
|
|
|
// handle a toggle cheats request
|
|
if (!ui_error && machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0))
|
|
machine().cheat().set_enable(!machine().cheat().enabled());
|
|
|
|
// see if any other UI keys are pressed
|
|
if (menu_event.iptkey == IPT_INVALID)
|
|
for (int code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++)
|
|
{
|
|
if (ui_error || code == IPT_UI_CONFIGURE || (code == IPT_UI_LEFT && ignoreleft) || (code == IPT_UI_RIGHT && ignoreright) || (code == IPT_UI_PAUSE && ignorepause))
|
|
continue;
|
|
|
|
if (exclusive_input_pressed(code, 0))
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// handle input events for main menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::handle_main_events(UINT32 flags)
|
|
{
|
|
bool stop = false;
|
|
ui_event local_menu_event;
|
|
|
|
// loop while we have interesting events
|
|
while (!stop && machine().ui_input().pop_event(&local_menu_event))
|
|
{
|
|
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 (ui_error)
|
|
{
|
|
menu_event.iptkey = IPT_OTHER;
|
|
stop = true;
|
|
}
|
|
else
|
|
{
|
|
if (hover >= 0 && hover < item.size())
|
|
{
|
|
if (hover >= visible_items - 1 && selected < visible_items)
|
|
m_prev_selected = item[selected].ref;
|
|
selected = hover;
|
|
m_focus = focused_menu::main;
|
|
}
|
|
else if (hover == HOVER_ARROW_UP)
|
|
{
|
|
selected -= visitems;
|
|
if (selected < 0)
|
|
selected = 0;
|
|
top_line -= visitems - (top_line + visible_lines == visible_items);
|
|
}
|
|
else if (hover == HOVER_ARROW_DOWN)
|
|
{
|
|
selected += visible_lines - 2 + (selected == 0);
|
|
if (selected >= visible_items)
|
|
selected = visible_items - 1;
|
|
top_line += visible_lines - 2;
|
|
}
|
|
else if (hover == HOVER_UI_RIGHT)
|
|
menu_event.iptkey = IPT_UI_RIGHT;
|
|
else if (hover == HOVER_UI_LEFT)
|
|
menu_event.iptkey = IPT_UI_LEFT;
|
|
else if (hover == HOVER_DAT_DOWN)
|
|
topline_datsview += right_visible_lines - 1;
|
|
else if (hover == HOVER_DAT_UP)
|
|
topline_datsview -= 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)
|
|
{
|
|
menu_event.iptkey = IPT_UI_FAVORITES;
|
|
stop = true;
|
|
}
|
|
else if (hover == HOVER_B_EXPORT)
|
|
{
|
|
menu_event.iptkey = IPT_UI_EXPORT;
|
|
stop = true;
|
|
}
|
|
else if (hover == HOVER_B_DATS)
|
|
{
|
|
menu_event.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);
|
|
menu_event.iptkey = IPT_OTHER;
|
|
stop = true;
|
|
}
|
|
else if (hover >= HOVER_FILTER_FIRST && hover <= HOVER_FILTER_LAST)
|
|
{
|
|
l_hover = (HOVER_FILTER_FIRST - hover) * (-1);
|
|
menu_event.iptkey = IPT_OTHER;
|
|
stop = true;
|
|
}
|
|
}
|
|
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;
|
|
menu_event.iptkey = IPT_UI_SELECT;
|
|
}
|
|
|
|
if (selected == item.size() - 1)
|
|
{
|
|
menu_event.iptkey = IPT_UI_CANCEL;
|
|
ui_menu::stack_pop(machine());
|
|
}
|
|
stop = true;
|
|
break;
|
|
|
|
// caught scroll event
|
|
case UI_EVENT_MOUSE_WHEEL:
|
|
if (local_menu_event.zdelta > 0)
|
|
{
|
|
if (selected >= visible_items || selected == 0 || 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 || ui_error)
|
|
break;
|
|
selected += local_menu_event.num_lines;
|
|
if (selected > visible_items - 1)
|
|
selected = visible_items - 1;
|
|
if (selected >= top_line + visitems + (top_line != 0))
|
|
top_line += local_menu_event.num_lines;
|
|
}
|
|
break;
|
|
|
|
// translate CHAR events into specials
|
|
case UI_EVENT_CHAR:
|
|
if (exclusive_input_pressed(IPT_UI_CONFIGURE, 0))
|
|
{
|
|
menu_event.iptkey = IPT_UI_CONFIGURE;
|
|
stop = true;
|
|
}
|
|
else
|
|
{
|
|
menu_event.iptkey = IPT_SPECIAL;
|
|
menu_event.unichar = local_menu_event.ch;
|
|
stop = true;
|
|
}
|
|
break;
|
|
|
|
// ignore everything else
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw right box title
|
|
//-------------------------------------------------
|
|
|
|
float ui_menu::draw_right_box_title(float x1, float y1, float x2, float y2)
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
float line_height = mui.get_line_height();
|
|
float midl = (x2 - x1) * 0.5f;
|
|
|
|
// add outlined box for options
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
|
|
// add separator line
|
|
container->add_line(x1 + midl, y1, x1 + midl, y1 + line_height, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
|
|
std::string buffer[RP_LAST + 1];
|
|
buffer[RP_IMAGES].assign("Images");
|
|
buffer[RP_INFOS].assign("Infos");
|
|
|
|
for (int cells = RP_IMAGES; cells <= RP_INFOS; cells++)
|
|
{
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
|
|
if (mouse_hit && x1 <= mouse_x && x1 + midl > mouse_x && y1 <= mouse_y && y1 + line_height > mouse_y)
|
|
{
|
|
if (ui_globals::rpanel != cells)
|
|
{
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
hover = HOVER_RP_FIRST + cells;
|
|
}
|
|
}
|
|
|
|
if (ui_globals::rpanel != cells)
|
|
{
|
|
container->add_line(x1, y1 + line_height, x1 + midl, y1 + line_height, UI_LINE_WIDTH,
|
|
UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
if (fgcolor != UI_MOUSEOVER_COLOR)
|
|
fgcolor = UI_CLONE_COLOR;
|
|
}
|
|
|
|
if (m_focus == focused_menu::righttop && ui_globals::rpanel == cells)
|
|
{
|
|
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
|
|
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
mui.draw_textured_box(container, x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height,
|
|
bgcolor, rgb_t(255, 43, 43, 43), hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
}
|
|
|
|
if (bgcolor == UI_MOUSEOVER_BG_COLOR)
|
|
container->add_rect(x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height,
|
|
bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
|
|
mui.draw_text_full(container, buffer[cells].c_str(), x1 + UI_LINE_WIDTH, y1, midl - UI_LINE_WIDTH,
|
|
JUSTIFY_CENTER, WRAP_NEVER, DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
x1 += midl;
|
|
}
|
|
|
|
return (y1 + line_height + UI_LINE_WIDTH);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// common function for images render
|
|
//-------------------------------------------------
|
|
|
|
std::string ui_menu::arts_render_common(float origx1, float origy1, float origx2, float origy2)
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
float line_height = mui.get_line_height();
|
|
std::string snaptext, searchstr;
|
|
get_title_search(snaptext, searchstr);
|
|
|
|
// apply title to right panel
|
|
float title_size = 0.0f;
|
|
float txt_lenght = 0.0f;
|
|
|
|
for (int x = FIRST_VIEW; x < LAST_VIEW; x++)
|
|
{
|
|
mui.draw_text_full(container, arts_info[x].title, origx1, origy1, origx2 - origx1, JUSTIFY_CENTER,
|
|
WRAP_TRUNCATE, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &txt_lenght, nullptr);
|
|
txt_lenght += 0.01f;
|
|
title_size = MAX(txt_lenght, title_size);
|
|
}
|
|
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
if (m_focus == focused_menu::rightbottom)
|
|
{
|
|
fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
|
|
bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
|
|
}
|
|
|
|
float middle = origx2 - origx1;
|
|
|
|
if (bgcolor != UI_TEXT_BG_COLOR)
|
|
mui.draw_textured_box(container, origx1 + ((middle - title_size) * 0.5f), origy1, origx1 + ((middle + title_size) * 0.5f),
|
|
origy1 + line_height, bgcolor, rgb_t(255, 43, 43, 43), hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
|
|
mui.draw_text_full(container, snaptext.c_str(), origx1, origy1, origx2 - origx1, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
|
|
draw_common_arrow(origx1, origy1, origx2, origy2, ui_globals::curimage_view, FIRST_VIEW, LAST_VIEW, title_size);
|
|
|
|
return searchstr;
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw favorites star
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_star(float x0, float y0)
|
|
{
|
|
float y1 = y0 + machine().ui().get_line_height();
|
|
float x1 = x0 + machine().ui().get_line_height() * container->manager().ui_aspect();
|
|
container->add_quad(x0, y0, x1, y1, ARGB_WHITE, star_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_PACKABLE);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw toolbar
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_toolbar(float x1, float y1, float x2, float y2, bool software)
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
// draw a box
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
|
|
|
|
// take off the borders
|
|
x1 += UI_BOX_LR_BORDER;
|
|
x2 -= UI_BOX_LR_BORDER;
|
|
y1 += UI_BOX_TB_BORDER;
|
|
y2 -= UI_BOX_TB_BORDER;
|
|
|
|
render_texture **t_texture = (software) ? sw_toolbar_texture : toolbar_texture;
|
|
bitmap_argb32 **t_bitmap = (software) ? sw_toolbar_bitmap : toolbar_bitmap;
|
|
|
|
int m_valid = 0;
|
|
for (int x = 0; x < UI_TOOLBAR_BUTTONS; ++x)
|
|
if (t_bitmap[x]->valid())
|
|
m_valid++;
|
|
|
|
float x_pixel = 1.0f / container->manager().ui_target().width();
|
|
int h_len = mui.get_line_height() * container->manager().ui_target().height();
|
|
h_len = (h_len % 2 == 0) ? h_len : h_len - 1;
|
|
x1 = (x1 + x2) * 0.5f - x_pixel * (m_valid * ((h_len / 2) + 2));
|
|
|
|
for (int z = 0; z < UI_TOOLBAR_BUTTONS; ++z)
|
|
{
|
|
if (t_bitmap[z]->valid())
|
|
{
|
|
x2 = x1 + x_pixel * h_len;
|
|
rgb_t color(0xEFEFEFEF);
|
|
if (mouse_hit && x1 <= mouse_x && x2 > mouse_x && y1 <= mouse_y && y2 > mouse_y)
|
|
{
|
|
hover = HOVER_B_FAV + z;
|
|
color = ARGB_WHITE;
|
|
float ypos = y2 + machine().ui().get_line_height() + 2.0f * UI_BOX_TB_BORDER;
|
|
mui.draw_text_box(container, hover_msg[z], JUSTIFY_CENTER, 0.5f, ypos, UI_BACKGROUND_COLOR);
|
|
}
|
|
|
|
container->add_quad(x1, y1, x2, y2, color, t_texture[z], PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
x1 += x_pixel * (h_len + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// perform rendering of image
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::arts_render_images(bitmap_argb32 *tmp_bitmap, float origx1, float origy1, float origx2, float origy2, bool software)
|
|
{
|
|
bool no_available = false;
|
|
float line_height = machine().ui().get_line_height();
|
|
|
|
// if it fails, use the default image
|
|
if (!tmp_bitmap->valid())
|
|
{
|
|
tmp_bitmap->allocate(256, 256);
|
|
for (int x = 0; x < 256; x++)
|
|
for (int y = 0; y < 256; y++)
|
|
tmp_bitmap->pix32(y, x) = no_avail_bitmap->pix32(y, x);
|
|
no_available = true;
|
|
}
|
|
|
|
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);
|
|
int screen_width = machine().render().ui_target().width();
|
|
int screen_height = machine().render().ui_target().height();
|
|
int panel_width_pixel = panel_width * screen_width;
|
|
int panel_height_pixel = panel_height * screen_height;
|
|
float ratio = 0.0f;
|
|
|
|
// Calculate resize ratios for resizing
|
|
float ratioW = (float)panel_width_pixel / tmp_bitmap->width();
|
|
float ratioH = (float)panel_height_pixel / tmp_bitmap->height();
|
|
float ratioI = (float)tmp_bitmap->height() / tmp_bitmap->width();
|
|
int dest_xPixel = tmp_bitmap->width();
|
|
int dest_yPixel = tmp_bitmap->height();
|
|
|
|
// force 4:3 ratio min
|
|
if (machine().ui().options().forced_4x3_snapshot() && ratioI < 0.75f && ui_globals::curimage_view == SNAPSHOT_VIEW)
|
|
{
|
|
// smaller ratio will ensure that the image fits in the view
|
|
dest_yPixel = tmp_bitmap->width() * 0.75f;
|
|
ratioH = (float)panel_height_pixel / dest_yPixel;
|
|
ratio = MIN(ratioW, ratioH);
|
|
dest_xPixel = tmp_bitmap->width() * ratio;
|
|
dest_yPixel *= ratio;
|
|
}
|
|
// resize the bitmap if necessary
|
|
else if (ratioW < 1 || ratioH < 1 || (machine().ui().options().enlarge_snaps() && !no_available))
|
|
{
|
|
// smaller ratio will ensure that the image fits in the view
|
|
ratio = MIN(ratioW, ratioH);
|
|
dest_xPixel = tmp_bitmap->width() * ratio;
|
|
dest_yPixel = tmp_bitmap->height() * ratio;
|
|
}
|
|
|
|
bitmap_argb32 *dest_bitmap;
|
|
|
|
// resample if necessary
|
|
if (dest_xPixel != tmp_bitmap->width() || dest_yPixel != tmp_bitmap->height())
|
|
{
|
|
dest_bitmap = auto_alloc(machine(), bitmap_argb32);
|
|
dest_bitmap->allocate(dest_xPixel, dest_yPixel);
|
|
render_color color = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
render_resample_argb_bitmap_hq(*dest_bitmap, *tmp_bitmap, color, true);
|
|
}
|
|
else
|
|
dest_bitmap = tmp_bitmap;
|
|
|
|
snapx_bitmap->allocate(panel_width_pixel, panel_height_pixel);
|
|
int x1 = (0.5f * panel_width_pixel) - (0.5f * dest_xPixel);
|
|
int y1 = (0.5f * panel_height_pixel) - (0.5f * dest_yPixel);
|
|
|
|
for (int x = 0; x < dest_xPixel; x++)
|
|
for (int y = 0; y < dest_yPixel; y++)
|
|
snapx_bitmap->pix32(y + y1, x + x1) = dest_bitmap->pix32(y, x);
|
|
|
|
auto_free(machine(), dest_bitmap);
|
|
|
|
// apply bitmap
|
|
snapx_texture->set_bitmap(*snapx_bitmap, snapx_bitmap->cliprect(), TEXFORMAT_ARGB32);
|
|
}
|
|
else
|
|
snapx_bitmap->reset();
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw common arrows
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_common_arrow(float origx1, float origy1, float origx2, float origy2, int current, int dmin, int dmax, float title_size)
|
|
{
|
|
float line_height = machine().ui().get_line_height();
|
|
float lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect();
|
|
float gutter_width = lr_arrow_width * 1.3f;
|
|
|
|
// set left-right arrows dimension
|
|
float ar_x0 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width - lr_arrow_width;
|
|
float ar_y0 = origy1 + 0.1f * line_height;
|
|
float ar_x1 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width;
|
|
float ar_y1 = origy1 + 0.9f * line_height;
|
|
|
|
float al_x0 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width;
|
|
float al_y0 = origy1 + 0.1f * line_height;
|
|
float al_x1 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width + lr_arrow_width;
|
|
float al_y1 = origy1 + 0.9f * line_height;
|
|
|
|
rgb_t fgcolor_right, fgcolor_left;
|
|
fgcolor_right = fgcolor_left = UI_TEXT_COLOR;
|
|
|
|
// set hover
|
|
if (mouse_hit && ar_x0 <= mouse_x && ar_x1 > mouse_x && ar_y0 <= mouse_y && ar_y1 > mouse_y && current != dmax)
|
|
{
|
|
machine().ui().draw_textured_box(container, ar_x0 + 0.01f, ar_y0, ar_x1 - 0.01f, ar_y1, UI_MOUSEOVER_BG_COLOR, rgb_t(255, 43, 43, 43),
|
|
hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
hover = HOVER_UI_RIGHT;
|
|
fgcolor_right = UI_MOUSEOVER_COLOR;
|
|
}
|
|
else if (mouse_hit && al_x0 <= mouse_x && al_x1 > mouse_x && al_y0 <= mouse_y && al_y1 > mouse_y && current != dmin)
|
|
{
|
|
machine().ui().draw_textured_box(container, al_x0 + 0.01f, al_y0, al_x1 - 0.01f, al_y1, UI_MOUSEOVER_BG_COLOR, rgb_t(255, 43, 43, 43),
|
|
hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
hover = HOVER_UI_LEFT;
|
|
fgcolor_left = UI_MOUSEOVER_COLOR;
|
|
}
|
|
|
|
// apply arrow
|
|
if (current == dmin)
|
|
container->add_quad(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90) | PRIMFLAG_PACKABLE);
|
|
else if (current == dmax)
|
|
container->add_quad(al_x0, al_y0, al_x1, al_y1, fgcolor_left, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90 ^ ORIENTATION_FLIP_X) | PRIMFLAG_PACKABLE);
|
|
else
|
|
{
|
|
container->add_quad(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90) | PRIMFLAG_PACKABLE);
|
|
container->add_quad(al_x0, al_y0, al_x1, al_y1, fgcolor_left, arrow_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90 ^ ORIENTATION_FLIP_X) | PRIMFLAG_PACKABLE);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw icons
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_icon(int linenum, void *selectedref, float x0, float y0)
|
|
{
|
|
static const game_driver *olddriver[MAX_ICONS_RENDER] = { nullptr };
|
|
float x1 = x0 + machine().ui().get_line_height() * container->manager().ui_aspect(container);
|
|
float y1 = y0 + machine().ui().get_line_height();
|
|
const game_driver *driver = (const game_driver *)selectedref;
|
|
|
|
if (olddriver[linenum] != driver || ui_globals::redraw_icon)
|
|
{
|
|
olddriver[linenum] = driver;
|
|
|
|
// set clone status
|
|
bool cloneof = strcmp(driver->parent, "0");
|
|
if (cloneof)
|
|
{
|
|
int cx = driver_list::find(driver->parent);
|
|
if (cx != -1 && ((driver_list::driver(cx).flags & MACHINE_IS_BIOS_ROOT) != 0))
|
|
cloneof = false;
|
|
}
|
|
|
|
// get search path
|
|
path_iterator path(machine().ui().options().icons_directory());
|
|
std::string curpath;
|
|
std::string searchstr(machine().ui().options().icons_directory());
|
|
|
|
// iterate over path and add path for zipped formats
|
|
while (path.next(curpath))
|
|
searchstr.append(";").append(curpath.c_str()).append(PATH_SEPARATOR).append("icons");
|
|
|
|
bitmap_argb32 *tmp = auto_alloc(machine(), bitmap_argb32);
|
|
emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ);
|
|
std::string fullname = std::string(driver->name).append(".ico");
|
|
render_load_ico(*tmp, snapfile, nullptr, fullname.c_str());
|
|
|
|
if (!tmp->valid() && cloneof)
|
|
{
|
|
fullname.assign(driver->parent).append(".ico");
|
|
render_load_ico(*tmp, snapfile, nullptr, fullname.c_str());
|
|
}
|
|
|
|
if (tmp->valid())
|
|
{
|
|
float panel_width = x1 - x0;
|
|
float panel_height = y1 - y0;
|
|
int screen_width = machine().render().ui_target().width();
|
|
int screen_height = machine().render().ui_target().height();
|
|
int panel_width_pixel = panel_width * screen_width;
|
|
int panel_height_pixel = panel_height * screen_height;
|
|
|
|
// Calculate resize ratios for resizing
|
|
float ratioW = (float)panel_width_pixel / tmp->width();
|
|
float ratioH = (float)panel_height_pixel / tmp->height();
|
|
int dest_xPixel = tmp->width();
|
|
int dest_yPixel = tmp->height();
|
|
|
|
if (ratioW < 1 || ratioH < 1)
|
|
{
|
|
// smaller ratio will ensure that the image fits in the view
|
|
float ratio = MIN(ratioW, ratioH);
|
|
dest_xPixel = tmp->width() * ratio;
|
|
dest_yPixel = tmp->height() * ratio;
|
|
}
|
|
|
|
bitmap_argb32 *dest_bitmap;
|
|
dest_bitmap = auto_alloc(machine(), bitmap_argb32);
|
|
|
|
// resample if necessary
|
|
if (dest_xPixel != tmp->width() || dest_yPixel != tmp->height())
|
|
{
|
|
dest_bitmap->allocate(dest_xPixel, dest_yPixel);
|
|
render_color color = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
render_resample_argb_bitmap_hq(*dest_bitmap, *tmp, color, true);
|
|
}
|
|
else
|
|
dest_bitmap = tmp;
|
|
|
|
icons_bitmap[linenum]->reset();
|
|
icons_bitmap[linenum]->allocate(panel_width_pixel, panel_height_pixel);
|
|
|
|
for (int x = 0; x < dest_xPixel; x++)
|
|
for (int y = 0; y < dest_yPixel; y++)
|
|
icons_bitmap[linenum]->pix32(y, x) = dest_bitmap->pix32(y, x);
|
|
|
|
auto_free(machine(), dest_bitmap);
|
|
|
|
icons_texture[linenum]->set_bitmap(*icons_bitmap[linenum], icons_bitmap[linenum]->cliprect(), TEXFORMAT_ARGB32);
|
|
}
|
|
else {
|
|
if (icons_bitmap[linenum] != nullptr)
|
|
{
|
|
icons_bitmap[linenum]->reset();
|
|
}
|
|
}
|
|
auto_free(machine(), tmp);
|
|
}
|
|
|
|
if (icons_bitmap[linenum] != nullptr && icons_bitmap[linenum]->valid())
|
|
container->add_quad(x0, y0, x1, y1, ARGB_WHITE, icons_texture[linenum], PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_PACKABLE);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw info arrow
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float text_size, float ud_arrow_width)
|
|
{
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
UINT32 orientation = (!ub) ? ROT0 : ROT0 ^ ORIENTATION_FLIP_Y;
|
|
|
|
if (mouse_hit && origx1 <= mouse_x && origx2 > mouse_x && oy1 <= mouse_y && oy1 + (line_height * text_size) > mouse_y)
|
|
{
|
|
machine().ui().draw_textured_box(container, origx1 + 0.01f, oy1, origx2 - 0.01f, oy1 + (line_height * text_size), UI_MOUSEOVER_BG_COLOR,
|
|
rgb_t(255, 43, 43, 43), hilight_main_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
|
|
hover = (!ub) ? HOVER_DAT_UP : HOVER_DAT_DOWN;
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
}
|
|
|
|
draw_arrow(container, 0.5f * (origx1 + origx2) - 0.5f * (ud_arrow_width * text_size), oy1 + 0.25f * (line_height * text_size),
|
|
0.5f * (origx1 + origx2) + 0.5f * (ud_arrow_width * text_size), oy1 + 0.75f * (line_height * text_size), fgcolor, orientation);
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw - draw palette menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_palette_menu()
|
|
{
|
|
ui_manager &mui = machine().ui();
|
|
float line_height = mui.get_line_height();
|
|
float lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect();
|
|
float ud_arrow_width = line_height * machine().render().ui_aspect();
|
|
float gutter_width = lr_arrow_width * 1.3f;
|
|
int itemnum, linenum;
|
|
|
|
if (machine().ui().options().use_background_image() && machine().options().system() == nullptr && bgrnd_bitmap->valid())
|
|
container->add_quad(0.0f, 0.0f, 1.0f, 1.0f, ARGB_WHITE, bgrnd_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
|
|
// compute the width and height of the full menu
|
|
float visible_width = 0;
|
|
float visible_main_menu_height = 0;
|
|
for (itemnum = 0; itemnum < item.size(); itemnum++)
|
|
{
|
|
const ui_menu_item &pitem = item[itemnum];
|
|
|
|
// compute width of left hand side
|
|
float total_width = gutter_width + mui.get_string_width(pitem.text) + gutter_width;
|
|
|
|
// add in width of right hand side
|
|
if (pitem.subtext)
|
|
total_width += 2.0f * gutter_width + mui.get_string_width(pitem.subtext);
|
|
|
|
// track the maximum
|
|
if (total_width > visible_width)
|
|
visible_width = total_width;
|
|
|
|
// track the height as well
|
|
visible_main_menu_height += line_height;
|
|
}
|
|
|
|
// account for extra space at the top and bottom
|
|
float visible_extra_menu_height = customtop + custombottom;
|
|
|
|
// add a little bit of slop for rounding
|
|
visible_width += 0.01f;
|
|
visible_main_menu_height += 0.01f;
|
|
|
|
// if we are too wide or too tall, clamp it down
|
|
if (visible_width + 2.0f * UI_BOX_LR_BORDER > 1.0f)
|
|
visible_width = 1.0f - 2.0f * UI_BOX_LR_BORDER;
|
|
|
|
// if the menu and extra menu won't fit, take away part of the regular menu, it will scroll
|
|
if (visible_main_menu_height + visible_extra_menu_height + 2.0f * UI_BOX_TB_BORDER > 1.0f)
|
|
visible_main_menu_height = 1.0f - 2.0f * UI_BOX_TB_BORDER - visible_extra_menu_height;
|
|
|
|
int visible_lines = floor(visible_main_menu_height / line_height);
|
|
visible_main_menu_height = (float)visible_lines * line_height;
|
|
|
|
// compute top/left of inner menu area by centering
|
|
float visible_left = (1.0f - visible_width) * 0.5f;
|
|
float visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f;
|
|
|
|
// if the menu is at the bottom of the extra, adjust
|
|
visible_top += customtop;
|
|
|
|
// first add us a box
|
|
float x1 = visible_left - UI_BOX_LR_BORDER;
|
|
float y1 = visible_top - UI_BOX_TB_BORDER;
|
|
float x2 = visible_left + visible_width + UI_BOX_LR_BORDER;
|
|
float y2 = visible_top + visible_main_menu_height + UI_BOX_TB_BORDER;
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
|
|
// determine the first visible line based on the current selection
|
|
int top_line = selected - visible_lines / 2;
|
|
if (top_line < 0)
|
|
top_line = 0;
|
|
if (top_line + visible_lines >= item.size())
|
|
top_line = item.size() - visible_lines;
|
|
|
|
// determine effective positions taking into account the hilighting arrows
|
|
float effective_width = visible_width - 2.0f * gutter_width;
|
|
float effective_left = visible_left + gutter_width;
|
|
|
|
// locate mouse
|
|
mouse_hit = false;
|
|
mouse_button = false;
|
|
mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
|
|
if (mouse_target != nullptr)
|
|
if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, *container, mouse_x, mouse_y))
|
|
mouse_hit = true;
|
|
|
|
// loop over visible lines
|
|
hover = item.size() + 1;
|
|
float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
|
|
float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
|
|
|
|
for (linenum = 0; linenum < visible_lines; linenum++)
|
|
{
|
|
float line_y = visible_top + (float)linenum * line_height;
|
|
itemnum = top_line + linenum;
|
|
const ui_menu_item &pitem = item[itemnum];
|
|
const char *itemtext = pitem.text;
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
float line_y0 = line_y;
|
|
float line_y1 = line_y + line_height;
|
|
|
|
// set the hover if this is our item
|
|
if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y && pitem.is_selectable())
|
|
hover = itemnum;
|
|
|
|
// if we're selected, draw with a different background
|
|
if (itemnum == selected)
|
|
{
|
|
fgcolor = UI_SELECTED_COLOR;
|
|
bgcolor = UI_SELECTED_BG_COLOR;
|
|
}
|
|
|
|
// else if the mouse is over this item, draw with a different background
|
|
else if (itemnum == hover)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
}
|
|
|
|
// if we have some background hilighting to do, add a quad behind everything else
|
|
if (bgcolor != UI_TEXT_BG_COLOR)
|
|
highlight(container, line_x0, line_y0, line_x1, line_y1, bgcolor);
|
|
|
|
// if we're on the top line, display the up arrow
|
|
if (linenum == 0 && top_line != 0)
|
|
{
|
|
draw_arrow(container,
|
|
0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
|
|
line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
|
|
line_y + 0.75f * line_height,
|
|
fgcolor,
|
|
ROT0);
|
|
if (hover == itemnum)
|
|
hover = HOVER_ARROW_UP;
|
|
}
|
|
|
|
// if we're on the bottom line, display the down arrow
|
|
else if (linenum == visible_lines - 1 && itemnum != item.size() - 1)
|
|
{
|
|
draw_arrow(container,
|
|
0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
|
|
line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
|
|
line_y + 0.75f * line_height,
|
|
fgcolor,
|
|
ROT0 ^ ORIENTATION_FLIP_Y);
|
|
if (hover == itemnum)
|
|
hover = HOVER_ARROW_DOWN;
|
|
}
|
|
|
|
// if we're just a divider, draw a line
|
|
else if (strcmp(itemtext, MENU_SEPARATOR_ITEM) == 0)
|
|
container->add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
|
|
// if we don't have a subitem, just draw the string centered
|
|
else if (pitem.subtext == nullptr)
|
|
mui.draw_text_full(container, itemtext, effective_left, line_y, effective_width,
|
|
JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
|
|
// otherwise, draw the item on the left and the subitem text on the right
|
|
else
|
|
{
|
|
const char *subitem_text = pitem.subtext;
|
|
rgb_t color = rgb_t((UINT32)strtoul(subitem_text, nullptr, 16));
|
|
|
|
// draw the left-side text
|
|
mui.draw_text_full(container, itemtext, effective_left, line_y, effective_width,
|
|
JUSTIFY_LEFT, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
|
|
// give 2 spaces worth of padding
|
|
float subitem_width = mui.get_string_width("FF00FF00");
|
|
|
|
mui.draw_outlined_box(container, effective_left + effective_width - subitem_width, line_y0,
|
|
effective_left + effective_width, line_y1, color);
|
|
}
|
|
}
|
|
|
|
// if there is something special to add, do it by calling the virtual method
|
|
custom_render((selected >= 0 && selected < item.size()) ? item[selected].ref : nullptr, customtop, custombottom, x1, y1, x2, y2);
|
|
|
|
// return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
|
|
visitems = visible_lines - (top_line != 0) - (top_line + visible_lines != item.size());
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// draw - draw dats menu
|
|
//-------------------------------------------------
|
|
|
|
void ui_menu::draw_dats_menu()
|
|
{
|
|
float line_height = machine().ui().get_line_height();
|
|
float ud_arrow_width = line_height * machine().render().ui_aspect();
|
|
float gutter_width = 0.52f * line_height * machine().render().ui_aspect();
|
|
mouse_x = -1, mouse_y = -1;
|
|
float visible_width = 1.0f - 2.0f * UI_BOX_LR_BORDER;
|
|
float visible_left = (1.0f - visible_width) * 0.5f;
|
|
ui_manager &mui = machine().ui();
|
|
|
|
// draw background image if available
|
|
if (machine().ui().options().use_background_image() && bgrnd_bitmap->valid())
|
|
container->add_quad(0.0f, 0.0f, 1.0f, 1.0f, ARGB_WHITE, bgrnd_texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
|
|
hover = item.size() + 1;
|
|
visible_items = item.size() - 2;
|
|
float extra_height = 2.0f * line_height;
|
|
float visible_extra_menu_height = customtop + custombottom + extra_height;
|
|
|
|
// locate mouse
|
|
mouse_hit = false;
|
|
mouse_button = false;
|
|
mouse_target = machine().ui_input().find_mouse(&mouse_target_x, &mouse_target_y, &mouse_button);
|
|
if (mouse_target != nullptr)
|
|
if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, *container, mouse_x, mouse_y))
|
|
mouse_hit = true;
|
|
|
|
// account for extra space at the top and bottom
|
|
float visible_main_menu_height = 1.0f - 2.0f * UI_BOX_TB_BORDER - visible_extra_menu_height;
|
|
visible_lines = floor(visible_main_menu_height / line_height);
|
|
visible_main_menu_height = (float)(visible_lines * line_height);
|
|
|
|
// compute top/left of inner menu area by centering
|
|
float visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f;
|
|
|
|
// if the menu is at the bottom of the extra, adjust
|
|
visible_top += customtop;
|
|
|
|
// compute left box size
|
|
float x1 = visible_left;
|
|
float y1 = visible_top - UI_BOX_TB_BORDER;
|
|
float x2 = x1 + visible_width;
|
|
float y2 = visible_top + visible_main_menu_height + UI_BOX_TB_BORDER + extra_height;
|
|
float line = visible_top + (float)(visible_lines * line_height);
|
|
|
|
//machine().ui().draw_outlined_box(container, x1, y1, x2, y2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
|
|
mui.draw_outlined_box(container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
|
|
|
|
if (visible_items < visible_lines)
|
|
visible_lines = visible_items;
|
|
if (top_line < 0)
|
|
top_line = 0;
|
|
if (top_line + visible_lines >= visible_items)
|
|
top_line = visible_items - visible_lines;
|
|
|
|
// determine effective positions taking into account the hilighting arrows
|
|
float effective_width = visible_width - 2.0f * gutter_width;
|
|
float effective_left = visible_left + gutter_width;
|
|
|
|
int n_loop = (visible_items >= visible_lines) ? visible_lines : visible_items;
|
|
|
|
for (int linenum = 0; linenum < n_loop; linenum++)
|
|
{
|
|
float line_y = visible_top + (float)linenum * line_height;
|
|
int itemnum = top_line + linenum;
|
|
const ui_menu_item &pitem = item[itemnum];
|
|
const char *itemtext = pitem.text;
|
|
rgb_t fgcolor = UI_TEXT_COLOR;
|
|
rgb_t bgcolor = UI_TEXT_BG_COLOR;
|
|
float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
|
|
float line_y0 = line_y;
|
|
float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
|
|
float line_y1 = line_y + line_height;
|
|
|
|
// if we're on the top line, display the up arrow
|
|
if (linenum == 0 && top_line != 0)
|
|
{
|
|
draw_arrow(container, 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0);
|
|
|
|
if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
highlight(container, line_x0, line_y0, line_x1, line_y1, bgcolor);
|
|
hover = HOVER_ARROW_UP;
|
|
}
|
|
}
|
|
// if we're on the bottom line, display the down arrow
|
|
else if (linenum == visible_lines - 1 && itemnum != visible_items - 1)
|
|
{
|
|
draw_arrow(container, 0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
|
|
0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0 ^ ORIENTATION_FLIP_Y);
|
|
|
|
if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y)
|
|
{
|
|
fgcolor = UI_MOUSEOVER_COLOR;
|
|
bgcolor = UI_MOUSEOVER_BG_COLOR;
|
|
highlight(container, line_x0, line_y0, line_x1, line_y1, bgcolor);
|
|
hover = HOVER_ARROW_DOWN;
|
|
}
|
|
}
|
|
|
|
// draw dats text
|
|
else if (pitem.subtext == nullptr)
|
|
{
|
|
mui.draw_text_full(container, itemtext, effective_left, line_y, effective_width, JUSTIFY_LEFT, WRAP_NEVER,
|
|
DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
for (size_t count = visible_items; count < item.size(); count++)
|
|
{
|
|
const ui_menu_item &pitem = item[count];
|
|
const char *itemtext = pitem.text;
|
|
float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
|
|
float line_y0 = line;
|
|
float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
|
|
float line_y1 = line + line_height;
|
|
rgb_t fgcolor = UI_SELECTED_COLOR;
|
|
rgb_t bgcolor = UI_SELECTED_BG_COLOR;
|
|
|
|
if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y && pitem.is_selectable())
|
|
hover = count;
|
|
|
|
if (strcmp(itemtext, MENU_SEPARATOR_ITEM) == 0)
|
|
container->add_line(visible_left, line + 0.5f * line_height, visible_left + visible_width, line + 0.5f * line_height,
|
|
UI_LINE_WIDTH, UI_TEXT_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
|
|
else
|
|
{
|
|
highlight(container, line_x0, line_y0, line_x1, line_y1, bgcolor);
|
|
mui.draw_text_full(container, itemtext, effective_left, line, effective_width, JUSTIFY_CENTER, WRAP_TRUNCATE,
|
|
DRAW_NORMAL, fgcolor, bgcolor, nullptr, nullptr);
|
|
}
|
|
line += line_height;
|
|
}
|
|
|
|
// if there is something special to add, do it by calling the virtual method
|
|
custom_render((selected >= 0 && selected < item.size()) ? item[selected].ref : nullptr, customtop, custombottom, x1, y1, x2, y2);
|
|
|
|
// return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
|
|
visitems = visible_lines - (top_line != 0) - (top_line + visible_lines != visible_items);
|
|
}
|