diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index 242e84037..5a1b562ee 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -974,10 +974,11 @@ Screens the specified type (e.g. ``df.viewscreen_titlest``), or ``nil`` if none match. If ``depth`` is not specified or is less than 1, all viewscreens are checked. -* ``dfhack.gui.getDFViewscreen([skip_dismissed])`` +* ``dfhack.gui.getDFViewscreen([skip_dismissed[, viewscreen]])`` Returns the topmost viewscreen not owned by DFHack. If ``skip_dismissed`` is - ``true``, ignores screens already marked to be removed. + ``true``, ignores screens already marked to be removed. If ``viewscreen`` is + specified, starts the scan at the given viewscreen. General-purpose selections ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index cfb8925e3..aeb763e4a 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -189,7 +189,7 @@ namespace DFHack DFHACK_EXPORT df::viewscreen *getViewscreenByIdentity(virtual_identity &id, int n = 1); /// Get the top-most underlying DF viewscreen (not owned by DFHack) - DFHACK_EXPORT df::viewscreen *getDFViewscreen(bool skip_dismissed = false); + DFHACK_EXPORT df::viewscreen *getDFViewscreen(bool skip_dismissed = false, df::viewscreen *top = NULL); /// Get the top-most viewscreen of the given type from the top `n` viewscreens (or all viewscreens if n < 1) /// returns NULL if none match diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 0f0afd6e2..c21e3ad32 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -351,6 +351,7 @@ namespace DFHack virtual bool is_lua_screen() { return false; } + virtual bool isFocused() { return true; } virtual std::string getFocusString() = 0; virtual void onShow() {}; virtual void onDismiss() {}; @@ -365,6 +366,7 @@ namespace DFHack class DFHACK_EXPORT dfhack_lua_viewscreen : public dfhack_viewscreen { std::string focus; + bool defocused = false; void update_focus(lua_State *L, int idx); @@ -384,6 +386,7 @@ namespace DFHack static df::viewscreen *get_pointer(lua_State *L, int idx, bool make); virtual bool is_lua_screen() { return true; } + virtual bool isFocused() { return !defocused; } virtual std::string getFocusString() { return focus; } virtual void render(); diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 7d56977a6..1008b0972 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -484,6 +484,12 @@ bool Gui::matchFocusString(std::string focusString, bool prefixMatch) { }) != currentFocusStrings.end(); } +static void push_dfhack_focus_string(dfhack_viewscreen *vs, std::vector &focusStrings) +{ + auto name = vs->getFocusString(); + focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); +} + std::vector Gui::getFocusStrings(df::viewscreen* top) { std::vector focusStrings; @@ -493,10 +499,21 @@ std::vector Gui::getFocusStrings(df::viewscreen* top) if (dfhack_viewscreen::is_instance(top)) { - auto name = static_cast(top)->getFocusString(); - focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); + dfhack_viewscreen *vs = static_cast(top); + if (vs->isFocused()) + { + push_dfhack_focus_string(vs, focusStrings); + return focusStrings; + } + top = Gui::getDFViewscreen(top); + if (dfhack_viewscreen::is_instance(top)) + { + push_dfhack_focus_string(static_cast(top), focusStrings); + return focusStrings; + } } - else if (virtual_identity *id = virtual_identity::get(top)) + + if (virtual_identity *id = virtual_identity::get(top)) { std::string name = getNameChunk(id, 11, 2); @@ -504,7 +521,8 @@ std::vector Gui::getFocusStrings(df::viewscreen* top) if (handler) handler(name, focusStrings, top); } - else + + if (!focusStrings.size()) { Core &core = Core::getInstance(); std::string name = core.p->readClassName(*(void**)top); @@ -1865,8 +1883,9 @@ df::viewscreen *Gui::getViewscreenByIdentity (virtual_identity &id, int n) return NULL; } -df::viewscreen *Gui::getDFViewscreen(bool skip_dismissed) { - df::viewscreen *screen = Gui::getCurViewscreen(skip_dismissed); +df::viewscreen *Gui::getDFViewscreen(bool skip_dismissed, df::viewscreen *screen) { + if (!screen) + screen = Gui::getCurViewscreen(skip_dismissed); while (screen && dfhack_viewscreen::is_instance(screen)) { screen = screen->parent; if (skip_dismissed) diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 13dbcb204..a78a36a3f 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -865,6 +865,9 @@ void dfhack_lua_viewscreen::update_focus(lua_State *L, int idx) lua_getfield(L, idx, "allow_options"); allow_options = lua_toboolean(L, -1); lua_pop(L, 1); + lua_getfield(L, idx, "defocused"); + defocused = lua_toboolean(L, -1); + lua_pop(L, 1); lua_getfield(L, idx, "focus_path"); auto str = lua_tostring(L, -1); @@ -1081,6 +1084,7 @@ using df::identity_traits; #define CUR_STRUCT dfhack_viewscreen static const struct_field_info dfhack_viewscreen_fields[] = { { METHOD(OBJ_METHOD, is_lua_screen), 0, 0 }, + { METHOD(OBJ_METHOD, isFocused), 0, 0 }, { METHOD(OBJ_METHOD, getFocusString), 0, 0 }, { METHOD(OBJ_METHOD, onShow), 0, 0 }, { METHOD(OBJ_METHOD, onDismiss), 0, 0 },