summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/ui/menu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/ui/menu.cpp')
-rw-r--r--src/frontend/mame/ui/menu.cpp954
1 files changed, 511 insertions, 443 deletions
diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp
index b0a84d3a95c..71e15631879 100644
--- a/src/frontend/mame/ui/menu.cpp
+++ b/src/frontend/mame/ui/menu.cpp
@@ -9,7 +9,6 @@
*********************************************************************/
#include "emu.h"
-
#include "ui/menu.h"
#include "ui/ui.h"
@@ -20,34 +19,29 @@
#include "cheat.h"
#include "mame.h"
+#include "corestr.h"
#include "drivenum.h"
+#include "fileio.h"
#include "rendutil.h"
#include "uiinput.h"
+#include "osdepend.h"
+
#include <algorithm>
#include <cassert>
#include <cmath>
-#include <utility>
+#include <type_traits>
namespace ui {
/***************************************************************************
- GLOBAL VARIABLES
-***************************************************************************/
-
-std::mutex menu::s_global_state_guard;
-menu::global_state_map menu::s_global_states;
-
-/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
-menu::global_state_ptr menu::get_global_state(running_machine &machine)
+menu::global_state &menu::get_global_state(mame_ui_manager &ui)
{
- std::lock_guard<std::mutex> guard(s_global_state_guard);
- auto const it(s_global_states.find(&machine));
- return (it != s_global_states.end()) ? it->second : global_state_ptr();
+ return ui.get_session_data<menu, global_state_wrapper>(ui);
}
//-------------------------------------------------
@@ -75,30 +69,30 @@ bool menu::exclusive_input_pressed(int &iptkey, int key, int repeat)
CORE SYSTEM MANAGEMENT
***************************************************************************/
-menu::global_state::global_state(running_machine &machine, ui_options const &options)
- : widgets_manager(machine)
- , m_machine(machine)
- , m_cleanup_callbacks()
+menu::global_state::global_state(mame_ui_manager &ui)
+ : widgets_manager(ui.machine())
+ , m_ui(ui)
, m_bgrnd_bitmap()
- , m_bgrnd_texture(nullptr, machine.render())
+ , m_bgrnd_texture(nullptr, ui.machine().render())
, m_stack()
, m_free()
+ , m_hide(false)
{
- render_manager &render(machine.render());
+ render_manager &render(ui.machine().render());
// create a texture for main menu background
m_bgrnd_texture.reset(render.texture_alloc(render_texture::hq_scale));
- if (options.use_background_image() && (&machine.system() == &GAME_NAME(___empty)))
+ if (ui.options().use_background_image() && (&ui.machine().system() == &GAME_NAME(___empty)))
{
m_bgrnd_bitmap = std::make_unique<bitmap_argb32>(0, 0);
emu_file backgroundfile(".", OPEN_FLAG_READ);
- if (backgroundfile.open("background.jpg") == osd_file::error::NONE)
+ if (!backgroundfile.open("background.jpg"))
{
render_load_jpeg(*m_bgrnd_bitmap, backgroundfile);
backgroundfile.close();
}
- if (!m_bgrnd_bitmap->valid() && (backgroundfile.open("background.png") == osd_file::error::NONE))
+ if (!m_bgrnd_bitmap->valid() && !backgroundfile.open("background.png"))
{
render_load_png(*m_bgrnd_bitmap, backgroundfile);
backgroundfile.close();
@@ -114,29 +108,20 @@ menu::global_state::global_state(running_machine &machine, ui_options const &opt
menu::global_state::~global_state()
{
- // it shouldn't really be possible to get here with active menus because of reference loops
- assert(!m_stack);
- assert(!m_free);
-
stack_reset();
clear_free_list();
-
- for (auto const &callback : m_cleanup_callbacks)
- callback(m_machine);
-}
-
-
-void menu::global_state::add_cleanup_callback(cleanup_callback &&callback)
-{
- m_cleanup_callbacks.emplace_back(std::move(callback));
}
void menu::global_state::stack_push(std::unique_ptr<menu> &&menu)
{
+ if (m_stack && m_stack->is_active())
+ {
+ m_stack->m_active = false;
+ m_stack->menu_deactivated();
+ }
menu->m_parent = std::move(m_stack);
m_stack = std::move(menu);
- m_stack->reset(reset_options::SELECT_FIRST);
m_stack->machine().ui_input().reset();
}
@@ -145,11 +130,19 @@ void menu::global_state::stack_pop()
{
if (m_stack)
{
+ if (m_stack->is_one_shot())
+ m_hide = true;
+ if (m_stack->is_active())
+ {
+ m_stack->m_active = false;
+ m_stack->menu_deactivated();
+ }
+ m_stack->menu_dismissed();
std::unique_ptr<menu> menu(std::move(m_stack));
m_stack = std::move(menu->m_parent);
menu->m_parent = std::move(m_free);
m_free = std::move(menu);
- m_machine.ui_input().reset();
+ m_ui.machine().ui_input().reset();
}
}
@@ -188,40 +181,45 @@ bool menu::global_state::stack_has_special_main_menu() const
}
-
-
-//-------------------------------------------------
-// init - initialize the menu system
-//-------------------------------------------------
-
-void menu::init(running_machine &machine, ui_options &mopt)
+uint32_t menu::global_state::ui_handler(render_container &container)
{
- // initialize the menu stack
+ // if we have no menus stacked up, start with the main menu
+ if (!m_stack)
+ stack_push(std::make_unique<menu_main>(m_ui, container));
+
+ // ensure topmost menu is active - need a loop because it could push another menu
+ while (m_stack && !m_stack->is_active())
{
- std::lock_guard<std::mutex> guard(s_global_state_guard);
- auto const ins(s_global_states.emplace(&machine, std::make_shared<global_state>(machine, mopt)));
- assert(ins.second); // calling init twice is bad
- if (ins.second)
- machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&menu::exit, &machine)); // add an exit callback to free memory
- else
- ins.first->second->stack_reset();
+ m_stack->m_active = true;
+ m_stack->menu_activated();
}
-}
+ // update the menu state
+ m_hide = false;
+ bool need_update = m_stack && m_stack->do_handle();
-//-------------------------------------------------
-// exit - clean up after ourselves
-//-------------------------------------------------
+ // clear up anything pending being released
+ clear_free_list();
-void menu::exit(running_machine &machine)
-{
- // free menus
- global_state_ptr const state(get_global_state(machine));
- state->stack_reset();
- state->clear_free_list();
+ // if the menus are to be hidden, return a cancel here
+ if (m_ui.is_menu_active() && (m_hide || !m_stack))
+ {
+ if (m_stack)
+ {
+ if (m_stack->is_one_shot())
+ {
+ stack_pop();
+ }
+ else if (m_stack->is_active())
+ {
+ m_stack->m_active = false;
+ m_stack->menu_deactivated();
+ }
+ }
+ return mame_ui_manager::HANDLER_CANCEL;
+ }
- std::lock_guard<std::mutex> guard(s_global_state_guard);
- s_global_states.erase(&machine);
+ return need_update ? mame_ui_manager::HANDLER_UPDATE : 0;
}
@@ -235,30 +233,43 @@ void menu::exit(running_machine &machine)
//-------------------------------------------------
menu::menu(mame_ui_manager &mui, render_container &container)
- : m_selected(0)
- , m_items()
- , m_visible_lines(0)
- , m_visible_items(0)
- , m_global_state(get_global_state(mui.machine()))
- , m_special_main_menu(false)
+ : m_global_state(get_global_state(mui))
, m_ui(mui)
, m_container(container)
, m_parent()
+ , m_heading()
+ , m_items()
+ , m_rebuilding(false)
+ , m_last_size(0, 0)
+ , m_last_aspect(0.0F)
+ , m_line_height(0.0F)
+ , m_gutter_width(0.0F)
+ , m_tb_border(0.0F)
+ , m_lr_border(0.0F)
+ , m_lr_arrow_width(0.0F)
+ , m_ud_arrow_width(0.0F)
+ , m_process_flags(0)
+ , m_selected(0)
+ , m_hover(1)
+ , m_special_main_menu(false)
+ , m_one_shot(false)
+ , m_needs_prev_menu_item(true)
+ , m_active(false)
, m_event()
- , m_customtop(0.0f)
- , m_custombottom(0.0f)
+ , m_customtop(0.0F)
+ , m_custombottom(0.0F)
, m_resetpos(0)
, m_resetref(nullptr)
, m_mouse_hit(false)
, m_mouse_button(false)
- , m_mouse_x(-1.0f)
- , m_mouse_y(-1.0f)
+ , m_mouse_x(-1.0F)
+ , m_mouse_y(-1.0F)
{
- assert(m_global_state); // not calling init is bad
-
reset(reset_options::SELECT_FIRST);
top_line = 0;
+ m_visible_lines = 0;
+ m_visible_items = 0;
}
@@ -289,37 +300,6 @@ void menu::reset(reset_options options)
m_items.clear();
m_visible_items = 0;
m_selected = 0;
-
- // add an item to return
- if (!m_parent)
- {
- item_append(_("Return to Machine"), "", 0, nullptr);
- }
- else if (m_parent->is_special_main_menu())
- {
- if (machine().options().ui() == emu_options::UI_SIMPLE)
- item_append(_("Exit"), "", 0, nullptr);
- else
- item_append(_("Exit"), "", FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, nullptr);
- }
- else
- {
- if (machine().options().ui() != emu_options::UI_SIMPLE && stack_has_special_main_menu())
- item_append(_("Return to Previous Menu"), "", FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW, nullptr);
- else
- item_append(_("Return to Previous Menu"), "", 0, nullptr);
- }
-}
-
-
-//-------------------------------------------------
-// is_special_main_menu - returns whether the
-// menu has special needs
-//-------------------------------------------------
-
-bool menu::is_special_main_menu() const
-{
- return m_special_main_menu;
}
@@ -339,30 +319,13 @@ void menu::set_special_main_menu(bool special)
// end of the menu
//-------------------------------------------------
-void menu::item_append(menu_item item)
-{
- item_append(item.text, item.subtext, item.flags, item.ref, item.type);
-}
-
-//-------------------------------------------------
-// item_append - append a new item to the
-// end of the menu
-//-------------------------------------------------
-
-void menu::item_append(menu_item_type type, uint32_t flags)
+int menu::item_append(menu_item_type type, uint32_t flags)
{
+ assert(menu_item_type::SEPARATOR == type);
if (type == menu_item_type::SEPARATOR)
- item_append(MENU_SEPARATOR_ITEM, "", flags, nullptr, menu_item_type::SEPARATOR);
-}
-
-//-------------------------------------------------
-// item_append - append a new item to the
-// end of the menu
-//-------------------------------------------------
-
-void menu::item_append(const std::string &text, const std::string &subtext, uint32_t flags, void *ref, menu_item_type type)
-{
- item_append(std::string(text), std::string(subtext), flags, ref, type);
+ return item_append(MENU_SEPARATOR_ITEM, flags, nullptr, menu_item_type::SEPARATOR);
+ else
+ return -1;
}
//-------------------------------------------------
@@ -370,39 +333,34 @@ void menu::item_append(const std::string &text, const std::string &subtext, uint
// end of the menu
//-------------------------------------------------
-void menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type)
+int menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags, void *ref, menu_item_type type)
{
- // only allow multiline as the first item
- if ((flags & FLAG_MULTILINE) != 0)
- assert(m_items.size() == 1);
-
- // only allow a single multi-line item
- else if (m_items.size() >= 2)
- assert((m_items[0].flags & FLAG_MULTILINE) == 0);
+ assert(m_rebuilding);
// allocate a new item and populate it
- menu_item pitem;
- pitem.text = std::move(text);
- pitem.subtext = std::move(subtext);
- pitem.flags = flags;
- pitem.ref = ref;
- pitem.type = type;
+ menu_item pitem(type, ref, flags);
+ pitem.set_text(std::move(text));
+ pitem.set_subtext(std::move(subtext));
// append to array
auto index = m_items.size();
- if (!m_items.empty())
+ if (!m_items.empty() && m_needs_prev_menu_item)
{
m_items.emplace(m_items.end() - 1, std::move(pitem));
--index;
}
else
+ {
m_items.emplace_back(std::move(pitem));
+ }
// update the selection if we need to
- if (m_resetpos == index || (m_resetref != nullptr && m_resetref == ref))
+ if ((m_resetpos == index) || (m_resetref && (m_resetref == ref)))
m_selected = index;
if (m_resetpos == (m_items.size() - 1))
m_selected = m_items.size() - 1;
+
+ return int(std::make_signed_t<decltype(index)>(index));
}
@@ -411,25 +369,26 @@ void menu::item_append(std::string &&text, std::string &&subtext, uint32_t flags
// item to the end of the menu
//-------------------------------------------------
-void menu::item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type)
+int menu::item_append_on_off(const std::string &text, bool state, uint32_t flags, void *ref, menu_item_type type)
{
if (flags & FLAG_DISABLE)
ref = nullptr;
else
flags |= state ? FLAG_LEFT_ARROW : FLAG_RIGHT_ARROW;
- item_append(std::string(text), state ? _("On") : _("Off"), flags, ref, type);
+ return item_append(std::string(text), state ? _("On") : _("Off"), flags, ref, type);
}
//-------------------------------------------------
-// repopulate - repopulate menu items
+// set_custom_space - set space required for
+// custom rendering above and below menu
//-------------------------------------------------
-void menu::repopulate(reset_options options)
+void menu::set_custom_space(float top, float bottom)
{
- reset(options);
- populate(m_customtop, m_custombottom);
+ m_customtop = top;
+ m_custombottom = bottom;
}
@@ -438,7 +397,7 @@ void menu::repopulate(reset_options options)
// and returning any interesting events
//-------------------------------------------------
-const menu::event *menu::process(uint32_t flags, float x0, float y0)
+std::pair<const menu::event *, bool> menu::process()
{
// reset the event
m_event.iptkey = IPT_INVALID;
@@ -446,33 +405,45 @@ const menu::event *menu::process(uint32_t flags, float x0, float y0)
// first make sure our selection is valid
validate_selection(1);
- // draw the menu
- if (m_items.size() > 1 && (m_items[0].flags & FLAG_MULTILINE) != 0)
- draw_text_box();
- else
- draw(flags);
+ // if we're not running the emulation, draw parent menus in the background
+ auto const draw_parent =
+ [] (auto &self, menu *parent) -> bool
+ {
+ if (!parent || !(parent->is_special_main_menu() || self(self, parent->m_parent.get())))
+ return false;
+ else
+ parent->draw(PROCESS_NOINPUT);
+ return true;
+ };
+ if (draw_parent(draw_parent, m_parent.get()))
+ container().add_rect(0.0F, 0.0F, 1.0F, 1.0F, rgb_t(114, 0, 0, 0), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ // draw the menu proper
+ // FIXME: this should happen after processing events to fix the egregious latency
+ // The main thing preventing this is that mouse handling is mixed up with drawing
+ draw(m_process_flags);
// process input
- if (!(flags & PROCESS_NOKEYS) && !(flags & PROCESS_NOINPUT))
+ if (!(m_process_flags & (PROCESS_NOKEYS | PROCESS_NOINPUT)))
{
// read events
- handle_events(flags, m_event);
+ handle_events(m_process_flags, m_event);
// handle the keys if we don't already have an event
if (m_event.iptkey == IPT_INVALID)
- handle_keys(flags, m_event.iptkey);
+ handle_keys(m_process_flags, m_event.iptkey);
}
// update the selected item in the event
if ((m_event.iptkey != IPT_INVALID) && selection_valid())
{
m_event.itemref = get_selection_ref();
- m_event.type = m_items[m_selected].type;
- return &m_event;
+ m_event.item = &m_items[m_selected];
+ return std::make_pair(&m_event, false);
}
else
{
- return nullptr;
+ return std::make_pair(nullptr, false);
}
}
@@ -487,7 +458,7 @@ void menu::set_selection(void *selected_itemref)
m_selected = -1;
for (int itemnum = 0; itemnum < m_items.size(); itemnum++)
{
- if (m_items[itemnum].ref == selected_itemref)
+ if (m_items[itemnum].ref() == selected_itemref)
{
m_selected = itemnum;
break;
@@ -508,23 +479,23 @@ void menu::set_selection(void *selected_itemref)
void menu::draw(uint32_t flags)
{
// first draw the FPS counter
+ // FIXME: provide a way to do this in the UI manager itself while menus are on-screen
if (ui().show_fps_counter())
{
- ui().draw_text_full(container(), machine().video().speed_text().c_str(), 0.0f, 0.0f, 1.0f,
- ui::text_layout::RIGHT, ui::text_layout::WORD, mame_ui_manager::OPAQUE_, rgb_t::white(), rgb_t::black(), nullptr, nullptr);
+ ui().draw_text_full(
+ container(),
+ machine().video().speed_text(),
+ 0.0F, 0.0F, 1.0F,
+ text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD,
+ mame_ui_manager::OPAQUE_, rgb_t::white(), rgb_t::black(),
+ nullptr, nullptr);
}
bool const customonly = (flags & PROCESS_CUSTOM_ONLY);
- bool const noimage = (flags & PROCESS_NOIMAGE);
bool const noinput = (flags & PROCESS_NOINPUT);
- float const aspect = machine().render().ui_aspect(&container());
- float const line_height = ui().get_line_height();
- float const lr_arrow_width = 0.4f * line_height * aspect;
- float const ud_arrow_width = line_height * aspect;
- float const gutter_width = lr_arrow_width * 1.3f;
- float const lr_border = ui().box_lr_border() * aspect;
-
- if (&machine().system() == &GAME_NAME(___empty) && !noimage)
+ float const max_width = 1.0F - ((lr_border() + (machine().render().ui_aspect(&container()) * UI_LINE_WIDTH)) * 2.0F);
+
+ if (is_special_main_menu())
draw_background();
// compute the width and height of the full menu
@@ -533,56 +504,86 @@ void menu::draw(uint32_t flags)
for (auto const &pitem : m_items)
{
// compute width of left hand side
- float total_width = gutter_width + ui().get_string_width(pitem.text.c_str()) + gutter_width;
+ float total_width = gutter_width() + get_string_width(pitem.text()) + gutter_width();
// add in width of right hand side
- if (!pitem.subtext.empty())
- total_width += 2.0f * gutter_width + ui().get_string_width(pitem.subtext.c_str());
+ if (!pitem.subtext().empty())
+ total_width += 2.0F * gutter_width() + get_string_width(pitem.subtext());
+ else if (pitem.flags() & FLAG_UI_HEADING)
+ total_width += 4.0F * ud_arrow_width();
// track the maximum
- if (total_width > visible_width)
- visible_width = total_width;
+ visible_width = std::max(total_width, visible_width);
// track the height as well
- visible_main_menu_height += line_height;
+ visible_main_menu_height += line_height();
+ }
+
+ // lay out the heading if present
+ std::optional<text_layout> heading_layout;
+ if (m_heading)
+ {
+ heading_layout.emplace(create_layout(max_width - (gutter_width() * 2.0F), text_layout::text_justify::CENTER));
+ heading_layout->add_text(*m_heading, ui().colors().text_color());
+
+ // readjust visible width if heading width exceeds that of the menu
+ visible_width = std::max(gutter_width() + heading_layout->actual_width() + gutter_width(), visible_width);
}
// account for extra space at the top and bottom
- float const visible_extra_menu_height = m_customtop + m_custombottom;
+ float const top_extra_menu_height = m_customtop + (heading_layout ? (heading_layout->actual_height() + (tb_border() * 3.0F)) : 0.0F);
+ float const visible_extra_menu_height = top_extra_menu_height + m_custombottom;
// add a little bit of slop for rounding
- visible_width += 0.01f;
- visible_main_menu_height += 0.01f;
+ 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 * lr_border > 1.0f)
- visible_width = 1.0f - 2.0f * lr_border;
+ visible_width = std::min(visible_width, max_width);
// 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;
+ if (visible_main_menu_height + visible_extra_menu_height + 2.0F * tb_border() > 1.0F)
+ visible_main_menu_height = 1.0F - 2.0F * tb_border() - visible_extra_menu_height;
- m_visible_lines = std::min(int(std::floor(visible_main_menu_height / line_height)), int(unsigned(m_items.size())));
- visible_main_menu_height = float(m_visible_lines) * line_height;
+ m_visible_lines = std::min(int(std::floor(visible_main_menu_height / line_height())), int(unsigned(m_items.size())));
+ visible_main_menu_height = float(m_visible_lines) * line_height();
// compute top/left of inner menu area by centering
- float const visible_left = (1.0f - visible_width) * 0.5f;
- float const visible_top = ((1.0f - visible_main_menu_height - visible_extra_menu_height) * 0.5f) + m_customtop;
+ float const visible_left = (1.0F - visible_width) * 0.5F;
+ float const visible_top = std::round((((1.0F - visible_main_menu_height - visible_extra_menu_height) * 0.5F) + top_extra_menu_height) * float(m_last_size.second)) / float(m_last_size.second);
// first add us a box
- float const x1 = visible_left - lr_border;
- float const y1 = visible_top - ui().box_tb_border();
- float const x2 = visible_left + visible_width + lr_border;
- float const y2 = visible_top + visible_main_menu_height + ui().box_tb_border();
+ float const x1 = visible_left - lr_border();
+ float const y1 = visible_top - tb_border();
+ float const x2 = visible_left + visible_width + lr_border();
+ float const y2 = visible_top + visible_main_menu_height + tb_border();
if (!customonly)
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ {
+ if (heading_layout)
+ {
+ ui().draw_outlined_box(
+ container(),
+ x1, y1 - top_extra_menu_height,
+ x2, y1 - m_customtop - tb_border(),
+ UI_GREEN_COLOR);
+ heading_layout->emit(container(), (1.0F - heading_layout->width()) * 0.5F, y1 - top_extra_menu_height + tb_border());
+ }
+ ui().draw_outlined_box(
+ container(),
+ x1, y1,
+ x2, y2,
+ ui().colors().background_color());
+ }
+
+ if ((m_selected >= (top_line + m_visible_lines)) || (m_selected < (top_line + 1)))
+ top_line = m_selected - (m_visible_lines / 2);
if (top_line < 0 || is_first_selected())
top_line = 0;
- if (m_selected >= (top_line + m_visible_lines))
- top_line = m_selected - (m_visible_lines / 2);
- if ((top_line > (m_items.size() - m_visible_lines)) || is_last_selected())
+ else if ((top_line > (m_items.size() - m_visible_lines)) || is_last_selected())
top_line = m_items.size() - m_visible_lines;
+ else if (m_selected >= (top_line + m_visible_lines - 2))
+ top_line = m_selected - m_visible_lines + ((m_selected == (m_items.size() - 1)) ? 1: 2);
// if scrolling, show arrows
bool const show_top_arrow((m_items.size() > m_visible_lines) && !first_item_visible());
@@ -592,8 +593,8 @@ void menu::draw(uint32_t flags)
m_visible_items = m_visible_lines - (show_top_arrow ? 1 : 0) - (show_bottom_arrow ? 1 : 0);
// determine effective positions taking into account the hilighting arrows
- float const effective_width = visible_width - 2.0f * gutter_width;
- float const effective_left = visible_left + gutter_width;
+ float const effective_width = visible_width - 2.0F * gutter_width();
+ float const effective_left = visible_left + gutter_width();
// locate mouse
if (!customonly && !noinput)
@@ -604,36 +605,47 @@ void menu::draw(uint32_t flags)
// loop over visible lines
m_hover = m_items.size() + 1;
bool selected_subitem_too_big = false;
- float const line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
- float const line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
+ float const line_x0 = x1 + 0.5F * UI_LINE_WIDTH;
+ float const line_x1 = x2 - 0.5F * UI_LINE_WIDTH;
if (!customonly)
{
for (int linenum = 0; linenum < m_visible_lines; linenum++)
{
auto const itemnum = top_line + linenum;
menu_item const &pitem = m_items[itemnum];
- char const *const itemtext = pitem.text.c_str();
+ std::string_view const itemtext = pitem.text();
rgb_t fgcolor = ui().colors().text_color();
rgb_t bgcolor = ui().colors().text_bg_color();
rgb_t fgcolor2 = ui().colors().subitem_color();
rgb_t fgcolor3 = ui().colors().clone_color();
- float const line_y0 = visible_top + (float)linenum * line_height;
- float const line_y1 = line_y0 + line_height;
+ float const line_y0 = visible_top + (float)linenum * line_height();
+ float const line_y1 = line_y0 + line_height();
+
+ // work out what we're dealing with
+ bool const uparrow = !linenum && show_top_arrow;
+ bool const downarrow = (linenum == (m_visible_lines - 1)) && show_bottom_arrow;
// set the hover if this is our item
- if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
- m_hover = itemnum;
+ bool const hovered = mouse_in_rect(line_x0, line_y0, line_x1, line_y1);
+ if (hovered)
+ {
+ if (uparrow)
+ m_hover = HOVER_ARROW_UP;
+ else if (downarrow)
+ m_hover = HOVER_ARROW_DOWN;
+ else if (is_selectable(pitem))
+ m_hover = itemnum;
+ }
- // if we're selected, draw with a different background
if (is_selected(itemnum))
{
+ // if we're selected, draw with a different background
fgcolor = fgcolor2 = fgcolor3 = ui().colors().selected_color();
bgcolor = ui().colors().selected_bg_color();
}
-
- // else if the mouse is over this item, draw with a different background
- else if (itemnum == m_hover)
+ else if (hovered && (uparrow || downarrow || is_selectable(pitem)))
{
+ // else if the mouse is over this item, draw with a different background
fgcolor = fgcolor2 = fgcolor3 = ui().colors().mouseover_color();
bgcolor = ui().colors().mouseover_bg_color();
}
@@ -642,77 +654,89 @@ void menu::draw(uint32_t flags)
if (bgcolor != ui().colors().text_bg_color())
highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
- if (linenum == 0 && show_top_arrow)
+ if (uparrow)
{
// if we're on the top line, display the up arrow
draw_arrow(
- 0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
- line_y0 + 0.25f * line_height,
- 0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
- line_y0 + 0.75f * line_height,
- fgcolor,
- ROT0);
- if (m_hover == itemnum)
- m_hover = HOVER_ARROW_UP;
+ 0.5F * (x1 + x2) - 0.5F * ud_arrow_width(),
+ line_y0 + 0.25F * line_height(),
+ 0.5F * (x1 + x2) + 0.5F * ud_arrow_width(),
+ line_y0 + 0.75F * line_height(),
+ fgcolor,
+ ROT0);
}
- else if (linenum == m_visible_lines - 1 && show_bottom_arrow)
+ else if (downarrow)
{
// if we're on the bottom line, display the down arrow
draw_arrow(
- 0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
- line_y0 + 0.25f * line_height,
- 0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
- line_y0 + 0.75f * line_height,
- fgcolor,
- ROT0 ^ ORIENTATION_FLIP_Y);
- if (m_hover == itemnum)
- m_hover = HOVER_ARROW_DOWN;
+ 0.5F * (x1 + x2) - 0.5F * ud_arrow_width(),
+ line_y0 + 0.25F * line_height(),
+ 0.5F * (x1 + x2) + 0.5F * ud_arrow_width(),
+ line_y0 + 0.75F * line_height(),
+ fgcolor,
+ ROT0 ^ ORIENTATION_FLIP_Y);
}
- else if (pitem.type == menu_item_type::SEPARATOR)
+ else if (pitem.type() == menu_item_type::SEPARATOR)
{
// if we're just a divider, draw a line
- container().add_line(visible_left, line_y0 + 0.5f * line_height, visible_left + visible_width, line_y0 + 0.5f * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_line(visible_left, line_y0 + 0.5F * line_height(), visible_left + visible_width, line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
- else if (pitem.subtext.empty())
+ else if (pitem.subtext().empty())
{
// if we don't have a subitem, just draw the string centered
- if (pitem.flags & FLAG_UI_HEADING)
+ if (pitem.flags() & FLAG_UI_HEADING)
{
- float heading_width = ui().get_string_width(itemtext);
- container().add_line(visible_left, line_y0 + 0.5f * line_height, visible_left + ((visible_width - heading_width) / 2) - lr_border, line_y0 + 0.5f * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
- container().add_line(visible_left + visible_width - ((visible_width - heading_width) / 2) + lr_border, line_y0 + 0.5f * line_height, visible_left + visible_width, line_y0 + 0.5f * line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ float heading_width = get_string_width(itemtext);
+ container().add_line(visible_left, line_y0 + 0.5F * line_height(), visible_left + ((visible_width - heading_width) / 2) - lr_border(), line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_line(visible_left + visible_width - ((visible_width - heading_width) / 2) + lr_border(), line_y0 + 0.5F * line_height(), visible_left + visible_width, line_y0 + 0.5F * line_height(), UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
- ui().draw_text_full(container(), itemtext, effective_left, line_y0, effective_width,
- ui::text_layout::CENTER, ui::text_layout::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr);
+ ui().draw_text_full(
+ container(),
+ itemtext,
+ effective_left, line_y0, effective_width,
+ text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, fgcolor, bgcolor,
+ nullptr, nullptr,
+ line_height());
}
else
{
// otherwise, draw the item on the left and the subitem text on the right
- bool const subitem_invert(pitem.flags & FLAG_INVERT);
- char const *subitem_text(pitem.subtext.c_str());
+ bool const subitem_invert(pitem.flags() & FLAG_INVERT);
float item_width, subitem_width;
// draw the left-side text
- ui().draw_text_full(container(), itemtext, effective_left, line_y0, effective_width,
- ui::text_layout::LEFT, ui::text_layout::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor, &item_width, nullptr);
-
- if (pitem.flags & FLAG_COLOR_BOX)
+ ui().draw_text_full(
+ container(),
+ itemtext,
+ effective_left, line_y0, effective_width,
+ text_layout::text_justify::LEFT, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, fgcolor, bgcolor,
+ &item_width, nullptr,
+ line_height());
+
+ if (pitem.flags() & FLAG_COLOR_BOX)
{
- rgb_t color = rgb_t((uint32_t)strtoul(subitem_text, nullptr, 16));
+ rgb_t color = rgb_t((uint32_t)strtoul(pitem.subtext().c_str(), nullptr, 16));
// give 2 spaces worth of padding
- subitem_width = ui().get_string_width("FF00FF00");
+ subitem_width = get_string_width("FF00FF00");
- ui().draw_outlined_box(container(), effective_left + effective_width - subitem_width, line_y0,
- effective_left + effective_width, line_y1, color);
+ ui().draw_outlined_box(
+ container(),
+ effective_left + effective_width - subitem_width, line_y0 + (UI_LINE_WIDTH * 2.0F),
+ effective_left + effective_width, line_y1 - (UI_LINE_WIDTH * 2.0F),
+ color);
}
else
{
+ std::string_view subitem_text(pitem.subtext());
+
// give 2 spaces worth of padding
- item_width += 2.0f * gutter_width;
+ item_width += 2.0F * gutter_width();
// if the subitem doesn't fit here, display dots
- if (ui().get_string_width(subitem_text) > effective_width - item_width)
+ if (get_string_width(subitem_text) > effective_width - item_width)
{
subitem_text = "...";
if (is_selected(itemnum))
@@ -720,40 +744,48 @@ void menu::draw(uint32_t flags)
}
// customize subitem text color
- if (!core_stricmp(subitem_text, _("On")))
+ if (!core_stricmp(pitem.subtext(), _("On")))
fgcolor2 = rgb_t(0x00,0xff,0x00);
- if (!core_stricmp(subitem_text, _("Off")))
+ if (!core_stricmp(pitem.subtext(), _("Off")))
fgcolor2 = rgb_t(0xff,0x00,0x00);
- if (!core_stricmp(subitem_text, _("Auto")))
+ if (!core_stricmp(pitem.subtext(), _("Auto")))
fgcolor2 = rgb_t(0xff,0xff,0x00);
// draw the subitem right-justified
- ui().draw_text_full(container(), subitem_text, effective_left + item_width, line_y0, effective_width - item_width,
- ui::text_layout::RIGHT, ui::text_layout::TRUNCATE, mame_ui_manager::NORMAL, subitem_invert ? fgcolor3 : fgcolor2, bgcolor, &subitem_width, nullptr);
+ ui().draw_text_full(
+ container(),
+ subitem_text,
+ effective_left + item_width, line_y0, effective_width - item_width,
+ text_layout::text_justify::RIGHT, text_layout::word_wrapping::TRUNCATE,
+ mame_ui_manager::NORMAL, subitem_invert ? fgcolor3 : fgcolor2, bgcolor,
+ &subitem_width, nullptr,
+ line_height());
}
// apply arrows
- if (is_selected(itemnum) && (pitem.flags & FLAG_LEFT_ARROW))
+ if (is_selected(itemnum) && (pitem.flags() & FLAG_LEFT_ARROW))
{
+ float const l = effective_left + effective_width - subitem_width - gutter_width();
+ float const r = l + lr_arrow_width();
draw_arrow(
- effective_left + effective_width - subitem_width - gutter_width,
- line_y0 + 0.1f * line_height,
- effective_left + effective_width - subitem_width - gutter_width + lr_arrow_width,
- line_y0 + 0.9f * line_height,
+ l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height(),
fgcolor,
ROT90 ^ ORIENTATION_FLIP_X);
+ if (mouse_in_rect(l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height()))
+ m_hover = HOVER_UI_LEFT;
}
- if (is_selected(itemnum) && (pitem.flags & FLAG_RIGHT_ARROW))
+ if (is_selected(itemnum) && (pitem.flags() & FLAG_RIGHT_ARROW))
{
+ float const r = effective_left + effective_width + gutter_width();
+ float const l = r - lr_arrow_width();
draw_arrow(
- effective_left + effective_width + gutter_width - lr_arrow_width,
- line_y0 + 0.1f * line_height,
- effective_left + effective_width + gutter_width,
- line_y0 + 0.9f * line_height,
- fgcolor,
- ROT90);
+ l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height(),
+ fgcolor,
+ ROT90);
+ if (mouse_in_rect(l, line_y0 + 0.1F * line_height(), r, line_y0 + 0.9F * line_height()))
+ m_hover = HOVER_UI_RIGHT;
}
}
}
@@ -763,104 +795,58 @@ void menu::draw(uint32_t flags)
if (selected_subitem_too_big)
{
menu_item const &pitem = selected_item();
- bool const subitem_invert(pitem.flags & FLAG_INVERT);
+ bool const subitem_invert(pitem.flags() & FLAG_INVERT);
auto const linenum = m_selected - top_line;
- float const line_y = visible_top + (float)linenum * line_height;
- float target_width, target_height;
+ float const line_y = visible_top + float(linenum) * line_height();
// compute the multi-line target width/height
- ui().draw_text_full(container(), pitem.subtext.c_str(), 0, 0, visible_width * 0.75f,
- ui::text_layout::RIGHT, ui::text_layout::WORD, mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(), &target_width, &target_height);
+ auto const [target_width, target_height] = get_text_dimensions(
+ pitem.subtext(),
+ 0, 0, visible_width * 0.75F,
+ text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD);
// determine the target location
- float const target_x = visible_left + visible_width - target_width - lr_border;
- float 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();
+ float const target_x = visible_left + visible_width - target_width - lr_border();
+ float target_y = line_y + line_height() + tb_border();
+ if (target_y + target_height + tb_border() > visible_main_menu_height)
+ target_y = line_y - target_height - tb_border();
// add a box around that
- ui().draw_outlined_box(container(), target_x - lr_border,
- target_y - ui().box_tb_border(),
- target_x + target_width + lr_border,
- target_y + target_height + ui().box_tb_border(),
+ ui().draw_outlined_box(
+ container(),
+ target_x - lr_border(), target_y - tb_border(),
+ target_x + target_width + lr_border(), target_y + target_height + tb_border(),
subitem_invert ? ui().colors().selected_bg_color() : ui().colors().background_color());
- ui().draw_text_full(container(), pitem.subtext.c_str(), target_x, target_y, target_width,
- ui::text_layout::RIGHT, ui::text_layout::WORD, mame_ui_manager::NORMAL, ui().colors().selected_color(), ui().colors().selected_bg_color(), nullptr, nullptr);
+ ui().draw_text_full(
+ container(),
+ pitem.subtext(),
+ target_x, target_y, target_width,
+ text_layout::text_justify::RIGHT, text_layout::word_wrapping::WORD,
+ mame_ui_manager::NORMAL, ui().colors().selected_color(), ui().colors().selected_bg_color(),
+ nullptr, nullptr);
}
// if there is something special to add, do it by calling the virtual method
custom_render(get_selection_ref(), m_customtop, m_custombottom, x1, y1, x2, y2);
}
-void menu::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu::recompute_metrics(uint32_t width, uint32_t height, float aspect)
{
-}
+ float const ui_line_height = ui().get_line_height();
-//-------------------------------------------------
-// draw_text_box - draw a multiline
-// word-wrapped text box with a menu item at the
-// bottom
-//-------------------------------------------------
+ // force whole pixels for line height, gutters and borders
+ m_line_height = std::floor(ui_line_height * float(height)) / float(height);
+ m_gutter_width = std::floor(0.5F * ui_line_height * aspect * float(width)) / float(width);
+ m_tb_border = std::floor(ui().box_tb_border() * float(height)) / float(height);
+ m_lr_border = std::floor(ui().box_lr_border() * aspect * float(width)) / float(width);
-void menu::draw_text_box()
+ m_lr_arrow_width = 0.4F * m_line_height * aspect;
+ m_ud_arrow_width = m_line_height * aspect;
+}
+
+void menu::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
- const char *text = m_items[0].text.c_str();
- const char *backtext = m_items[1].text.c_str();
- float const aspect = machine().render().ui_aspect(&container());
- float line_height = ui().get_line_height();
- float lr_arrow_width = 0.4f * line_height * aspect;
- float gutter_width = lr_arrow_width;
- float const lr_border = ui().box_lr_border() * aspect;
- float target_width, target_height, prior_width;
- float target_x, target_y;
-
- // compute the multi-line target width/height
- ui().draw_text_full(container(), text, 0, 0, 1.0f - 2.0f * lr_border - 2.0f * gutter_width,
- ui::text_layout::LEFT, ui::text_layout::WORD, mame_ui_manager::NONE, rgb_t::white(), rgb_t::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 = ui().get_string_width(backtext) + 2.0f * gutter_width;
- target_width = std::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 < lr_border + gutter_width)
- target_x = lr_border + gutter_width;
- if (target_x + target_width + gutter_width + lr_border > 1.0f)
- target_x = 1.0f - 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
- ui().draw_outlined_box(container(), target_x - lr_border - gutter_width,
- target_y - ui().box_tb_border(),
- target_x + target_width + gutter_width + lr_border,
- target_y + target_height + ui().box_tb_border(),
- (m_items[0].flags & FLAG_REDTEXT) ? UI_RED_COLOR : ui().colors().background_color());
- ui().draw_text_full(container(), text, target_x, target_y, target_width,
- ui::text_layout::LEFT, ui::text_layout::WORD, mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(), nullptr, nullptr);
-
- // draw the "return to prior menu" text with a hilight behind it
- highlight(
- 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().colors().selected_bg_color());
- ui().draw_text_full(container(), backtext, target_x, target_y + target_height - line_height, target_width,
- ui::text_layout::CENTER, ui::text_layout::TRUNCATE, mame_ui_manager::NORMAL, ui().colors().selected_color(), ui().colors().selected_bg_color(), nullptr, nullptr);
-
- // artificially set the hover to the last item so a double-click exits
- m_hover = m_items.size() - 1;
}
@@ -891,8 +877,8 @@ void menu::ignore_mouse()
{
m_mouse_hit = false;
m_mouse_button = false;
- m_mouse_x = -1.0f;
- m_mouse_y = -1.0f;
+ m_mouse_x = -1.0F;
+ m_mouse_y = -1.0F;
}
@@ -916,33 +902,51 @@ void menu::handle_events(uint32_t flags, event &ev)
if (custom_mouse_down())
return;
- if ((flags & PROCESS_ONLYCHAR) == 0)
+ if (!(flags & PROCESS_ONLYCHAR))
{
if (m_hover >= 0 && m_hover < m_items.size())
+ {
m_selected = m_hover;
+ }
else if (m_hover == HOVER_ARROW_UP)
{
- if ((flags & FLAG_UI_DATS) != 0)
+ if (flags & PROCESS_CUSTOM_NAV)
+ {
+ ev.iptkey = IPT_UI_PAGE_UP;
+ stop = true;
+ }
+ else
{
+ m_selected -= m_visible_items;
+ if (m_selected < 0)
+ m_selected = 0;
top_line -= m_visible_items - (last_item_visible() ? 1 : 0);
- return;
}
- m_selected -= m_visible_items;
- if (m_selected < 0)
- m_selected = 0;
- top_line -= m_visible_items - (last_item_visible() ? 1 : 0);
}
else if (m_hover == HOVER_ARROW_DOWN)
{
- if ((flags & FLAG_UI_DATS) != 0)
+ if (flags & PROCESS_CUSTOM_NAV)
{
+ ev.iptkey = IPT_UI_PAGE_DOWN;
+ stop = true;
+ }
+ else
+ {
+ m_selected += m_visible_lines - 2 + is_first_selected();
+ if (m_selected > m_items.size() - 1)
+ m_selected = m_items.size() - 1;
top_line += m_visible_lines - 2;
- return;
}
- m_selected += m_visible_lines - 2 + is_first_selected();
- if (m_selected > m_items.size() - 1)
- m_selected = m_items.size() - 1;
- top_line += m_visible_lines - 2;
+ }
+ else if (m_hover == HOVER_UI_LEFT)
+ {
+ ev.iptkey = IPT_UI_LEFT;
+ stop = true;
+ }
+ else if (m_hover == HOVER_UI_RIGHT)
+ {
+ ev.iptkey = IPT_UI_RIGHT;
+ stop = true;
}
}
break;
@@ -953,10 +957,12 @@ void menu::handle_events(uint32_t flags, event &ev)
{
m_selected = m_hover;
ev.iptkey = IPT_UI_SELECT;
- if (is_last_selected())
+ if (is_last_selected() && m_needs_prev_menu_item)
{
- ev.iptkey = IPT_UI_CANCEL;
+ ev.iptkey = IPT_UI_BACK;
stack_pop();
+ if (is_special_main_menu())
+ machine().schedule_exit();
}
stop = true;
}
@@ -964,17 +970,14 @@ void menu::handle_events(uint32_t flags, event &ev)
// caught scroll event
case ui_event::type::MOUSE_WHEEL:
- if (!(flags & PROCESS_ONLYCHAR))
+ if (!custom_mouse_scroll((0 < local_menu_event.zdelta) ? -local_menu_event.num_lines : local_menu_event.num_lines) && !(flags & (PROCESS_ONLYCHAR | PROCESS_CUSTOM_NAV)))
{
if (local_menu_event.zdelta > 0)
{
- if ((flags & FLAG_UI_DATS) != 0)
- {
- top_line -= local_menu_event.num_lines;
- return;
- }
if (is_first_selected())
+ {
select_last_item();
+ }
else
{
m_selected -= local_menu_event.num_lines;
@@ -986,13 +989,10 @@ void menu::handle_events(uint32_t flags, event &ev)
}
else
{
- if ((flags & FLAG_UI_DATS))
- {
- top_line += local_menu_event.num_lines;
- return;
- }
if (is_last_selected())
+ {
select_first_item();
+ }
else
{
m_selected += local_menu_event.num_lines;
@@ -1027,8 +1027,7 @@ void menu::handle_events(uint32_t flags, event &ev)
void menu::handle_keys(uint32_t flags, int &iptkey)
{
- bool ignorepause = stack_has_special_main_menu();
- int code;
+ bool const ignorepause = (flags & PROCESS_IGNOREPAUSE) || stack_has_special_main_menu();
// bail if no items
if (m_items.empty())
@@ -1037,23 +1036,40 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// if we hit select, return true or pop the stack, depending on the item
if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0))
{
- if (is_last_selected())
+ if (is_last_selected() && m_needs_prev_menu_item)
{
- iptkey = IPT_UI_CANCEL;
+ iptkey = IPT_INVALID;
stack_pop();
+ if (is_special_main_menu())
+ machine().schedule_exit();
}
return;
}
+ // UI configure hides the menus
+ if (!(flags & PROCESS_NOKEYS) && exclusive_input_pressed(iptkey, IPT_UI_MENU, 0) && !m_global_state.stack_has_special_main_menu())
+ {
+ if (is_one_shot())
+ stack_pop();
+ else
+ m_global_state.hide_menu();
+ return;
+ }
+
// bail out
- if ((flags & PROCESS_ONLYCHAR))
+ if (flags & PROCESS_ONLYCHAR)
return;
- // hitting cancel also pops the stack
- if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
+ // hitting back also pops the stack
+ if (exclusive_input_pressed(iptkey, IPT_UI_BACK, 0))
{
- if (!menu_has_search_active())
+ if (!custom_ui_back())
+ {
+ iptkey = IPT_INVALID;
stack_pop();
+ if (is_special_main_menu())
+ machine().schedule_exit();
+ }
return;
}
@@ -1061,13 +1077,10 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
validate_selection(1);
// swallow left/right keys if they are not appropriate
- bool ignoreleft = ((selected_item().flags & FLAG_LEFT_ARROW) == 0);
- bool ignoreright = ((selected_item().flags & FLAG_RIGHT_ARROW) == 0);
+ bool const ignoreleft = !(flags & PROCESS_LR_ALWAYS) && !(selected_item().flags() & FLAG_LEFT_ARROW);
+ bool const ignoreright = !(flags & PROCESS_LR_ALWAYS) && !(selected_item().flags() & FLAG_RIGHT_ARROW);
- if ((m_items[0].flags & FLAG_UI_DATS))
- ignoreleft = ignoreright = false;
-
- // accept left/right keys as-is with repeat
+ // accept left/right/prev/next keys as-is with repeat if appropriate
if (!ignoreleft && exclusive_input_pressed(iptkey, IPT_UI_LEFT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
return;
if (!ignoreright && exclusive_input_pressed(iptkey, IPT_UI_RIGHT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
@@ -1076,13 +1089,14 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// up backs up by one item
if (exclusive_input_pressed(iptkey, IPT_UI_UP, 6))
{
- if ((m_items[0].flags & FLAG_UI_DATS))
+ if (flags & PROCESS_CUSTOM_NAV)
{
- top_line--;
return;
}
- if (is_first_selected())
+ else if (is_first_selected())
+ {
select_last_item();
+ }
else
{
--m_selected;
@@ -1096,13 +1110,14 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// down advances by one item
if (exclusive_input_pressed(iptkey, IPT_UI_DOWN, 6))
{
- if ((m_items[0].flags & FLAG_UI_DATS))
+ if (flags & PROCESS_CUSTOM_NAV)
{
- top_line++;
return;
}
- if (is_last_selected())
+ else if (is_last_selected())
+ {
select_first_item();
+ }
else
{
++m_selected;
@@ -1116,6 +1131,8 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// page up backs up by m_visible_items
if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_UP, 6))
{
+ if (flags & PROCESS_CUSTOM_NAV)
+ return;
m_selected -= m_visible_items;
top_line -= m_visible_items - (last_item_visible() ? 1 : 0);
if (m_selected < 0)
@@ -1126,6 +1143,8 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// page down advances by m_visible_items
if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_DOWN, 6))
{
+ if (flags & PROCESS_CUSTOM_NAV)
+ return;
m_selected += m_visible_lines - 2 + is_first_selected();
top_line += m_visible_lines - 2;
@@ -1136,11 +1155,19 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
// home goes to the start
if (exclusive_input_pressed(iptkey, IPT_UI_HOME, 0))
+ {
+ if (flags & PROCESS_CUSTOM_NAV)
+ return;
select_first_item();
+ }
// end goes to the last
if (exclusive_input_pressed(iptkey, IPT_UI_END, 0))
+ {
+ if (flags & PROCESS_CUSTOM_NAV)
+ return;
select_last_item();
+ }
// pause enables/disables pause
if (!ignorepause && exclusive_input_pressed(iptkey, IPT_UI_PAUSE, 0))
@@ -1151,17 +1178,26 @@ void menu::handle_keys(uint32_t flags, int &iptkey)
machine().pause();
}
- // handle a toggle cheats request
- if (machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0))
- mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled());
-
// see if any other UI keys are pressed
if (iptkey == IPT_INVALID)
{
- for (code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++)
+ for (int 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;
+ switch (code)
+ {
+ case IPT_UI_LEFT:
+ if (ignoreleft)
+ continue;
+ break;
+ case IPT_UI_RIGHT:
+ if (ignoreright)
+ continue;
+ break;
+ case IPT_UI_PAUSE:
+ if (ignorepause)
+ continue;
+ break;
+ }
if (exclusive_input_pressed(iptkey, code, 0))
break;
}
@@ -1218,11 +1254,52 @@ void menu::validate_selection(int scandir)
MENU STACK MANAGEMENT
***************************************************************************/
-void menu::do_handle()
+bool menu::do_handle()
{
- if (m_items.size() < 2)
- populate(m_customtop, m_custombottom);
- handle();
+ bool need_update = false;
+
+ // let OSD do its thing
+ machine().osd().check_osd_inputs();
+
+ // recompute metrics if necessary
+ render_manager &render(machine().render());
+ render_target &target(render.ui_target());
+ std::pair<uint32_t, uint32_t> const uisize(target.width(), target.height());
+ float const aspect = render.ui_aspect(&container());
+ if ((uisize != m_last_size) || (aspect != m_last_aspect))
+ {
+ m_last_size = uisize;
+ m_last_aspect = aspect;
+ recompute_metrics(uisize.first, uisize.second, aspect);
+ need_update = true;
+ }
+
+ if (m_items.empty())
+ {
+ m_rebuilding = true;
+ try
+ {
+ // add an item to return - this is a really hacky way of doing this
+ if (m_needs_prev_menu_item)
+ item_append(_("Return to Previous Menu"), 0, nullptr);
+
+ // let implementation add other items
+ populate();
+ need_update = true;
+ }
+ catch (...)
+ {
+ m_items.clear();
+ m_rebuilding = false;
+ throw;
+ }
+ m_rebuilding = false;
+ }
+
+ auto [eventptr, changed] = process();
+ need_update = need_update || changed;
+ need_update = handle(eventptr) || need_update;
+ return need_update;
}
@@ -1235,26 +1312,10 @@ void menu::do_handle()
// and calls the menu handler
//-------------------------------------------------
-uint32_t menu::ui_handler(render_container &container, mame_ui_manager &mui)
+delegate<uint32_t (render_container &)> menu::get_ui_handler(mame_ui_manager &mui)
{
- global_state_ptr const state(get_global_state(mui.machine()));
-
- // if we have no menus stacked up, start with the main menu
- if (!state->topmost_menu<menu>())
- state->stack_push(std::unique_ptr<menu>(make_unique_clear<menu_main>(mui, container)));
-
- // update the menu state
- if (state->topmost_menu<menu>())
- state->topmost_menu<menu>()->do_handle();
-
- // clear up anything pending to be released
- state->clear_free_list();
-
- // if the menus are to be hidden, return a cancel here
- if (mui.is_menu_active() && ((mui.machine().ui_input().pressed(IPT_UI_CONFIGURE) && !state->stack_has_special_main_menu()) || !state->topmost_menu<menu>()))
- return UI_HANDLER_CANCEL;
-
- return 0;
+ global_state &state(get_global_state(mui));
+ return delegate<uint32_t (render_container &)>(&global_state::ui_handler, &state);
}
/***************************************************************************
@@ -1262,12 +1323,22 @@ uint32_t menu::ui_handler(render_container &container, mame_ui_manager &mui)
***************************************************************************/
//-------------------------------------------------
+// create_layout
+//-------------------------------------------------
+
+text_layout menu::create_layout(float width, text_layout::text_justify justify, text_layout::word_wrapping wrap)
+{
+ return text_layout(*ui().get_font(), line_height() * m_last_aspect, line_height(), width, justify, wrap);
+}
+
+
+//-------------------------------------------------
// highlight
//-------------------------------------------------
void menu::highlight(float x0, float y0, float x1, float y1, rgb_t bgcolor)
{
- container().add_quad(x0, y0, x1, y1, bgcolor, m_global_state->hilight_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1) | PRIMFLAG_PACKABLE);
+ container().add_quad(x0, y0, x1, y1, bgcolor, m_global_state.hilight_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1) | PRIMFLAG_PACKABLE);
}
@@ -1277,7 +1348,7 @@ void menu::highlight(float x0, float y0, float x1, float y1, rgb_t bgcolor)
void menu::draw_arrow(float x0, float y0, float x1, float y1, rgb_t fgcolor, uint32_t orientation)
{
- container().add_quad(x0, y0, x1, y1, fgcolor, m_global_state->arrow_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(orientation) | PRIMFLAG_PACKABLE);
+ container().add_quad(x0, y0, x1, y1, fgcolor, m_global_state.arrow_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(orientation) | PRIMFLAG_PACKABLE);
}
@@ -1286,10 +1357,10 @@ void menu::draw_arrow(float x0, float y0, float x1, float y1, rgb_t fgcolor, uin
// or footer text
//-------------------------------------------------
-void menu::extra_text_draw_box(float origx1, float origx2, float origy, float yspan, const char *text, int direction)
+void menu::extra_text_draw_box(float origx1, float origx2, float origy, float yspan, std::string_view text, int direction)
{
// get the size of the text
- auto layout = ui().create_layout(container());
+ auto layout = create_layout();
layout.add_text(text);
// position this extra text
@@ -1300,8 +1371,8 @@ void menu::extra_text_draw_box(float origx1, float origx2, float origy, float ys
ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
// take off the borders
- x1 += ui().box_lr_border() * machine().render().ui_aspect(&container());
- y1 += ui().box_tb_border();
+ x1 += lr_border();
+ y1 += tb_border();
// draw the text within it
layout.emit(container(), x1, y1);
@@ -1311,8 +1382,8 @@ void menu::extra_text_draw_box(float origx1, float origx2, float origy, float ys
void menu::draw_background()
{
// draw background image if available
- if (ui().options().use_background_image() && m_global_state->bgrnd_bitmap() && m_global_state->bgrnd_bitmap()->valid())
- container().add_quad(0.0f, 0.0f, 1.0f, 1.0f, rgb_t::white(), m_global_state->bgrnd_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ if (ui().options().use_background_image() && m_global_state.bgrnd_bitmap() && m_global_state.bgrnd_bitmap()->valid())
+ container().add_quad(0.0F, 0.0F, 1.0F, 1.0F, rgb_t::white(), m_global_state.bgrnd_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
@@ -1324,14 +1395,14 @@ void menu::draw_background()
void menu::extra_text_position(float origx1, float origx2, float origy, float yspan, text_layout &layout,
int direction, float &x1, float &y1, float &x2, float &y2)
{
- float width = layout.actual_width() + (2 * ui().box_lr_border() * machine().render().ui_aspect(&container()));
+ float width = layout.actual_width() + (2 * lr_border());
float maxwidth = std::max(width, origx2 - origx1);
// compute our bounds
- x1 = 0.5f - 0.5f * maxwidth;
+ x1 = 0.5F - (0.5F * maxwidth);
x2 = x1 + maxwidth;
y1 = origy + (yspan * direction);
- y2 = origy + (ui().box_tb_border() * direction);
+ y2 = origy + (tb_border() * direction);
if (y1 > y2)
std::swap(y1, y2);
@@ -1343,14 +1414,11 @@ void menu::extra_text_position(float origx1, float origx2, float origy, float ys
// and footer text
//-------------------------------------------------
-void menu::extra_text_render(float top, float bottom, float origx1, float origy1, float origx2, float origy2, const char *header, const char *footer)
+void menu::extra_text_render(float top, float bottom, float origx1, float origy1, float origx2, float origy2, std::string_view header, std::string_view footer)
{
- header = (header && *header) ? header : nullptr;
- footer = (footer && *footer) ? footer : nullptr;
-
- if (header != nullptr)
+ if (!header.empty())
extra_text_draw_box(origx1, origx2, origy1, top, header, -1);
- if (footer != nullptr)
+ if (!footer.empty())
extra_text_draw_box(origx1, origx2, origy2, bottom, footer, +1);
}