summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/ui/analogipt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/ui/analogipt.cpp')
-rw-r--r--src/frontend/mame/ui/analogipt.cpp395
1 files changed, 329 insertions, 66 deletions
diff --git a/src/frontend/mame/ui/analogipt.cpp b/src/frontend/mame/ui/analogipt.cpp
index f8767ad3c58..aa6ffa5a07f 100644
--- a/src/frontend/mame/ui/analogipt.cpp
+++ b/src/frontend/mame/ui/analogipt.cpp
@@ -13,6 +13,8 @@
#include "ui/textbox.h"
+#include "uiinput.h"
+
#include <algorithm>
#include <iterator>
#include <string>
@@ -74,9 +76,20 @@ menu_analog::menu_analog(mame_ui_manager &mui, render_container &container)
, m_item_data()
, m_field_data()
, m_prompt()
- , m_visible_fields(0U)
+ , m_bottom_fields(0U)
+ , m_visible_fields(0)
, m_top_field(0)
, m_hide_menu(false)
+ , m_box_left(1.0F)
+ , m_box_top(1.0F)
+ , m_box_right(0.0F)
+ , m_box_bottom(0.0F)
+ , m_pointer_action(pointer_action::NONE)
+ , m_scroll_repeat(std::chrono::steady_clock::time_point::min())
+ , m_base_pointer(0.0F, 0.0F)
+ , m_last_pointer(0.0F, 0.0F)
+ , m_scroll_base(0)
+ , m_arrow_clicked_first(false)
{
set_process_flags(PROCESS_LR_REPEAT);
set_heading(_("menu-analoginput", "Analog Input Adjustments"));
@@ -92,51 +105,50 @@ void menu_analog::recompute_metrics(uint32_t width, uint32_t height, float aspec
{
menu::recompute_metrics(width, height, aspect);
+ m_box_left = m_box_top = 1.0F;
+ m_box_right = m_box_bottom = 0.0F;
+
// space for live display
- set_custom_space(0.0f, (line_height() * m_visible_fields) + (tb_border() * 3.0f));
+ set_custom_space(0.0F, (line_height() * m_bottom_fields) + (tb_border() * 3.0F));
}
-void menu_analog::custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
+void menu_analog::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2)
{
// work out how much space to use for field names
- float const aspect(machine().render().ui_aspect(&container()));
- float const extrawidth(0.4f + (((ui().box_lr_border() * 2.0f) + ui().get_line_height()) * aspect));
- float const nameavail(1.0f - (lr_border() * 2.0f) - extrawidth);
- float namewidth(0.0f);
+ float const extrawidth(0.4F + (((ui().box_lr_border() * 2.0F) + ui().get_line_height()) * x_aspect()));
+ float const nameavail(1.0F - (lr_border() * 2.0F) - extrawidth);
+ float namewidth(0.0F);
for (field_data &data : m_field_data)
namewidth = (std::min)((std::max)(get_string_width(data.field.get().name()), namewidth), nameavail);
// make a box or two
rgb_t const fgcolor(ui().colors().text_color());
- float const boxleft((1.0f - namewidth - extrawidth) / 2.0f);
- float const boxright(boxleft + namewidth + extrawidth);
- float boxtop;
- float boxbottom;
+ m_box_left = (1.0F - namewidth - extrawidth) * 0.5F;
+ m_box_right = m_box_left + namewidth + extrawidth;
float firstliney;
- int visible_fields;
if (m_hide_menu)
{
if (m_prompt.empty())
m_prompt = util::string_format(_("menu-analoginput", "Press %s to show settings"), ui().get_general_input_setting(IPT_UI_ON_SCREEN_DISPLAY));
draw_text_box(
&m_prompt, &m_prompt + 1,
- boxleft, boxright, y - top, y - top + line_height() + (tb_border() * 2.0f),
+ m_box_left, m_box_right, y - top, y - top + line_height() + (tb_border() * 2.0F),
text_layout::text_justify::CENTER, text_layout::word_wrapping::TRUNCATE, false,
fgcolor, ui().colors().background_color());
- boxtop = y - top + line_height() + (tb_border() * 3.0f);
- firstliney = y - top + line_height() + (tb_border() * 4.0f);
- visible_fields = std::min<int>(m_field_data.size(), int((y2 + bottom - tb_border() - firstliney) / line_height()));
- boxbottom = firstliney + (line_height() * visible_fields) + tb_border();
+ m_box_top = y - top + line_height() + (tb_border() * 3.0F);
+ firstliney = y - top + line_height() + (tb_border() * 4.0F);
+ m_visible_fields = std::min<int>(m_field_data.size(), int((y2 + bottom - tb_border() - firstliney) / line_height()));
+ m_box_bottom = firstliney + (line_height() * m_visible_fields) + tb_border();
}
else
{
- boxtop = y2 + tb_border();
- boxbottom = y2 + bottom;
- firstliney = y2 + (tb_border() * 2.0f);
- visible_fields = m_visible_fields;
+ m_box_top = y2 + tb_border();
+ m_box_bottom = y2 + bottom;
+ firstliney = y2 + (tb_border() * 2.0F);
+ m_visible_fields = m_bottom_fields;
}
- ui().draw_outlined_box(container(), boxleft, boxtop, boxright, boxbottom, ui().colors().background_color());
+ ui().draw_outlined_box(container(), m_box_left, m_box_top, m_box_right, m_box_bottom, ui().colors().background_color());
// force the field being configured to be visible
ioport_field *const selfield(selectedref ? &reinterpret_cast<item_data *>(selectedref)->field.get() : nullptr);
@@ -152,35 +164,44 @@ void menu_analog::custom_render(void *selectedref, float top, float bottom, floa
auto const i(std::distance(m_field_data.begin(), found));
if (m_top_field > i)
m_top_field = i;
- if ((m_top_field + visible_fields) <= i)
- m_top_field = i - m_visible_fields + 1;
+ if ((m_top_field + m_visible_fields) <= i)
+ m_top_field = i - m_bottom_fields + 1;
}
}
if (0 > m_top_field)
m_top_field = 0;
- if ((m_top_field + visible_fields) > m_field_data.size())
- m_top_field = m_field_data.size() - visible_fields;
+ if ((m_top_field + m_visible_fields) > m_field_data.size())
+ m_top_field = m_field_data.size() - m_visible_fields;
// show live fields
- namewidth += line_height() * aspect;
- float const nameleft(boxleft + lr_border());
+ namewidth += line_height() * x_aspect();
+ float const nameleft(m_box_left + lr_border());
float const indleft(nameleft + namewidth);
- float const indright(indleft + 0.4f);
- for (unsigned line = 0; visible_fields > line; ++line)
+ float const indright(indleft + 0.4F);
+ for (unsigned line = 0; m_visible_fields > line; ++line)
{
// draw arrows if scrolling is possible and menu is hidden
- float const liney(firstliney + (line_height() * line));
+ float const liney(firstliney + (line_height() * float(line)));
if (m_hide_menu)
{
bool const uparrow(!line && m_top_field);
- bool const downarrow(((visible_fields - 1) == line) && ((m_field_data.size() - 1) > (line + m_top_field)));
+ bool const downarrow(((m_visible_fields - 1) == line) && ((m_field_data.size() - 1) > (line + m_top_field)));
if (uparrow || downarrow)
{
- float const arrowwidth = line_height() * aspect;
+ bool const active((uparrow && (pointer_action::SCROLL_UP == m_pointer_action)) || (downarrow && (pointer_action::SCROLL_DOWN == m_pointer_action)));
+ bool const hovered((active || pointer_idle()) && pointer_in_rect(nameleft, liney, indright, liney + line_height()));
+ float const arrowwidth(line_height() * x_aspect());
+ rgb_t const arrowcolor(!(active || hovered) ? fgcolor : (active && hovered) ? ui().colors().selected_color() : ui().colors().mouseover_color());
+ if (active || hovered)
+ {
+ highlight(
+ nameleft, liney, indright, liney + line_height(),
+ (active && hovered) ? ui().colors().selected_bg_color() : ui().colors().mouseover_bg_color());
+ }
draw_arrow(
- 0.5f * (nameleft + indright - arrowwidth), liney + (0.25f * line_height()),
- 0.5f * (nameleft + indright + arrowwidth), liney + (0.75f * line_height()),
- fgcolor, line ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
+ 0.5F * (nameleft + indright - arrowwidth), liney + (0.25F * line_height()),
+ 0.5F * (nameleft + indright + arrowwidth), liney + (0.75F * line_height()),
+ arrowcolor, line ? (ROT0 ^ ORIENTATION_FLIP_Y) : ROT0);
continue;
}
}
@@ -200,42 +221,201 @@ void menu_analog::custom_render(void *selectedref, float top, float bottom, floa
cur = ((cur >> data.shift) - data.field.get().minval()) & (data.field.get().mask() >> data.shift);
float fill(float(cur) / data.range);
if (data.field.get().analog_reverse())
- fill = 1.0f - fill;
+ fill = 1.0F - fill;
- float const indtop(liney + (line_height() * 0.2f));
- float const indbottom(liney + (line_height() * 0.8f));
+ float const indtop(liney + (line_height() * 0.2F));
+ float const indbottom(liney + (line_height() * 0.8F));
if (data.origin > fill)
- container().add_rect(indleft + (fill * 0.4f), indtop, indleft + (data.origin * 0.4f), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_rect(indleft + (fill * 0.4F), indtop, indleft + (data.origin * 0.4F), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
else
- container().add_rect(indleft + (data.origin * 0.4f), indtop, indleft + (fill * 0.4f), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_rect(indleft + (data.origin * 0.4F), indtop, indleft + (fill * 0.4F), indbottom, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indleft, indtop, indright, indtop, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indright, indtop, indright, indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indright, indbottom, indleft, indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
container().add_line(indleft, indbottom, indleft, indtop, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
if (data.show_neutral)
- container().add_line(indleft + (data.neutral * 0.4f), indtop, indleft + (data.neutral * 0.4f), indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ container().add_line(indleft + (data.neutral * 0.4F), indtop, indleft + (data.neutral * 0.4F), indbottom, UI_LINE_WIDTH, fgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
}
}
+std::tuple<int, bool, bool> menu_analog::custom_pointer_updated(bool changed, ui_event const &uievt)
+{
+ // no pointer input if we don't have up-to-date content on-screen
+ if ((m_box_left > m_box_right) || (ui_event::type::POINTER_ABORT == uievt.event_type))
+ {
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
+
+ // if nothing's happening, check for clicks
+ if (pointer_idle())
+ {
+ if ((uievt.pointer_pressed & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
+ {
+ if (1 == uievt.pointer_clicks)
+ m_arrow_clicked_first = false;
+
+ float const firstliney(m_box_top + tb_border());
+ float const fieldleft(m_box_left + lr_border());
+ float const fieldright(m_box_right - lr_border());
+ auto const [x, y] = pointer_location();
+ bool const inwidth((x >= fieldleft) && (x < fieldright));
+ if (m_hide_menu && m_top_field && inwidth && (y >= firstliney) && (y < (firstliney + line_height())))
+ {
+ // scroll up arrow
+ if (1 == uievt.pointer_clicks)
+ m_arrow_clicked_first = true;
+
+ --m_top_field;
+ m_pointer_action = pointer_action::SCROLL_UP;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if (m_hide_menu && ((m_top_field + m_visible_fields) < m_field_data.size()) && inwidth && (y >= (firstliney + (float(m_visible_fields - 1) * line_height()))) && (y < (firstliney + (float(m_visible_fields) * line_height()))))
+ {
+ // scroll down arrow
+ if (1 == uievt.pointer_clicks)
+ m_arrow_clicked_first = true;
+
+ ++m_top_field;
+ m_pointer_action = pointer_action::SCROLL_DOWN;
+ m_scroll_repeat = std::chrono::steady_clock::now() + std::chrono::milliseconds(300);
+ m_last_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ else if ((x >= m_box_left) && (x < m_box_right) && (y >= m_box_top) && (y < m_box_bottom))
+ {
+ if (!m_arrow_clicked_first && (2 == uievt.pointer_clicks))
+ {
+ // toggle menu display
+ // FIXME: this should really use the start point of the multi-click action
+ m_pointer_action = pointer_action::CHECK_TOGGLE_MENU;
+ m_base_pointer = std::make_pair(x, y);
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ else if (ui_event::pointer::TOUCH == uievt.pointer_type)
+ {
+ m_pointer_action = pointer_action::SCROLL_DRAG;
+ m_base_pointer = std::make_pair(x, y);
+ m_last_pointer = m_base_pointer;
+ m_scroll_base = m_top_field;
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ }
+ }
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
+
+ // handle in-progress actions
+ switch (m_pointer_action)
+ {
+ case pointer_action::NONE:
+ break;
+
+ case pointer_action::SCROLL_UP:
+ case pointer_action::SCROLL_DOWN:
+ {
+ // check for re-entry
+ bool redraw(false);
+ float const linetop(m_box_top + tb_border() + ((pointer_action::SCROLL_DOWN == m_pointer_action) ? (float(m_visible_fields - 1) * line_height()) : 0.0F));
+ float const linebottom(linetop + line_height());
+ auto const [x, y] = pointer_location();
+ bool const reentered(reentered_rect(m_last_pointer.first, m_last_pointer.second, x, y, m_box_left + lr_border(), linetop, m_box_right - lr_border(), linebottom));
+ if (reentered)
+ {
+ auto const now(std::chrono::steady_clock::now());
+ if (scroll_if_expired(now))
+ {
+ redraw = true;
+ m_scroll_repeat = now + std::chrono::milliseconds(100);
+ }
+ }
+ m_last_pointer = std::make_pair(x, y);
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, redraw);
+ }
+ break;
+
+ case pointer_action::SCROLL_DRAG:
+ {
+ bool const scrolled(update_scroll_drag(uievt));
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, scrolled);
+ }
+
+ case pointer_action::CHECK_TOGGLE_MENU:
+ if ((ui_event::pointer::TOUCH == uievt.pointer_type) && (0 > uievt.pointer_clicks))
+ {
+ // converted to hold/drag - treat as scroll if it's touch
+ m_pointer_action = pointer_action::SCROLL_DRAG;
+ m_last_pointer = m_base_pointer;
+ m_scroll_base = m_top_field;
+ bool const scrolled(update_scroll_drag(uievt));
+ return std::make_tuple(IPT_INVALID, pointer_action::NONE != m_pointer_action, scrolled);
+ }
+ else if (uievt.pointer_released & 0x01)
+ {
+ // primary button released - simulate the on-screen display key if it wasn't converted to a hold/drag
+ return std::make_tuple((2 == uievt.pointer_clicks) ? IPT_UI_ON_SCREEN_DISPLAY : IPT_INVALID, false, false);
+ }
+ else if ((2 != uievt.pointer_clicks) || (uievt.pointer_buttons & ~u32(0x01)))
+ {
+ // treat converting to a hold/drag or pressing another button as cancelling the action
+ return std::make_tuple(IPT_INVALID, false, false);
+ }
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
+ return std::make_tuple(IPT_INVALID, false, false);
+}
+
+
void menu_analog::menu_activated()
{
// scripts could have changed something in the mean time
m_item_data.clear();
m_field_data.clear();
reset(reset_options::REMEMBER_POSITION);
+
+ m_box_left = m_box_top = 1.0F;
+ m_box_right = m_box_bottom = 0.0F;
+ m_pointer_action = pointer_action::NONE;
+ m_arrow_clicked_first = false;
}
bool menu_analog::handle(event const *ev)
{
+ // deal with repeating scroll arrows
+ bool scrolled(false);
+ if ((pointer_action::SCROLL_UP == m_pointer_action) || (pointer_action::SCROLL_DOWN == m_pointer_action))
+ {
+ float const linetop(m_box_top + tb_border() + ((pointer_action::SCROLL_DOWN == m_pointer_action) ? (float(m_visible_fields - 1) * line_height()) : 0.0F));
+ float const linebottom(linetop + line_height());
+ if (pointer_in_rect(m_box_left + lr_border(), linetop, m_box_right - lr_border(), linebottom))
+ {
+ while (scroll_if_expired(std::chrono::steady_clock::now()))
+ {
+ scrolled = true;
+ m_scroll_repeat += std::chrono::milliseconds(100);
+ }
+ }
+ }
+
if (!ev)
{
- return false;
+ return scrolled;
}
else if (IPT_UI_ON_SCREEN_DISPLAY == ev->iptkey)
{
m_hide_menu = !m_hide_menu;
+
+ m_box_left = m_box_top = 1.0F;
+ m_box_right = m_box_bottom = 0.0F;
+ m_pointer_action = pointer_action::NONE;
+ m_arrow_clicked_first = false;
+
set_process_flags(PROCESS_LR_REPEAT | (m_hide_menu ? (PROCESS_CUSTOM_NAV | PROCESS_CUSTOM_ONLY) : 0));
return true;
}
@@ -254,26 +434,53 @@ bool menu_analog::handle(event const *ev)
ui().get_general_input_setting(IPT_UI_PREV_GROUP),
ui().get_general_input_setting(IPT_UI_NEXT_GROUP),
ui().get_general_input_setting(IPT_UI_BACK)));
- return false;
}
else if (m_hide_menu)
{
switch (ev->iptkey)
{
case IPT_UI_UP:
- --m_top_field;
- return true;
+ if (m_top_field)
+ {
+ --m_top_field;
+ return true;
+ }
+ break;
case IPT_UI_DOWN:
- ++m_top_field;
- return true;
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ {
+ ++m_top_field;
+ return true;
+ }
+ break;
+ case IPT_UI_PAGE_UP:
+ if (m_visible_fields)
+ {
+ m_top_field -= std::min<int>(m_visible_fields - 3, m_top_field);
+ return true;
+ }
+ break;
+ case IPT_UI_PAGE_DOWN:
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ {
+ m_top_field = std::min<int>(m_top_field + m_visible_fields - 3, m_field_data.size() - m_visible_fields);
+ return true;
+ }
+ break;
case IPT_UI_HOME:
- m_top_field = 0;
- return true;
+ if (m_top_field)
+ {
+ m_top_field = 0;
+ return true;
+ }
+ break;
case IPT_UI_END:
- m_top_field = m_field_data.size();
- return true;
- default:
- return false;
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ {
+ m_top_field = m_field_data.size() - m_visible_fields;
+ return true;
+ }
+ break;
}
}
else if (ev->itemref)
@@ -384,15 +591,9 @@ bool menu_analog::handle(event const *ev)
ev->item->set_flags((data.cur <= data.min) ? FLAG_RIGHT_ARROW : (data.cur >= data.max) ? FLAG_LEFT_ARROW : FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW);
return true;
}
- else
- {
- return false;
- }
- }
- else
- {
- return false;
}
+
+ return scrolled;
}
@@ -466,7 +667,7 @@ void menu_analog::populate()
item_append(menu_item_type::SEPARATOR);
// space for live display
- set_custom_space(0.0f, (line_height() * m_visible_fields) + (tb_border() * 3.0f));
+ set_custom_space(0.0F, (line_height() * m_bottom_fields) + (tb_border() * 3.0F));
}
@@ -518,10 +719,72 @@ void menu_analog::find_fields()
}
// restrict live display to 40% height plus borders
- if ((line_height() * m_field_data.size()) > 0.4f)
- m_visible_fields = unsigned(0.4f / line_height());
+ if ((line_height() * m_field_data.size()) > 0.4F)
+ m_bottom_fields = unsigned(0.4F / line_height());
+ else
+ m_bottom_fields = m_field_data.size();
+}
+
+
+bool menu_analog::scroll_if_expired(std::chrono::steady_clock::time_point now)
+{
+ if (now < m_scroll_repeat)
+ return false;
+
+ if (pointer_action::SCROLL_DOWN == m_pointer_action)
+ {
+ if ((m_top_field + m_visible_fields) < m_field_data.size())
+ ++m_top_field;
+ if ((m_top_field + m_visible_fields) == m_field_data.size())
+ m_pointer_action = pointer_action::NONE;
+ }
else
- m_visible_fields = m_field_data.size();
+ {
+ if (0 < m_top_field)
+ --m_top_field;
+ if (!m_top_field)
+ m_pointer_action = pointer_action::NONE;
+ }
+ return true;
+}
+
+
+bool menu_analog::update_scroll_drag(ui_event const &uievt)
+{
+ // set thresholds depending on the direction for hysteresis
+ float const y(pointer_location().second);
+ float const base(m_base_pointer.second + (line_height() * ((y > m_last_pointer.second) ? -0.3F : 0.3F)));
+ auto const target(int((base - y) / line_height()));
+ m_last_pointer.second = base + (float(target) * line_height());
+
+ // scroll if it moved
+ int newtop(std::clamp<int>(m_scroll_base + target, 0, m_field_data.size() - m_visible_fields));
+ if (!m_hide_menu && (newtop != m_top_field))
+ {
+ // if the menu is visible, keep the highlighted field on-screen
+ void *const selectedref(get_selection_ref());
+ ioport_field *const selfield(selectedref ? &reinterpret_cast<item_data *>(selectedref)->field.get() : nullptr);
+ if (selfield)
+ {
+ auto const found(
+ std::find_if(
+ m_field_data.begin(),
+ m_field_data.end(),
+ [selfield] (field_data const &d) { return &d.field.get() == selfield; }));
+ if (m_field_data.end() != found)
+ {
+ auto const i(std::distance(m_field_data.begin(), found));
+ newtop = std::clamp<int>(newtop, i + 1 - m_visible_fields, i);
+ }
+ }
+ }
+ bool const scrolled(newtop != m_top_field);
+ m_top_field = newtop;
+
+ // catch the end of the gesture
+ if ((uievt.pointer_released & 0x01) || (uievt.pointer_pressed & ~u32(0x01)))
+ m_pointer_action = pointer_action::NONE;
+ return scrolled;
}