diff options
author | Aaron Giles <aaron@aarongiles.com> | 2021-06-04 07:29:56 -0700 |
---|---|---|
committer | Aaron Giles <aaron@aarongiles.com> | 2021-06-04 07:29:56 -0700 |
commit | 43a5da3703baf8ab37a2ef29d6f5791d2db385bd (patch) | |
tree | d1560a73d5a677389fc97539217e0122c524e07f | |
parent | 3f3e0eda38bc08c9ad67ae46543ff7a0f45a856e (diff) |
Add support for dragging position-animated layout items.draggable-layout
-rw-r--r-- | src/emu/ioport.cpp | 2 | ||||
-rw-r--r-- | src/emu/render.cpp | 31 | ||||
-rw-r--r-- | src/emu/render.h | 6 | ||||
-rw-r--r-- | src/emu/rendlay.cpp | 55 | ||||
-rw-r--r-- | src/emu/rendlay.h | 7 | ||||
-rw-r--r-- | src/emu/uiinput.cpp | 54 | ||||
-rw-r--r-- | src/emu/uiinput.h | 7 |
7 files changed, 141 insertions, 21 deletions
diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index 4f3d5b2c7ac..6df457d65f6 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -656,6 +656,8 @@ void ioport_field::set_value(ioport_value value) { if (is_analog()) live().analog->set_value(s32(value)); + else if (type() == IPT_ADJUSTER) + live().value = s32(value); else m_digital_value = value != 0; } diff --git a/src/emu/render.cpp b/src/emu/render.cpp index b7a66b7bd2a..640e100653b 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -1493,7 +1493,7 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta // field, if possible //------------------------------------------------- -bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y) +bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, item_handle &input_item, float &input_x, float &input_y) { std::pair<float, float> target_f(map_point_internal(target_x, target_y)); if (m_orientation & ORIENTATION_FLIP_X) @@ -1535,8 +1535,9 @@ bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&in { // point successfully mapped std::tie(input_port, input_mask) = item.input_tag_and_mask(); - input_x = (target_f.first - bounds.x0) / bounds.width(); - input_y = (target_f.second - bounds.y0) / bounds.height(); + input_x = target_f.first - ((bounds.x0 + bounds.x1) / 2); + input_y = target_f.second - ((bounds.y0 + bounds.y1) / 2); + input_item = reinterpret_cast<item_handle>(&item); return true; } else @@ -1551,11 +1552,35 @@ bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&in input_port = nullptr; input_mask = 0; input_x = input_y = -1.0f; + input_item = nullptr; return false; } //------------------------------------------------- +// interpolated_value - return the interpolated +// value of an item given x,y coordinates and +// an x,y offset from the item's center +//------------------------------------------------- + +ioport_value render_target::interpolated_value(s32 target_x, s32 target_y, item_handle item, float xoffs, float yoffs) +{ + std::pair<float, float> target_f(map_point_internal(target_x, target_y)); + if (m_orientation & ORIENTATION_FLIP_X) + target_f.first = 1.0f - target_f.first; + if (m_orientation & ORIENTATION_FLIP_Y) + target_f.second = 1.0f - target_f.second; + if (m_orientation & ORIENTATION_SWAP_XY) + std::swap(target_f.first, target_f.second); + + target_f.first -= xoffs; + target_f.second -= yoffs; + + return reinterpret_cast<layout_view::item *>(item)->state_from_position(target_f.first, target_f.second); +} + + +//------------------------------------------------- // invalidate_all - if any of our primitive lists // contain a reference to the given pointer, // clear them diff --git a/src/emu/render.h b/src/emu/render.h index a372da7f5c5..4c58a47b919 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -509,6 +509,9 @@ class render_target ~render_target(); public: + // abstraction of layout_view::item for hit testing + using item_handle = void *; + // getters render_target *next() const { return m_next; } render_manager &manager() const { return m_manager; } @@ -562,7 +565,8 @@ public: // hit testing bool map_point_container(s32 target_x, s32 target_y, render_container &container, float &container_x, float &container_y); - bool map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y); + bool map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, item_handle &input_item, float &input_x, float &input_y); + ioport_value interpolated_value(s32 target_x, s32 target_y, item_handle item, float xoffs, float yoffs); // reference tracking void invalidate_all(void *refptr); diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 789c7c15a7c..0541b41d07e 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -1052,6 +1052,50 @@ inline render_bounds interpolate_bounds(emu::render::detail::bounds_vector const } } +int uninterpolate_bounds(emu::render::detail::bounds_vector const &steps, float x, float y) +{ + float smallest_distance = 1e10; + int state = 0; + + auto first(steps.begin()); + auto second(first); + while (steps.end() != ++second) + { + // create a line between the center of the first and the center of the second + float x0 = (first->bounds.x0 + first->bounds.x1) / 2; + float y0 = (first->bounds.y0 + first->bounds.y1) / 2; + float x1 = (second->bounds.x0 + second->bounds.x1) / 2; + float y1 = (second->bounds.y0 + second->bounds.y1) / 2; + float length = sqrtf((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); + + // determine the distance to that line; if it's not the smallest so far, continue + float distance = fabsf((x1 - x0) * (y0 - y) - (x0 - x) * (y1 - y0)) / length; + if (distance > smallest_distance) + continue; + smallest_distance = distance; + + // handle out of bounds + if (((x0 < x1 && x < x0) || (x0 > x1 && x > x0) || x0 == x1) && ((y0 < y1 && y < y0) || (y0 > y1 && y > y1) || y0 == y1)) + state = first->state; + else if (((x1 < x0 && x < x1) || (x1 > x0 && x > x1) || x0 == x1) && ((y1 < y0 && y < y1) || (y1 > y0 && y > y0) || y0 == y1)) + state = second->state; + else + { + // compute the distance from our point to the first center + float dist_to_first = sqrtf((x - x0) * (x - x0) + (y - y0) * (y - y0)); + + // using that, compute the fractional distance along the line + float dist_along_line = sqrtf(dist_to_first * dist_to_first - distance * distance) / length; + + // clamp to max, else interpolate + if (dist_along_line > 1.0) + state = second->state; + else + state = first->state + (second->state - first->state) * dist_along_line; + } + } + return state; +} bool add_color_step(emu::render::detail::layout_environment &env, emu::render::detail::color_vector &steps, util::xml::data_node const &node) { @@ -4682,6 +4726,17 @@ void layout_view::item::resolve_tags() //------------------------------------------------- +// state_from_position - given a position, +// compute the effective state +//------------------------------------------------- + +ioport_value layout_view::item::state_from_position(float x, float y) +{ + return uninterpolate_bounds(m_bounds, x, y); +} + + +//------------------------------------------------- // default_get_elem_state - get default element // state handler //------------------------------------------------- diff --git a/src/emu/rendlay.h b/src/emu/rendlay.h index fb7871bd0ef..c364a0921fa 100644 --- a/src/emu/rendlay.h +++ b/src/emu/rendlay.h @@ -288,8 +288,8 @@ public: render_container *screen_container() const { return m_screen ? &m_screen->container() : nullptr; } // interactivity - bool has_input() const { return bool(m_input_port); } - std::pair<ioport_port *, ioport_value> input_tag_and_mask() const { return std::make_pair(m_input_port, m_input_mask); }; + bool has_input() const { return bool(m_input_port) || (bounds_animated() && bool(m_animinput_port)); } + std::pair<ioport_port *, ioport_value> input_tag_and_mask() const { return m_animinput_port ? std::make_pair(m_animinput_port, m_animmask) : std::make_pair(m_input_port, m_input_mask); }; bool clickthrough() const { return m_clickthrough; } // fetch state based on configured source @@ -308,6 +308,9 @@ public: // resolve tags, if any void resolve_tags(); + // return the best state relative to a point at the given position + ioport_value state_from_position(float x, float y); + private: using bounds_vector = emu::render::detail::bounds_vector; using color_vector = emu::render::detail::color_vector; diff --git a/src/emu/uiinput.cpp b/src/emu/uiinput.cpp index d1398591687..3c2b5e2422a 100644 --- a/src/emu/uiinput.cpp +++ b/src/emu/uiinput.cpp @@ -41,6 +41,9 @@ ui_input_manager::ui_input_manager(running_machine &machine) , m_current_mouse_y(-1) , m_current_mouse_down(false) , m_current_mouse_field(nullptr) + , m_current_mouse_drag_item(nullptr) + , m_current_mouse_target_dx(0) + , m_current_mouse_target_dy(0) , m_events_start(0) , m_events_end(0) { @@ -81,20 +84,44 @@ void ui_input_manager::frame_update() } } - // perform mouse hit testing - ioport_field *mouse_field = m_current_mouse_down ? find_mouse_field() : nullptr; - if (m_current_mouse_field != mouse_field) + // if dragging, update based solely on position + if (m_current_mouse_down && m_current_mouse_drag_item != nullptr) { - // clear the old field if there was one - if (m_current_mouse_field != nullptr) - m_current_mouse_field->set_value(0); + m_current_mouse_field->set_value(m_current_mouse_target->interpolated_value(m_current_mouse_x, m_current_mouse_y, m_current_mouse_drag_item, m_current_mouse_target_dx, m_current_mouse_target_dy)); + } + else + { + // perform mouse hit testing + float xoffs = 0, yoffs = 0; + render_target::item_handle item = nullptr; + ioport_field *mouse_field = m_current_mouse_down ? find_mouse_field(xoffs, yoffs, item) : nullptr; + if (m_current_mouse_field != mouse_field) + { + // clear the old field if there was one + if (m_current_mouse_field != nullptr && !m_current_mouse_field->is_analog() && m_current_mouse_field->type() != IPT_ADJUSTER) + m_current_mouse_field->set_value(0); + m_current_mouse_drag_item = nullptr; - // set the new field if it exists and isn't already being pressed - if (mouse_field != nullptr && !mouse_field->digital_value()) - mouse_field->set_value(1); + if (mouse_field != nullptr) + { + if (!mouse_field->is_analog() && mouse_field->type() != IPT_ADJUSTER) + { + // set the new field if it exists and isn't already being pressed + if (!mouse_field->digital_value()) + mouse_field->set_value(1); + } + else + { + // if analog, treat as the start of a dragging operation + m_current_mouse_drag_item = item; + m_current_mouse_target_dx = xoffs; + m_current_mouse_target_dy = yoffs; + } + } - // update internal state - m_current_mouse_field = mouse_field; + // update internal state + m_current_mouse_field = mouse_field; + } } } @@ -208,15 +235,14 @@ render_target *ui_input_manager::find_mouse(s32 *x, s32 *y, bool *button) const the mouse is currently pointing at -------------------------------------------------*/ -ioport_field *ui_input_manager::find_mouse_field() const +ioport_field *ui_input_manager::find_mouse_field(float &xoffs, float &yoffs, render_target::item_handle &item) const { // map the point and determine what was hit if (m_current_mouse_target != nullptr) { ioport_port *port = nullptr; ioport_value mask; - float x, y; - if (m_current_mouse_target->map_point_input(m_current_mouse_x, m_current_mouse_y, port, mask, x, y)) + if (m_current_mouse_target->map_point_input(m_current_mouse_x, m_current_mouse_y, port, mask, item, xoffs, yoffs)) { if (port != nullptr) return port->field(mask); diff --git a/src/emu/uiinput.h b/src/emu/uiinput.h index 29b18d06d30..a69abc07c6b 100644 --- a/src/emu/uiinput.h +++ b/src/emu/uiinput.h @@ -13,6 +13,8 @@ #pragma once +#include "render.h" + /*************************************************************************** TYPE DEFINITIONS @@ -70,7 +72,7 @@ public: // retrieves the current location of the mouse render_target *find_mouse(s32 *x, s32 *y, bool *button) const; - ioport_field *find_mouse_field() const; + ioport_field *find_mouse_field(float &xoffs, float &yoffs, render_target::item_handle &item) const; // return true if a key down for the given user interface sequence is detected bool pressed(int code); @@ -119,6 +121,9 @@ private: s32 m_current_mouse_y; bool m_current_mouse_down; ioport_field * m_current_mouse_field; + render_target::item_handle m_current_mouse_drag_item; + float m_current_mouse_target_dx; + float m_current_mouse_target_dy; // popped states; ring buffer of ui_events ui_event m_events[EVENT_QUEUE_SIZE]; |