From b0633778028af6df83e03e465c383250f4d0db3d Mon Sep 17 00:00:00 2001 From: Robert Alm Nilsson Date: Sat, 15 Aug 2020 22:00:12 +0200 Subject: [PATCH] Add state for active objects to GUI Fixes #200 about sliders that only work when the mouse is over them. This commit adds two things; state variables (`*childpath`) with function that modifies it, and modifications to relevant GUI object (e.g. button, slider) functions so that they use that function to register as a unique object and find out whether they are active. The GUI code now keeps track of the "path" to each object. By "path" I mean e.g. go to the 3rd top-level child and then to it's 2nd child. This is similar to what it looks like they do in Red Eclipse 2. This is not a perfect solution because it's possible that these paths change when objects are added or removed but it is much better than just giving each object a simple number as ID because when something is changed inside one list it doesn't change the path to an object that is not a child of that list. The modifications to the GUI objects are usually done so that the mouse-up action only happens if the object is the active object (the mouse was pressed down on that object). --- src/engine/menus.cpp | 4 ++- src/engine/ui.cpp | 85 +++++++++++++++++++++++++++++++++++++++----- src/shared/iengine.h | 2 ++ 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/engine/menus.cpp b/src/engine/menus.cpp index faa3f08e9..38dce5599 100644 --- a/src/engine/menus.cpp +++ b/src/engine/menus.cpp @@ -638,7 +638,9 @@ void ui_body(uint *contents, char *action, char *altact, uint *onhover) cgui->pushlist(action && *action ? true : false); execute(contents); int ret = cgui->poplist(); - if(ret&GUI_UP) + if(guilayoutpass) return; + bool active = cgui->unique_object_active(ret & GUI_ROLLOVER); + if(ret&GUI_UP && active) { char *act = NULL; if(ret&GUI_ALT && altact && *altact) act = altact; diff --git a/src/engine/ui.cpp b/src/engine/ui.cpp index 03408ea62..a497ed42b 100644 --- a/src/engine/ui.cpp +++ b/src/engine/ui.cpp @@ -95,6 +95,44 @@ struct gui : guient static int curdepth, curlist, xsize, ysize, curx, cury, fontdepth, mergelist, mergedepth; static bool hitfx, skinfx, cursorfx; + // Example: {3, 2} means that the current object (button/slider/whatever) is the 2nd child of the 3rd top-level child. + // The exact numbers don't matter as long as they are unique for each object. + static vector curchildpath; + + // This is a copy of curchildpath made when pressing down a mouse button on an object. + // It's used to remember what item is interacted with (on the previous frame). + static vector activechildpath; + + // This is used to set activechildpath on the next frame. + // The reason we don't set activechildpath directly is that then it will be half set in the middle of each frame. + static vector nextactivechildpath; + + bool current_is_active() + { + if(activechildpath.size() != curchildpath.size()) return false; + for(int i = 0; i < curchildpath.size(); i++) + { + if(curchildpath[i] != activechildpath[i]) return false; + } + return true; + } + + // Registers current object as unique and returns whether it's active (pressed with mouse). + bool unique_object_active(bool hit) + { + bool active = current_is_active(); + if(active && mouse_action[0] & GUI_UP) + { + nextactivechildpath.clear(); + } + else if(hit && mouse_action[0] & GUI_DOWN) + { + nextactivechildpath = curchildpath; + } + curchildpath.last()++; + return active; + } + static void reset() { if(statusstr) DELETEA(statusstr); @@ -212,9 +250,11 @@ struct gui : guient int x1 = curx+tx, x2 = x1 + width + ui_size_spacer, y1 = cury - ui_size_spacer - height, y2 = cury - ui_size_spacer * 3 / 4, alpha = ui_blend_text, border = -1; if(!visibletab()) { - if(tcurrent && !passthrough && fieldmode != FIELDKEY && hitx>=x1 && hity>=y1 && hitx= x1 && hity >= y1 && hitx < x2 && hity < y2; + bool active = unique_object_active(hit); + if(tcurrent && !passthrough && fieldmode != FIELDKEY && hit) { - if(!ui_click_tab || mouse_action[0] & GUI_UP) *tcurrent = tpos; // switch tab + if(active && (!ui_click_tab || mouse_action[0] & GUI_UP)) *tcurrent = tpos; // switch tab tcolor = ui_color_active; alpha = max(alpha, ui_fade_text); if(ui_tab_border) border = tcolor; @@ -245,11 +285,11 @@ struct gui : guient #define uibtn(a,b) \ { \ int border = -1; \ - bool hit = false; \ - if(!passthrough && fieldmode != FIELDKEY && hitx>=x1 && hity>=y1 && hitx=x1 && hity>=y1 && hitx 0 ? wrap : -1); if(clickable && hit && hitfx && cursorfx && !ui_cursor_type) ui_cursor_type = 1; + + int ret = layout(w, h); + if(!active) ret &= ~GUI_UP; + return ret; } return layout(w, h); } @@ -1172,6 +1235,7 @@ struct gui : guient void start(int starttime, int *tab, bool allowinput, bool wantstitle, bool wantsbgfx) { + if(!guilayoutpass) activechildpath = nextactivechildpath; fontdepth = 0; gui::pushfont("default"); basescale = ui_scale; @@ -1285,6 +1349,9 @@ float gui::basescale, gui::maxscale = 1, gui::hitx, gui::hity; bool gui::passthrough, gui::hitfx = true, gui::cursorfx = true, gui::skinfx = true; int gui::curdepth, gui::fontdepth, gui::curlist, gui::xsize, gui::ysize, gui::curx, gui::cury, gui::mergelist, gui::mergedepth; int gui::ty, gui::tx, gui::tpos, *gui::tcurrent, gui::tcolor; +vector gui::curchildpath; +vector gui::activechildpath; +vector gui::nextactivechildpath; static vector guis; namespace UI diff --git a/src/shared/iengine.h b/src/shared/iengine.h index 1cd6abcab..58bc6b746 100644 --- a/src/shared/iengine.h +++ b/src/shared/iengine.h @@ -568,6 +568,8 @@ struct guient virtual void start(int starttime, int *tab = NULL, bool allowinput = true, bool wantstitle = true, bool wantsbgfx = true) = 0; virtual void end() = 0; + virtual bool unique_object_active(bool hit) = 0; + virtual int text(const char *text, int color = 0xFFFFFF, const char *icon = NULL, int icolour = 0xFFFFFF, int wrap = -1, bool faded = false, const char *oicon = NULL, int ocolor = 0xFFFFFF) = 0; int textf(const char *fmt, int color = 0xFFFFFF, const char *icon = NULL, int icolour = 0xFFFFFF, int wrap = -1, bool faded = false, const char *oicon = NULL, int ocolor = 0xFFFFFF, ...) PRINTFARGS(2, 10) {