diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index f0c33eb73..58d9ea0b7 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -950,10 +950,18 @@ Screens Returns the topmost viewscreen. If ``skip_dismissed`` is *true*, ignores screens already marked to be removed. -* ``dfhack.gui.getFocusString(viewscreen)`` +* ``dfhack.gui.getFocusStrings(viewscreen)`` - Returns a string representation of the current focus position - in the ui. The string has a "screen/foo/bar/baz..." format. + Returns a table of string representations of the current UI focuses. + The strings have a "screen/foo/bar/baz..." format e.g..:: + + [1] = "dwarfmode/Info/CREATURES" + [2] = "dwardmode/Squads" + +* ``dfhack.gui.matchFocusString(focus_string)`` + + Returns ``true`` if the given ``focus_string`` is found in current focus, or ``false`` + if no match is found. Matching is case insensitive. * ``dfhack.gui.getCurFocus([skip_dismissed])`` diff --git a/library/Core.cpp b/library/Core.cpp index 98582d8cd..0a9d5f948 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -947,8 +947,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v << "Supported keys: [Ctrl-][Alt-][Shift-](A-Z, 0-9, F1-F12, `, or Enter)." << endl << "Context may be used to limit the scope of the binding, by" << endl << "requiring the current context to have a certain prefix." << endl - << "Current UI context is: " - << Gui::getFocusString(Core::getTopViewscreen()) << endl; + << "Current UI context is: " << endl + << join_strings("\n", Gui::getFocusStrings(Gui::getDFViewscreen())) << endl; } } else if (first == "alias") @@ -2419,11 +2419,22 @@ bool Core::SelectHotkey(int sym, int modifiers) binding.modifiers, modifiers); continue; } - string focusString = Gui::getFocusString(screen); - if (!binding.focus.empty() && !prefix_matches(binding.focus, focusString)) { - DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", - focusString.c_str(), binding.focus.c_str()); - continue; + if (!binding.focus.empty()) { + // TODO: understand more about this to figure out if this solution works + bool found = false; + std::vector focusStrings = Gui::getFocusStrings(Core::getTopViewscreen()); + // is there convention for when to use size_t vs int? + for (std::string focusString : focusStrings) { + if (prefix_matches(binding.focus, focusString)) { + found = true; + } + } + + if (!found) { + DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", + join_strings(", ", focusStrings), binding.focus.c_str()); + continue; + } } if (!plug_mgr->CanInvokeHotkey(binding.command[0], screen)) { DEBUG(keybinding).print("skipping keybinding due to hotkey guard rejection (command: '%s')\n", diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 39b7364bd..7cb8d9825 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1471,13 +1471,13 @@ static int gui_getMousePos(lua_State *L) static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, getCurViewscreen), WRAPM(Gui, getDFViewscreen), - WRAPM(Gui, getFocusString), WRAPM(Gui, getCurFocus), WRAPM(Gui, getSelectedWorkshopJob), WRAPM(Gui, getSelectedJob), WRAPM(Gui, getSelectedUnit), WRAPM(Gui, getSelectedItem), WRAPM(Gui, getSelectedBuilding), + WRAPM(Gui, getSelectedStockpile), WRAPM(Gui, getSelectedPlant), WRAPM(Gui, getAnyUnit), WRAPM(Gui, getAnyItem), @@ -1495,9 +1495,17 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = { WRAPM(Gui, refreshSidebar), WRAPM(Gui, inRenameBuilding), WRAPM(Gui, getDepthAt), + WRAPM(Gui, matchFocusString), { NULL, NULL } }; +static int gui_getFocusStrings(lua_State *state) { + df::viewscreen *r = Lua::GetDFObject(state, 1); + std::vector focusStrings = Gui::getFocusStrings(r); + Lua::PushVector(state, focusStrings); + return 1; +} + static int gui_autoDFAnnouncement(lua_State *state) { bool rv; @@ -1623,6 +1631,7 @@ static const luaL_Reg dfhack_gui_funcs[] = { { "pauseRecenter", gui_pauseRecenter }, { "revealInDwarfmodeMap", gui_revealInDwarfmodeMap }, { "getMousePos", gui_getMousePos }, + { "getFocusStrings", gui_getFocusStrings }, { NULL, NULL } }; diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 25415ffa9..0ac48f7bb 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -36,6 +36,7 @@ distribution. #include "df/plotinfost.h" #include "df/announcement_type.h" #include "df/announcement_flags.h" +#include "df/building_stockpilest.h" #include "df/report_init.h" #include "df/report_zoom_type.h" #include "df/unit_report_type.h" @@ -65,7 +66,9 @@ namespace DFHack */ namespace Gui { - DFHACK_EXPORT std::string getFocusString(df::viewscreen *top); + DFHACK_EXPORT std::vector getFocusStrings(df::viewscreen *top); + DFHACK_EXPORT bool matchFocusString(std::string focusString); + // Full-screen item details view DFHACK_EXPORT bool item_details_hotkey(df::viewscreen *top); @@ -107,6 +110,10 @@ namespace DFHack DFHACK_EXPORT df::building *getAnyBuilding(df::viewscreen *top); DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false); + DFHACK_EXPORT bool any_stockpile_hotkey(df::viewscreen* top); + DFHACK_EXPORT df::building_stockpilest *getAnyStockpile(df::viewscreen* top); + DFHACK_EXPORT df::building_stockpilest *getSelectedStockpile(color_ostream& out, bool quiet = false); + // A plant is selected, e.g. via 'k' DFHACK_EXPORT bool any_plant_hotkey(df::viewscreen *top); DFHACK_EXPORT df::plant *getAnyPlant(df::viewscreen *top); @@ -191,8 +198,8 @@ namespace DFHack return strict_virtual_cast(getViewscreenByIdentity(T::_identity, n)); } - inline std::string getCurFocus(bool skip_dismissed = false) { - return getFocusString(getCurViewscreen(skip_dismissed)); + inline std::vector getCurFocus(bool skip_dismissed = false) { + return getFocusStrings(getCurViewscreen(skip_dismissed)); } /// get the size of the window buffer diff --git a/library/include/modules/Screen.h b/library/include/modules/Screen.h index 0f0afd6e2..c4fa48f08 100644 --- a/library/include/modules/Screen.h +++ b/library/include/modules/Screen.h @@ -351,7 +351,7 @@ namespace DFHack virtual bool is_lua_screen() { return false; } - virtual std::string getFocusString() = 0; + virtual std::string getFocusStrings() = 0; virtual void onShow() {}; virtual void onDismiss() {}; virtual df::unit *getSelectedUnit() { return nullptr; } @@ -384,7 +384,7 @@ namespace DFHack static df::viewscreen *get_pointer(lua_State *L, int idx, bool make); virtual bool is_lua_screen() { return true; } - virtual std::string getFocusString() { return focus; } + virtual std::string getFocusStrings() { return focus; } virtual void render(); virtual void logic(); diff --git a/library/lua/gui/dwarfmode.lua b/library/lua/gui/dwarfmode.lua index 675a7228e..1f449dab5 100644 --- a/library/lua/gui/dwarfmode.lua +++ b/library/lua/gui/dwarfmode.lua @@ -48,7 +48,7 @@ function enterSidebarMode(sidebar_mode, max_esc) local focus_string = '' while remaining_esc > 0 do local screen = dfhack.gui.getCurViewscreen(true) - focus_string = dfhack.gui.getFocusString(screen) + focus_string = dfhack.gui.getFocusStrings(screen) if df.global.plotinfo.main.mode == df.ui_sidebar_mode.Default and focus_string == 'dwarfmode/Default' then if #navkey > 0 then gui.simulateInput(screen, navkey) end diff --git a/library/modules/Gui.cpp b/library/modules/Gui.cpp index 858014feb..fed4b0aca 100644 --- a/library/modules/Gui.cpp +++ b/library/modules/Gui.cpp @@ -130,479 +130,261 @@ static std::string getNameChunk(virtual_identity *id, int start, int end) * Classifying focus context by means of a string path. */ -typedef void (*getFocusStringHandler)(std::string &str, df::viewscreen *screen); -static std::map getFocusStringHandlers; +typedef void (*getFocusStringsHandler)(std::string &str, std::vector &strList, df::viewscreen *screen); +static std::map getFocusStringsHandlers; #define VIEWSCREEN(name) df::viewscreen_##name##st #define DEFINE_GET_FOCUS_STRING_HANDLER(screen_type) \ - static void getFocusString_##screen_type(std::string &focus, VIEWSCREEN(screen_type) *screen);\ + static void getFocusStrings_##screen_type(std::string &baseFocus, std::vector &focusStrings, VIEWSCREEN(screen_type) *screen);\ DFHACK_STATIC_ADD_TO_MAP(\ - &getFocusStringHandlers, &VIEWSCREEN(screen_type)::_identity, \ - (getFocusStringHandler)getFocusString_##screen_type \ + &getFocusStringsHandlers, &VIEWSCREEN(screen_type)::_identity, \ + (getFocusStringsHandler)getFocusStrings_##screen_type \ ); \ - static void getFocusString_##screen_type(std::string &focus, VIEWSCREEN(screen_type) *screen) + static void getFocusStrings_##screen_type(std::string &baseFocus, std::vector &focusStrings, VIEWSCREEN(screen_type) *screen) DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) { -/* TODO: understand how this changes for v50 - using namespace df::enums::ui_sidebar_mode; - - using df::global::ui_workshop_in_add; - using df::global::ui_build_selector; - using df::global::ui_selected_unit; - using df::global::ui_look_list; - using df::global::ui_look_cursor; - using df::global::ui_building_item_cursor; - using df::global::ui_building_assign_type; - using df::global::ui_building_assign_is_marked; - using df::global::ui_building_assign_units; - using df::global::ui_building_assign_items; - using df::global::ui_building_in_assign; - - focus += "/" + enum_item_key(plotinfo->main.mode); - - switch (plotinfo->main.mode) - { - case QueryBuilding: - if (df::building *selected = world->selected_building) - { - if (!selected->jobs.empty() && - selected->jobs[0]->job_type == job_type::DestroyBuilding) - { - focus += "/Destroying"; - break; - } - - focus += "/Some"; - - virtual_identity *id = virtual_identity::get(selected); - - bool jobs = false; - - if (id == &df::building_workshopst::_identity || - id == &df::building_furnacest::_identity) - { - focus += "/Workshop"; - jobs = true; - } - else if (id == &df::building_trapst::_identity) - { - auto trap = (df::building_trapst*)selected; - focus += "/" + enum_item_key(trap->trap_type); - if (trap->trap_type == trap_type::Lever) - jobs = true; - } - else if (ui_building_in_assign && *ui_building_in_assign && - ui_building_assign_type && ui_building_assign_units && - ui_building_assign_type->size() == ui_building_assign_units->size()) - { - focus += "/Assign"; - if (ui_building_item_cursor) - { - auto unit = vector_get(*ui_building_assign_units, *ui_building_item_cursor); - focus += unit ? "/Unit" : "/None"; - } - } - else - focus += "/" + enum_item_key(selected->getType()); - - if (jobs) - { - if (ui_workshop_in_add && *ui_workshop_in_add) - focus += "/AddJob"; - else if (!selected->jobs.empty()) - focus += "/Job"; - else - focus += "/Empty"; - } + std::string newFocusString; + + if (game->main_interface.info.open) { + newFocusString = baseFocus; + newFocusString += "/Info"; + newFocusString += "/" + enum_item_key(game->main_interface.info.current_mode); + focusStrings.push_back(newFocusString); + } + if (game->main_interface.view_sheets.open) { + newFocusString = baseFocus; + newFocusString += "/ViewSheets"; + newFocusString += "/" + enum_item_key(game->main_interface.view_sheets.active_sheet); + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::STOCKPILE) { + newFocusString = baseFocus; + // TODO: learn more about where /Some was used previously to ensure proper/consistent usage + if (game->main_interface.stockpile.cur_bld) { + newFocusString += "/Some"; } - else - focus += "/None"; - break; - - case Build: - if (ui_build_selector) - { - // Not selecting, or no choices? - if (ui_build_selector->building_type < 0) - focus += "/Type"; - else if (ui_build_selector->stage != 2) - { - if (ui_build_selector->stage != 1) - focus += "/NoMaterials"; - else - focus += "/Position"; - - focus += "/" + enum_item_key(ui_build_selector->building_type); - } - else - { - focus += "/Material"; - if (ui_build_selector->is_grouped) - focus += "/Groups"; - else - focus += "/Items"; - } - } - break; - - case ViewUnits: - if (ui_selected_unit) - { - if (vector_get(world->units.active, *ui_selected_unit)) - { - focus += "/Some"; - - using df::global::ui_unit_view_mode; - - if (ui_unit_view_mode) - focus += "/" + enum_item_key(ui_unit_view_mode->value); - } - else - focus += "/None"; - } - break; - - case LookAround: - if (ui_look_list && ui_look_cursor) - { - auto item = vector_get(ui_look_list->items, *ui_look_cursor); - if (item) - focus += "/" + enum_item_key(item->type); - else - focus += "/None"; - } - break; - - case BuildingItems: - if (VIRTUAL_CAST_VAR(selected, df::building_actual, world->selected_building)) - { - if (selected->contained_items.empty()) - focus += "/Some/Empty"; - else - focus += "/Some/Item"; - } - else - focus += "/None"; - break; - - case ZonesPenInfo: - if (ui_building_assign_type && ui_building_assign_units && - ui_building_assign_is_marked && ui_building_assign_items && - ui_building_assign_type->size() == ui_building_assign_units->size()) - { - focus += "/Assign"; - if (ui_building_item_cursor) - { - if (vector_get(*ui_building_assign_units, *ui_building_item_cursor)) - focus += "/Unit"; - else if (vector_get(*ui_building_assign_items, *ui_building_item_cursor)) - focus += "/Vermin"; - else - focus += "/None"; - } - } - break; - - case Burrows: - if (plotinfo->burrows.in_confirm_delete) - focus += "/ConfirmDelete"; - else if (plotinfo->burrows.in_add_units_mode) - focus += "/AddUnits"; - else if (plotinfo->burrows.in_edit_name_mode) - focus += "/EditName"; - else if (plotinfo->burrows.in_define_mode) - focus += "/Define"; - else - focus += "/List"; - break; - - case Hauling: - if (plotinfo->hauling.in_assign_vehicle) - { - auto vehicle = vector_get(plotinfo->hauling.vehicles, plotinfo->hauling.cursor_vehicle); - focus += "/AssignVehicle/" + std::string(vehicle ? "Some" : "None"); - } - else - { - int idx = plotinfo->hauling.cursor_top; - auto route = vector_get(plotinfo->hauling.view_routes, idx); - auto stop = vector_get(plotinfo->hauling.view_stops, idx); - std::string tag = stop ? "Stop" : (route ? "Route" : "None"); - - if (plotinfo->hauling.in_name) - focus += "/Rename/" + tag; - else if (plotinfo->hauling.in_stop) - { - int sidx = plotinfo->hauling.cursor_stop; - auto cond = vector_get(plotinfo->hauling.stop_conditions, sidx); - auto link = vector_get(plotinfo->hauling.stop_links, sidx); - - focus += "/DefineStop"; - - if (cond) - focus += "/Cond/" + enum_item_key(cond->mode); - else if (link) - { - focus += "/Link/"; - if (link->mode.bits.give) focus += "Give"; - if (link->mode.bits.take) focus += "Take"; - } - else - focus += "/None"; - } - else - focus += "/Select/" + tag; - } - break; - - default: - break; + newFocusString += "/Stockpile"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::HAULING) { + newFocusString = baseFocus; + newFocusString += "/Hauling"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.trade.open) { + newFocusString = baseFocus; + newFocusString += "/Trade"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.job_details.open) { + newFocusString = baseFocus; + newFocusString += "/JobDetails"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_trade.open) { + newFocusString = baseFocus; + newFocusString += "/AssignTrade"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.diplomacy.open) { + newFocusString = baseFocus; + newFocusString += "/Diplomacy"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.petitions.open) { + newFocusString = baseFocus; + newFocusString += "/Petitions"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.stocks.open) { + newFocusString = baseFocus; + newFocusString += "/Stocks"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_display_item.open) { + newFocusString = baseFocus; + newFocusString += "/AssignDisplayItem"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.name_creator.open) { + newFocusString = baseFocus; + newFocusString += "/NameCreator"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.image_creator.open) { + newFocusString = baseFocus; + newFocusString += "/ImageCreator"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.unit_selector.open) { + newFocusString = baseFocus; + newFocusString += "/UnitSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.announcement_alert.open) { + newFocusString = baseFocus; + newFocusString += "/AnnouncementAlert"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.custom_symbol.open) { + newFocusString = baseFocus; + newFocusString += "/CustomSymbol"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.patrol_routes.open) { + newFocusString = baseFocus; + newFocusString += "/PatrolRoutes"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_equipment.open) { + newFocusString = baseFocus; + newFocusString += "/SquadEquipment"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_schedule.open) { + newFocusString = baseFocus; + newFocusString += "/SquadSchedule"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_selector.open) { + newFocusString = baseFocus; + newFocusString += "/SquadSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.burrow_selector.open) { + newFocusString = baseFocus; + newFocusString += "/BurrowSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.location_selector.open) { + newFocusString = baseFocus; + newFocusString += "/LocationSelector"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.location_details.open) { + newFocusString = baseFocus; + newFocusString += "/LocationDetails"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.hauling_stop_conditions.open) { + newFocusString = baseFocus; + newFocusString += "/HaulingStopConditions"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_vehicle.open) { + newFocusString = baseFocus; + newFocusString += "/AssignVehicle"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.stockpile_link.open) { + newFocusString = baseFocus; + newFocusString += "/StockpileLink"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.stockpile_tools.open) { + newFocusString = baseFocus; + newFocusString += "/StockpileTools"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.custom_stockpile.open) { + newFocusString = baseFocus; + newFocusString += "/CustomStockpile"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.create_squad.open) { + newFocusString = baseFocus; + newFocusString += "/CreateSquad"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.squad_supplies.open) { + newFocusString = baseFocus; + newFocusString += "/SquadSupplies"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.assign_uniform.open) { + newFocusString = baseFocus; + newFocusString += "/AssignUniform"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.create_work_order.open) { + newFocusString = baseFocus; + newFocusString += "/CreateWorkOrder"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.hotkey.open) { + newFocusString = baseFocus; + newFocusString += "/Hotkey"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.options.open) { + newFocusString = baseFocus; + newFocusString += "/Options"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.help.open) { + newFocusString = baseFocus; + newFocusString += "/Help"; + focusStrings.push_back(newFocusString); + } + if (game->main_interface.settings.open) { + newFocusString = baseFocus; + newFocusString += "/Settings"; + focusStrings.push_back(newFocusString); } -*/ -} - -/* TODO: understand how this changes for v50 -DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) -{ - using df::global::adventure; - - if (!adventure) - return; - - focus += "/" + enum_item_key(adventure->menu); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(unitlist) -{ - focus += "/" + enum_item_key(screen->page); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_military) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - auto list3 = getLayerList(screen, 2); - if (!list1 || !list2 || !list3) return; - - focus += "/" + enum_item_key(screen->page); - - int cur_list; - if (list1->active) cur_list = 0; - else if (list2->active) cur_list = 1; - else if (list3->active) cur_list = 2; - else return; - - switch (screen->page) - { - case df::viewscreen_layer_militaryst::Positions: - { - static const char *lists[] = { "/Squads", "/Positions", "/Candidates" }; - focus += lists[cur_list]; - break; - } - - case df::viewscreen_layer_militaryst::Equip: - { - focus += "/" + enum_item_key(screen->equip.mode); - - switch (screen->equip.mode) - { - case df::viewscreen_layer_militaryst::T_equip::Customize: - { - if (screen->equip.edit_mode < 0) - focus += "/View"; - else - focus += "/" + enum_item_key(screen->equip.edit_mode); - break; - } - case df::viewscreen_layer_militaryst::T_equip::Uniform: - break; - case df::viewscreen_layer_militaryst::T_equip::Priority: - { - if (screen->equip.prio_in_move >= 0) - focus += "/Move"; - else - focus += "/View"; - break; - } - } - - static const char *lists[] = { "/Squads", "/Positions", "/Choices" }; - focus += lists[cur_list]; - break; - } - - default: - break; - } -} - -DEFINE_GET_FOCUS_STRING_HANDLER(workshop_profile) -{ - typedef df::viewscreen_workshop_profilest::T_tab T_tab; - switch(screen->tab) - { - case T_tab::Workers: - focus += "/Unit"; - break; - case T_tab::Orders: - focus += "/Orders"; - break; - case T_tab::Restrictions: - focus += "/Restrictions"; - break; - } -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_noblelist) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - if (!list1 || !list2) return; - - focus += "/" + enum_item_key(screen->mode); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(pet) -{ - focus += "/" + enum_item_key(screen->mode); - - switch (screen->mode) - { - case df::viewscreen_petst::List: - focus += vector_get(screen->is_vermin, screen->cursor) ? "/Vermin" : "/Unit"; - break; - - case df::viewscreen_petst::SelectTrainer: - if (vector_get(screen->trainer_unit, screen->trainer_cursor)) - focus += "/Unit"; - break; - - default: - break; - } -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_overall_health) -{ - auto list1 = getLayerList(screen, 0); - if (!list1) return; - - focus += "/Units"; -} - -DEFINE_GET_FOCUS_STRING_HANDLER(tradegoods) -{ - if (!screen->has_traders || screen->is_unloading) - focus += "/NoTraders"; - else if (screen->in_edit_count) - focus += "/EditCount"; - else - focus += (screen->in_right_pane ? "/Items/Broker" : "/Items/Trader"); -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_assigntrade) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - if (!list1 || !list2) return; - - int list_idx = vector_get(screen->visible_lists, list1->cursor, (int16_t)-1); - unsigned num_lists = sizeof(screen->lists)/sizeof(screen->lists[0]); - if (unsigned(list_idx) >= num_lists) - return; - - if (list1->active) - focus += "/Groups"; - else - focus += "/Items"; -} - -DEFINE_GET_FOCUS_STRING_HANDLER(stores) -{ - if (!screen->in_right_list) - focus += "/Categories"; - else if (screen->in_group_mode) - focus += "/Groups"; - else - focus += "/Items"; -} - -DEFINE_GET_FOCUS_STRING_HANDLER(layer_stockpile) -{ - auto list1 = getLayerList(screen, 0); - auto list2 = getLayerList(screen, 1); - auto list3 = getLayerList(screen, 2); - if (!list1 || !list2 || !list3 || !screen->settings) return; - - auto group = screen->cur_group; - if (group != vector_get(screen->group_ids, list1->cursor)) - return; - - focus += "/" + enum_item_key(group); - - auto bits = vector_get(screen->group_bits, list1->cursor); - if (bits.whole && !(bits.whole & screen->settings->flags.whole)) - { - focus += "/Off"; - return; + if (game->main_interface.squad_equipment.open) { + newFocusString = baseFocus; + newFocusString += "/SquadEquipment"; + focusStrings.push_back(newFocusString); + } + // squads should be last because it's the only one not exclusive with the others? or something? + if (game->main_interface.squads.open) { + newFocusString = baseFocus; + newFocusString += "/Squads"; + focusStrings.push_back(newFocusString); } - focus += "/On"; - - if (list2->active || list3->active || screen->list_ids.empty()) { - focus += "/" + enum_item_key(screen->cur_list); - - if (list3->active) - focus += (screen->item_names.empty() ? "/None" : "/Item"); + if (!newFocusString.size()) { + focusStrings.push_back(baseFocus); } } -DEFINE_GET_FOCUS_STRING_HANDLER(locations) -{ - focus += "/" + enum_item_key(screen->menu); -} +bool Gui::matchFocusString(std::string focusString) { + focusString = toLower(focusString); + std::vector currentFocus = getFocusStrings(Core::getTopViewscreen()); -DEFINE_GET_FOCUS_STRING_HANDLER(jobmanagement) -{ - focus += (screen->in_max_workshops ? "/MaxWorkshops" : "/Main"); + return std::find_if(currentFocus.begin(), currentFocus.end(), [&focusString](std::string item) { + return focusString == toLower(item); + }) != currentFocus.end(); } -DEFINE_GET_FOCUS_STRING_HANDLER(workquota_condition) +std::vector Gui::getFocusStrings(df::viewscreen* top) { - focus += "/" + enum_item_key(screen->mode); - if (screen->item_count_edit) - focus += "/EditCount"; -} -*/ + std::vector focusStrings; -std::string Gui::getFocusString(df::viewscreen *top) -{ if (!top) - return ""; + return focusStrings; if (dfhack_viewscreen::is_instance(top)) { - auto name = static_cast(top)->getFocusString(); - return name.empty() ? "dfhack" : "dfhack/"+name; + auto name = static_cast(top)->getFocusStrings(); + focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name); } else if (virtual_identity *id = virtual_identity::get(top)) { std::string name = getNameChunk(id, 11, 2); - auto handler = map_find(getFocusStringHandlers, id); + auto handler = map_find(getFocusStringsHandlers, id); if (handler) - handler(name, top); - - return name; + handler(name, focusStrings, top); } else { Core &core = Core::getInstance(); std::string name = core.p->readClassName(*(void**)top); - return name.substr(11, name.size()-11-2); + focusStrings.push_back(name.substr(11, name.size()-11-2)); } + return focusStrings; } // Predefined common guard functions @@ -1269,6 +1051,28 @@ df::item *Gui::getSelectedItem(color_ostream &out, bool quiet) return item; } +bool Gui::any_stockpile_hotkey(df::viewscreen* top) +{ + return getAnyStockpile(top) != NULL; +} + +df::building_stockpilest* Gui::getAnyStockpile(df::viewscreen* top) { + if (matchFocusString("dwarfmode/Some/Stockpile")) { + return game->main_interface.stockpile.cur_bld; + } + + return NULL; +} + +df::building_stockpilest* Gui::getSelectedStockpile(color_ostream& out, bool quiet) { + df::building_stockpilest* stockpile = getAnyStockpile(Core::getTopViewscreen()); + + if (!stockpile && !quiet) + out.printerr("No stockpile is selected in the UI.\n"); + + return stockpile; +} + df::building *Gui::getAnyBuilding(df::viewscreen *top) { using df::global::game; diff --git a/library/modules/Screen.cpp b/library/modules/Screen.cpp index 13dbcb204..74d457140 100644 --- a/library/modules/Screen.cpp +++ b/library/modules/Screen.cpp @@ -1081,7 +1081,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, getFocusString), 0, 0 }, + { METHOD(OBJ_METHOD, getFocusStrings), 0, 0 }, { METHOD(OBJ_METHOD, onShow), 0, 0 }, { METHOD(OBJ_METHOD, onDismiss), 0, 0 }, { METHOD(OBJ_METHOD, getSelectedUnit), 0, 0 }, diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1c11cd797..9ce25a4cc 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -97,7 +97,7 @@ add_subdirectory(channel-safely) dfhack_plugin(cleanconst cleanconst.cpp) dfhack_plugin(cleaners cleaners.cpp) dfhack_plugin(cleanowned cleanowned.cpp) -#dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) +dfhack_plugin(confirm confirm.cpp LINK_LIBRARIES lua) #dfhack_plugin(createitem createitem.cpp) dfhack_plugin(cursecheck cursecheck.cpp) dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) diff --git a/plugins/buildingplan/buildingplan.cpp b/plugins/buildingplan/buildingplan.cpp index cd4e84a6e..d4471a888 100644 --- a/plugins/buildingplan/buildingplan.cpp +++ b/plugins/buildingplan/buildingplan.cpp @@ -56,7 +56,7 @@ public: void render(); - std::string getFocusString() { return "buildingplan_choosemat"; } + std::string getFocusStrings() { return "buildingplan_choosemat"; } private: ListColumn masks_column; diff --git a/plugins/confirm.cpp b/plugins/confirm.cpp index 499d3f08f..80057ce4c 100644 --- a/plugins/confirm.cpp +++ b/plugins/confirm.cpp @@ -16,15 +16,11 @@ #include "uicommon.h" #include "df/building_tradedepotst.h" +#include "df/gamest.h" #include "df/general_ref.h" #include "df/general_ref_contained_in_itemst.h" #include "df/interfacest.h" #include "df/viewscreen_dwarfmodest.h" -#include "df/viewscreen_jobmanagementst.h" -#include "df/viewscreen_justicest.h" -#include "df/viewscreen_layer_militaryst.h" -#include "df/viewscreen_locationsst.h" -#include "df/viewscreen_tradegoodsst.h" using namespace DFHack; using namespace df::enums; @@ -32,11 +28,12 @@ using std::map; using std::queue; using std::string; using std::vector; +using df::global::game; DFHACK_PLUGIN("confirm"); DFHACK_PLUGIN_IS_ENABLED(is_enabled); +REQUIRE_GLOBAL(game) REQUIRE_GLOBAL(gps); -REQUIRE_GLOBAL(plotinfo); typedef std::set ikey_set; command_result df_confirm (color_ostream &out, vector & parameters); @@ -51,6 +48,8 @@ bool paused = false; // if set, confirm will unpause when this screen is no longer on the stack df::viewscreen *paused_screen = NULL; +std::string paused_focus = ""; + namespace DFHack { DBG_DECLARE(confirm,status); } @@ -121,25 +120,25 @@ public: }; namespace trade { - static bool goods_selected (const std::vector &selected) + static bool goods_selected (std::vector &selected) { - for (char c : selected) - if (c) + for (uint8_t sel : selected) + if (sel == 1) return true; return false; } - inline bool trader_goods_selected (df::viewscreen_tradegoodsst *screen) + inline bool trader_goods_selected (df::viewscreen_dwarfmodest *screen) { CHECK_NULL_POINTER(screen); - return goods_selected(screen->trader_selected); + return goods_selected(game->main_interface.trade.goodflag[0]); } - inline bool broker_goods_selected (df::viewscreen_tradegoodsst *screen) + inline bool broker_goods_selected (df::viewscreen_dwarfmodest*screen) { CHECK_NULL_POINTER(screen); - return goods_selected(screen->broker_selected); + return goods_selected(game->main_interface.trade.goodflag[1]); } - static bool goods_all_selected(const std::vector &selected, const std::vector &items) \ + /*static bool goods_all_selected(const std::vector& selected, const std::vector& items) \ { for (size_t i = 0; i < selected.size(); ++i) { @@ -162,16 +161,16 @@ namespace trade { } return true; } - inline bool trader_goods_all_selected(df::viewscreen_tradegoodsst *screen) + inline bool trader_goods_all_selected(df::viewscreen_dwarfmodest*screen) { CHECK_NULL_POINTER(screen); - return goods_all_selected(screen->trader_selected, screen->trader_items); + return false;// goods_all_selected(screen->trader_selected, screen->trader_items); } - inline bool broker_goods_all_selected(df::viewscreen_tradegoodsst *screen) + inline bool broker_goods_all_selected(df::viewscreen_dwarfmodest*screen) { CHECK_NULL_POINTER(screen); - return goods_all_selected(screen->broker_selected, screen->broker_items); - } + return false;// goods_all_selected(screen->broker_selected, screen->broker_items); + }*/ } namespace conf_lua { @@ -245,6 +244,7 @@ namespace conf_lua { DEBUG(status).print("unpausing\n"); paused = false; paused_screen = NULL; + paused_focus = ""; return 0; } int get_paused (lua_State *L) @@ -259,9 +259,9 @@ namespace conf_lua { DFHACK_PLUGIN_LUA_FUNCTIONS { CONF_LUA_FUNC( , set_conf_state), CONF_LUA_FUNC(trade, broker_goods_selected), - CONF_LUA_FUNC(trade, broker_goods_all_selected), + //CONF_LUA_FUNC(trade, broker_goods_all_selected), CONF_LUA_FUNC(trade, trader_goods_selected), - CONF_LUA_FUNC(trade, trader_goods_all_selected), + //CONF_LUA_FUNC(trade, trader_goods_all_selected), DFHACK_LUA_END }; @@ -306,16 +306,56 @@ public: return true; } bool feed (ikey_set *input) { + bool mouseExit = false; + if(df::global::enabler->mouse_rbut) { + mouseExit = true; + } + bool mouseSelect = false; + if(df::global::enabler->mouse_lbut) { + mouseSelect = true; + } + if (paused) { - // we can only detect that we've left the screen by intercepting the - // ESC key - if (!paused_screen && input->count(df::interface_key::LEAVESCREEN)) + // TODO: fix + if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit)) + if(mouseExit) { + df::global::enabler->mouse_rbut = 0; + df::global::enabler->mouse_rbut_down = 0; + } conf_lua::api::unpause(NULL); return false; } else if (state == INACTIVE) { + if(mouseExit) { + if(intercept_key("MOUSE_RIGHT")) { + if (set_state(ACTIVE)) + { + df::global::enabler->mouse_rbut = 0; + df::global::enabler->mouse_rbut_down = 0; + mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); + last_key_is_right_click = true; + return true; + } + } + } else + last_key_is_right_click = false; + + if(mouseSelect) { + if(intercept_key("MOUSE_LEFT")) { + if (set_state(ACTIVE)) + { + df::global::enabler->mouse_lbut = 0; + df::global::enabler->mouse_lbut_down = 0; + mouse_pos = df::coord2d(df::global::gps->mouse_x, df::global::gps->mouse_y); + last_key_is_left_click = true; + return true; + } + } + } else + last_key_is_left_click = false; + for (df::interface_key key : *input) { if (intercept_key(key)) @@ -331,20 +371,31 @@ public: } else if (state == ACTIVE) { - if (input->count(df::interface_key::LEAVESCREEN)) + if (input->count(df::interface_key::LEAVESCREEN) || mouseExit) { + if(mouseExit) { + df::global::enabler->mouse_rbut = 0; + df::global::enabler->mouse_rbut_down = 0; + } set_state(INACTIVE); - else if (input->count(df::interface_key::SELECT)) + } else if (input->count(df::interface_key::SELECT)) set_state(SELECTED); else if (input->count(df::interface_key::CUSTOM_P)) { + // TODO: fix DEBUG(status).print("pausing\n"); paused = true; // only record the screen when we're not at the top viewscreen // since this screen will *always* be on the stack. for // dwarfmode screens, use ESC detection to discover when to // unpause - if (!df::viewscreen_dwarfmodest::_identity.is_instance(screen)) - paused_screen = screen; + + std::vector focusStrings = Gui::getFocusStrings(Gui::getCurViewscreen()); + std::string current_focus = focusStrings[0]; + + if (current_focus != "dwarfmode") { + paused_focus = current_focus; + } + set_state(INACTIVE); } else if (input->count(df::interface_key::CUSTOM_S)) @@ -429,8 +480,27 @@ public: else if (state == SELECTED) { ikey_set tmp; - tmp.insert(last_key); - screen->feed(&tmp); + if(last_key_is_left_click) { + long prevx = df::global::gps->mouse_x; + long prevy = df::global::gps->mouse_y; + df::global::gps->mouse_x = mouse_pos.x; + df::global::gps->mouse_y = mouse_pos.y; + df::global::enabler->mouse_lbut = 1; + df::global::enabler->mouse_lbut_down = 1; + screen->feed(&tmp); + df::global::enabler->mouse_lbut = 0; + df::global::enabler->mouse_lbut_down = 0; + df::global::gps->mouse_x = prevx; + df::global::gps->mouse_y = prevy; + } + else if(last_key_is_right_click) { + tmp.insert(df::interface_key::LEAVESCREEN); + screen->feed(&tmp); + } + else { + tmp.insert(last_key); + screen->feed(&tmp); + } set_state(INACTIVE); } } @@ -445,6 +515,15 @@ public: else return false; }; + bool intercept_key (std::string mouse_button = "left") + { + CONF_LUA_START; + push(mouse_button); + if (call("intercept_key", 3, 1)) + return lua_toboolean(l_state, -1); + else + return false; + }; string get_title() { CONF_LUA_START; @@ -473,6 +552,9 @@ public: protected: cstate state; df::interface_key last_key; + bool last_key_is_left_click; + bool last_key_is_right_click; + df::coord2d mouse_pos; }; template @@ -501,18 +583,12 @@ struct cls##_hooks : cls::screen_type { \ INTERPOSE_NEXT(render)(); \ cls##_instance.render(); \ } \ - DEFINE_VMETHOD_INTERPOSE(bool, key_conflict, (df::interface_key key)) \ - { \ - return cls##_instance.key_conflict(key) || INTERPOSE_NEXT(key_conflict)(key); \ - } \ }; \ IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, feed, prio); \ IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, render, prio); \ -IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, key_conflict, prio); \ static int conf_register_##cls = conf_register(&cls##_instance, {\ &INTERPOSE_HOOK(cls##_hooks, feed), \ &INTERPOSE_HOOK(cls##_hooks, render), \ - &INTERPOSE_HOOK(cls##_hooks, key_conflict), \ }); #define DEFINE_CONFIRMATION(cls, screen) \ @@ -526,20 +602,21 @@ static int conf_register_##cls = conf_register(&cls##_instance, {\ IDs (used in the "confirm enable/disable" command, by Lua, and in the docs) are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION */ -DEFINE_CONFIRMATION(trade, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_cancel, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_seize, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_offer, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(trade_select_all, viewscreen_tradegoodsst); -DEFINE_CONFIRMATION(haul_delete, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(squad_disband, viewscreen_layer_militaryst); -DEFINE_CONFIRMATION(uniform_delete, viewscreen_layer_militaryst); -DEFINE_CONFIRMATION(note_delete, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(route_delete, viewscreen_dwarfmodest); -DEFINE_CONFIRMATION(location_retire, viewscreen_locationsst); -DEFINE_CONFIRMATION(convict, viewscreen_justicest); -DEFINE_CONFIRMATION(order_remove, viewscreen_jobmanagementst); +DEFINE_CONFIRMATION(trade, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(trade_seize, viewscreen_tradegoodsst); +//DEFINE_CONFIRMATION(trade_offer, viewscreen_tradegoodsst); +//DEFINE_CONFIRMATION(trade_select_all, viewscreen_tradegoodsst); +DEFINE_CONFIRMATION(haul_delete_route, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(haul_delete_stop, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); +DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(uniform_delete, viewscreen_layer_militaryst); +//DEFINE_CONFIRMATION(note_delete, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(route_delete, viewscreen_dwarfmodest); +//DEFINE_CONFIRMATION(location_retire, viewscreen_locationsst); +//DEFINE_CONFIRMATION(convict, viewscreen_justicest); +//DEFINE_CONFIRMATION(order_remove, viewscreen_jobmanagementst); DFhackCExport command_result plugin_init (color_ostream &out, vector &commands) { @@ -587,18 +664,10 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) static bool screen_found(df::viewscreen *target_screen) { - if (!df::global::gview) + if (!&game->main_interface) return false; - df::viewscreen *screen = &df::global::gview->view; - while (screen) - { - if (screen == target_screen) - return true; - screen = screen->child; - } - - return false; + return target_screen == Gui::getCurViewscreen(); } DFhackCExport command_result plugin_onupdate (color_ostream &out) @@ -610,7 +679,7 @@ DFhackCExport command_result plugin_onupdate (color_ostream &out) } // if the screen that we paused on is no longer on the stack, unpause - if (paused_screen && !screen_found(paused_screen)) + if (paused_focus != "" && Gui::getFocusStrings(Gui::getCurViewscreen())[0] != paused_focus) conf_lua::api::unpause(NULL); return CR_OK; diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 2b546bfad..04c58c1e4 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -410,7 +410,7 @@ public: OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); } - std::string getFocusString() { return "dwarfmonitor_dwarfstats"; } + std::vector getFocusStrings() { return std::vector{"dwarfmonitor_dwarfstats"}; } private: ListColumn dwarves_column; @@ -1021,7 +1021,7 @@ public: OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); } - std::string getFocusString() { return "dwarfmonitor_fortstats"; } + std::vector getFocusStrings() { return std::vector{"dwarfmonitor_fortstats"}; } private: ListColumn fort_activity_column, category_breakdown_column; @@ -1652,7 +1652,7 @@ public: getSelectedUnit() ? COLOR_WHITE : COLOR_DARKGREY); } - std::string getFocusString() override { return "dwarfmonitor_preferences"; } + std::vector getFocusStrings() override { return std::vector{"dwarfmonitor_preferences"}; } private: ListColumn preferences_column; diff --git a/plugins/embark-assistant/finder_ui.cpp b/plugins/embark-assistant/finder_ui.cpp index 6501f7f21..764b2bdb5 100644 --- a/plugins/embark-assistant/finder_ui.cpp +++ b/plugins/embark-assistant/finder_ui.cpp @@ -1613,7 +1613,7 @@ namespace embark_assist { void render(); - std::string getFocusString() { return "Finder UI"; } + std::vector getFocusStrings() { return std::vector{"Finder UI"}; } private: }; diff --git a/plugins/embark-assistant/help_ui.cpp b/plugins/embark-assistant/help_ui.cpp index a49a9b5e0..5e77f680b 100644 --- a/plugins/embark-assistant/help_ui.cpp +++ b/plugins/embark-assistant/help_ui.cpp @@ -31,7 +31,7 @@ namespace embark_assist{ void render(); - std::string getFocusString() { return "Help UI"; } + std::vector getFocusStrings() { return std::vector{"Help UI"}; } private: pages current_page = pages::Intro; diff --git a/plugins/embark-tools.cpp b/plugins/embark-tools.cpp index 924def798..739840ab4 100644 --- a/plugins/embark-tools.cpp +++ b/plugins/embark-tools.cpp @@ -572,7 +572,7 @@ public: embark_tools_settings () { }; ~embark_tools_settings () { }; void help () { }; - std::string getFocusString () { return "embark-tools/options"; }; + std::vector getFocusStrings () { return std::vector{"embark-tools/options"}; }; void render () { parent->render(); diff --git a/plugins/hotkeys.cpp b/plugins/hotkeys.cpp index 6bb6a1600..609941573 100644 --- a/plugins/hotkeys.cpp +++ b/plugins/hotkeys.cpp @@ -90,7 +90,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) { valid_keys.push_back("`"); - current_focus = Gui::getFocusString(screen); + current_focus = Gui::getFocusStrings(screen)[0]; for (int shifted = 0; shifted < 2; shifted++) { for (int alt = 0; alt < 2; alt++) { for (int ctrl = 0; ctrl < 2; ctrl++) { @@ -158,7 +158,7 @@ static void list(color_ostream &out) { static bool invoke_command(color_ostream &out, const size_t index) { auto screen = Core::getTopViewscreen(); if (sorted_keys.size() <= index || - Gui::getFocusString(screen) != MENU_SCREEN_FOCUS_STRING) + !Gui::matchFocusString(MENU_SCREEN_FOCUS_STRING)) return false; auto cmd = current_bindings[sorted_keys[index]]; diff --git a/plugins/lua/confirm.lua b/plugins/lua/confirm.lua index 736f13fe1..314c37eda 100644 --- a/plugins/lua/confirm.lua +++ b/plugins/lua/confirm.lua @@ -11,6 +11,9 @@ setmetatable(keys, { end, __newindex = function() error('Table is read-only') end }) +-- Mouse keys will be sent as a string instead of interface_key +local MOUSE_LEFT = "MOUSE_LEFT" +local MOUSE_RIGHT = "MOUSE_RIGHT" --[[ The screen where a confirmation has been triggered Note that this is *not* necessarily the topmost viewscreen, so do not use gui.getCurViewscreen() or related functions. ]] @@ -57,8 +60,7 @@ is equivalent to: trade = defconf('trade') function trade.intercept_key(key) - return screen.in_edit_count == 0 and - key == keys.TRADE_TRADE + return false--dfhack.gui.matchFocusString("dwarfmode/Trade") and key == MOUSE_LEFT and hovering over trade button? end trade.title = "Confirm trade" function trade.get_message() @@ -81,14 +83,14 @@ end trade_cancel = defconf('trade-cancel') function trade_cancel.intercept_key(key) - return screen.in_edit_count == 0 and - key == keys.LEAVESCREEN and - (trader_goods_selected(screen) or broker_goods_selected(screen)) + return dfhack.gui.matchFocusString("dwarfmode/Trade") and + (key == keys.LEAVESCREEN or key == MOUSE_RIGHT) and + (trader_goods_selected(screen) or broker_goods_selected(screen)) end trade_cancel.title = "Cancel trade" trade_cancel.message = "Are you sure you want leave this screen?\nSelected items will not be saved." -trade_seize = defconf('trade-seize') +--[[trade_seize = defconf('trade-seize') function trade_seize.intercept_key(key) return screen.in_edit_count == 0 and trader_goods_selected(screen) and @@ -119,31 +121,28 @@ function trade_select_all.intercept_key(key) end trade_select_all.title = "Confirm selection" trade_select_all.message = "Selecting all goods will overwrite your current selection\n" .. - "and cannot be undone. Continue?" + "and cannot be undone. Continue?"--]] -haul_delete = defconf('haul-delete') -function haul_delete.intercept_key(key) - if ui.main.mode == df.ui_sidebar_mode.Hauling and - #ui.hauling.view_routes > 0 and - not ui.hauling.in_name and - not ui.hauling.in_stop and - not ui.hauling.in_assign_vehicle then - return key == keys.D_HAULING_REMOVE - end - return false +haul_delete_route = defconf('haul-delete-route') +function haul_delete_route.intercept_key(key) + return df.global.game.main_interface.current_hover == 180 and key == MOUSE_LEFT end -haul_delete.title = "Confirm deletion" -function haul_delete.get_message() - local t = ui.hauling.view_stops[ui.hauling.cursor_top] and "stop" or "route" - return "Are you sure you want to delete this " .. - (ui.hauling.view_stops[ui.hauling.cursor_top] and "stop" or "route") .. "?" +haul_delete_route.title = "Confirm deletion" +haul_delete_route.message = "Are you sure you want to delete this route?" + +haul_delete_stop = defconf('haul-delete-stop') +function haul_delete_stop.intercept_key(key) + return df.global.game.main_interface.current_hover == 185 and key == MOUSE_LEFT end +haul_delete_stop.title = "Confirm deletion" +haul_delete_stop.message = "Are you sure you want to delete this stop?" depot_remove = defconf('depot-remove') function depot_remove.intercept_key(key) - if df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) and - key == keys.DESTROYBUILDING then - for _, caravan in pairs(ui.caravans) do + if df.global.game.main_interface.current_hover == 299 and + key == MOUSE_LEFT and + df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) then + for _, caravan in pairs(df.global.plotinfo.caravans) do if caravan.time_remaining > 0 then return true end @@ -156,15 +155,12 @@ depot_remove.message = "Are you sure you want to remove this depot?\n" .. squad_disband = defconf('squad-disband') function squad_disband.intercept_key(key) - return key == keys.D_MILITARY_DISBAND_SQUAD and - screen.page == screen._type.T_page.Positions and - screen.num_squads > 0 and - not screen.in_rename_alert + return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 341 end squad_disband.title = "Disband squad" squad_disband.message = "Are you sure you want to disband this squad?" -uniform_delete = defconf('uniform-delete') +--[[uniform_delete = defconf('uniform-delete') function uniform_delete.intercept_key(key) return key == keys.D_MILITARY_DELETE_UNIFORM and screen.page == screen._type.T_page.Uniforms and @@ -226,7 +222,7 @@ function order_remove.intercept_key(key) end order_remove.title = "Remove manager order" order_remove.message = "Are you sure you want to remove this order?" - +]]-- -- End of confirmation definitions function check() diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 6731aa513..dc8f52eed 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -864,7 +864,7 @@ public: } } } - std::string getFocusString() { return "unitlabors/batch"; } + std::vector getFocusStrings() { return std::vector{"unitlabors/batch"}; } void select_page (page p) { if (p == NICKNAME || p == PROFNAME) @@ -1034,7 +1034,7 @@ public: } } } - std::string getFocusString() { return "unitlabors/profession"; } + std::vector getFocusStrings() { return std::vector{"unitlabors/profession"}; } void feed(set *events) { if (events->count(interface_key::LEAVESCREEN)) @@ -1146,7 +1146,7 @@ public: void help() { } - std::string getFocusString() { return "unitlabors"; } + std::vector getFocusStrings() { return std::vector{"unitlabors"}; } df::unit *getSelectedUnit(); diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index 8cc27067c..a901446db 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -620,7 +620,7 @@ public: for (auto line = lines.begin(); line != lines.end(); ++line) OutputString(COLOR_WHITE, x, y, line->c_str(), true, left_margin); } - std::string getFocusString() { return "stocks_view/search_help"; } + std::vector getFocusStrings() { return std::vector{"stocks_view/search_help"}; } }; class ViewscreenStocks : public dfhack_viewscreen @@ -973,7 +973,7 @@ public: OutputHotkeyString(x, y, "Search help", interface_key::HELP, true, left_margin); } - std::string getFocusString() { return "stocks_view"; } + std::vector getFocusStrings() { return std::vector{"stocks_view"}; } df::item *getSelectedItem() override { diff --git a/plugins/uicommon.h b/plugins/uicommon.h index 6179324d4..07bb34f54 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -196,17 +196,6 @@ static inline char get_string_input(const std::set *input) * Utility Functions */ -static inline df::building_stockpilest *get_selected_stockpile() -{ - if (!Gui::dwarfmode_hotkey(Core::getTopViewscreen()) || - df::global::plotinfo->main.mode != ui_sidebar_mode::QueryBuilding) - { - return nullptr; - } - - return virtual_cast(df::global::world->selected_building); -} - static inline bool can_trade() { if (df::global::plotinfo->caravans.size() == 0) diff --git a/test/library/gui/dwarfmode.lua b/test/library/gui/dwarfmode.lua index 960397e02..52748f095 100644 --- a/test/library/gui/dwarfmode.lua +++ b/test/library/gui/dwarfmode.lua @@ -26,7 +26,7 @@ function test.enterSidebarMode() -- Simulate not being able to get to default from a screen via mocks. This -- failure can actually happen in-game in some situations, such as when -- naming a building with ctrl-N (no way to cancel changes). - mock.patch({{dfhack.gui, 'getFocusString', mock.func()}, + mock.patch({{dfhack.gui, 'getFocusStrings', mock.func()}, {gui, 'simulateInput', mock.func()}}, function() expect.error_match('Unable to get into target sidebar mode',