develop
Robob27 2023-01-27 13:22:52 -05:00
parent 3ca2521ce9
commit a770a4cae4
21 changed files with 467 additions and 574 deletions

@ -950,10 +950,18 @@ Screens
Returns the topmost viewscreen. If ``skip_dismissed`` is *true*, Returns the topmost viewscreen. If ``skip_dismissed`` is *true*,
ignores screens already marked to be removed. ignores screens already marked to be removed.
* ``dfhack.gui.getFocusString(viewscreen)`` * ``dfhack.gui.getFocusStrings(viewscreen)``
Returns a string representation of the current focus position Returns a table of string representations of the current UI focuses.
in the ui. The string has a "screen/foo/bar/baz..." format. 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])`` * ``dfhack.gui.getCurFocus([skip_dismissed])``

@ -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 << "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 << "Context may be used to limit the scope of the binding, by" << endl
<< "requiring the current context to have a certain prefix." << endl << "requiring the current context to have a certain prefix." << endl
<< "Current UI context is: " << "Current UI context is: " << endl
<< Gui::getFocusString(Core::getTopViewscreen()) << endl; << join_strings("\n", Gui::getFocusStrings(Gui::getDFViewscreen())) << endl;
} }
} }
else if (first == "alias") else if (first == "alias")
@ -2419,11 +2419,22 @@ bool Core::SelectHotkey(int sym, int modifiers)
binding.modifiers, modifiers); binding.modifiers, modifiers);
continue; continue;
} }
string focusString = Gui::getFocusString(screen); if (!binding.focus.empty()) {
if (!binding.focus.empty() && !prefix_matches(binding.focus, focusString)) { // TODO: understand more about this to figure out if this solution works
DEBUG(keybinding).print("skipping keybinding due to focus string mismatch: '%s' !~ '%s'\n", bool found = false;
focusString.c_str(), binding.focus.c_str()); std::vector<std::string> focusStrings = Gui::getFocusStrings(Core::getTopViewscreen());
continue; // 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)) { if (!plug_mgr->CanInvokeHotkey(binding.command[0], screen)) {
DEBUG(keybinding).print("skipping keybinding due to hotkey guard rejection (command: '%s')\n", DEBUG(keybinding).print("skipping keybinding due to hotkey guard rejection (command: '%s')\n",

@ -1471,13 +1471,13 @@ static int gui_getMousePos(lua_State *L)
static const LuaWrapper::FunctionReg dfhack_gui_module[] = { static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
WRAPM(Gui, getCurViewscreen), WRAPM(Gui, getCurViewscreen),
WRAPM(Gui, getDFViewscreen), WRAPM(Gui, getDFViewscreen),
WRAPM(Gui, getFocusString),
WRAPM(Gui, getCurFocus), WRAPM(Gui, getCurFocus),
WRAPM(Gui, getSelectedWorkshopJob), WRAPM(Gui, getSelectedWorkshopJob),
WRAPM(Gui, getSelectedJob), WRAPM(Gui, getSelectedJob),
WRAPM(Gui, getSelectedUnit), WRAPM(Gui, getSelectedUnit),
WRAPM(Gui, getSelectedItem), WRAPM(Gui, getSelectedItem),
WRAPM(Gui, getSelectedBuilding), WRAPM(Gui, getSelectedBuilding),
WRAPM(Gui, getSelectedStockpile),
WRAPM(Gui, getSelectedPlant), WRAPM(Gui, getSelectedPlant),
WRAPM(Gui, getAnyUnit), WRAPM(Gui, getAnyUnit),
WRAPM(Gui, getAnyItem), WRAPM(Gui, getAnyItem),
@ -1495,9 +1495,17 @@ static const LuaWrapper::FunctionReg dfhack_gui_module[] = {
WRAPM(Gui, refreshSidebar), WRAPM(Gui, refreshSidebar),
WRAPM(Gui, inRenameBuilding), WRAPM(Gui, inRenameBuilding),
WRAPM(Gui, getDepthAt), WRAPM(Gui, getDepthAt),
WRAPM(Gui, matchFocusString),
{ NULL, NULL } { NULL, NULL }
}; };
static int gui_getFocusStrings(lua_State *state) {
df::viewscreen *r = Lua::GetDFObject<df::viewscreen>(state, 1);
std::vector<std::string> focusStrings = Gui::getFocusStrings(r);
Lua::PushVector(state, focusStrings);
return 1;
}
static int gui_autoDFAnnouncement(lua_State *state) static int gui_autoDFAnnouncement(lua_State *state)
{ {
bool rv; bool rv;
@ -1623,6 +1631,7 @@ static const luaL_Reg dfhack_gui_funcs[] = {
{ "pauseRecenter", gui_pauseRecenter }, { "pauseRecenter", gui_pauseRecenter },
{ "revealInDwarfmodeMap", gui_revealInDwarfmodeMap }, { "revealInDwarfmodeMap", gui_revealInDwarfmodeMap },
{ "getMousePos", gui_getMousePos }, { "getMousePos", gui_getMousePos },
{ "getFocusStrings", gui_getFocusStrings },
{ NULL, NULL } { NULL, NULL }
}; };

@ -36,6 +36,7 @@ distribution.
#include "df/plotinfost.h" #include "df/plotinfost.h"
#include "df/announcement_type.h" #include "df/announcement_type.h"
#include "df/announcement_flags.h" #include "df/announcement_flags.h"
#include "df/building_stockpilest.h"
#include "df/report_init.h" #include "df/report_init.h"
#include "df/report_zoom_type.h" #include "df/report_zoom_type.h"
#include "df/unit_report_type.h" #include "df/unit_report_type.h"
@ -65,7 +66,9 @@ namespace DFHack
*/ */
namespace Gui namespace Gui
{ {
DFHACK_EXPORT std::string getFocusString(df::viewscreen *top); DFHACK_EXPORT std::vector<std::string> getFocusStrings(df::viewscreen *top);
DFHACK_EXPORT bool matchFocusString(std::string focusString);
// Full-screen item details view // Full-screen item details view
DFHACK_EXPORT bool item_details_hotkey(df::viewscreen *top); 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 *getAnyBuilding(df::viewscreen *top);
DFHACK_EXPORT df::building *getSelectedBuilding(color_ostream &out, bool quiet = false); 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' // A plant is selected, e.g. via 'k'
DFHACK_EXPORT bool any_plant_hotkey(df::viewscreen *top); DFHACK_EXPORT bool any_plant_hotkey(df::viewscreen *top);
DFHACK_EXPORT df::plant *getAnyPlant(df::viewscreen *top); DFHACK_EXPORT df::plant *getAnyPlant(df::viewscreen *top);
@ -191,8 +198,8 @@ namespace DFHack
return strict_virtual_cast<T>(getViewscreenByIdentity(T::_identity, n)); return strict_virtual_cast<T>(getViewscreenByIdentity(T::_identity, n));
} }
inline std::string getCurFocus(bool skip_dismissed = false) { inline std::vector<std::string> getCurFocus(bool skip_dismissed = false) {
return getFocusString(getCurViewscreen(skip_dismissed)); return getFocusStrings(getCurViewscreen(skip_dismissed));
} }
/// get the size of the window buffer /// get the size of the window buffer

@ -351,7 +351,7 @@ namespace DFHack
virtual bool is_lua_screen() { return false; } virtual bool is_lua_screen() { return false; }
virtual std::string getFocusString() = 0; virtual std::string getFocusStrings() = 0;
virtual void onShow() {}; virtual void onShow() {};
virtual void onDismiss() {}; virtual void onDismiss() {};
virtual df::unit *getSelectedUnit() { return nullptr; } virtual df::unit *getSelectedUnit() { return nullptr; }
@ -384,7 +384,7 @@ namespace DFHack
static df::viewscreen *get_pointer(lua_State *L, int idx, bool make); static df::viewscreen *get_pointer(lua_State *L, int idx, bool make);
virtual bool is_lua_screen() { return true; } virtual bool is_lua_screen() { return true; }
virtual std::string getFocusString() { return focus; } virtual std::string getFocusStrings() { return focus; }
virtual void render(); virtual void render();
virtual void logic(); virtual void logic();

@ -48,7 +48,7 @@ function enterSidebarMode(sidebar_mode, max_esc)
local focus_string = '' local focus_string = ''
while remaining_esc > 0 do while remaining_esc > 0 do
local screen = dfhack.gui.getCurViewscreen(true) 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 if df.global.plotinfo.main.mode == df.ui_sidebar_mode.Default and
focus_string == 'dwarfmode/Default' then focus_string == 'dwarfmode/Default' then
if #navkey > 0 then gui.simulateInput(screen, navkey) end if #navkey > 0 then gui.simulateInput(screen, navkey) end

@ -130,479 +130,261 @@ static std::string getNameChunk(virtual_identity *id, int start, int end)
* Classifying focus context by means of a string path. * Classifying focus context by means of a string path.
*/ */
typedef void (*getFocusStringHandler)(std::string &str, df::viewscreen *screen); typedef void (*getFocusStringsHandler)(std::string &str, std::vector<std::string> &strList, df::viewscreen *screen);
static std::map<virtual_identity*, getFocusStringHandler> getFocusStringHandlers; static std::map<virtual_identity*, getFocusStringsHandler> getFocusStringsHandlers;
#define VIEWSCREEN(name) df::viewscreen_##name##st #define VIEWSCREEN(name) df::viewscreen_##name##st
#define DEFINE_GET_FOCUS_STRING_HANDLER(screen_type) \ #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<std::string> &focusStrings, VIEWSCREEN(screen_type) *screen);\
DFHACK_STATIC_ADD_TO_MAP(\ DFHACK_STATIC_ADD_TO_MAP(\
&getFocusStringHandlers, &VIEWSCREEN(screen_type)::_identity, \ &getFocusStringsHandlers, &VIEWSCREEN(screen_type)::_identity, \
(getFocusStringHandler)getFocusString_##screen_type \ (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<std::string> &focusStrings, VIEWSCREEN(screen_type) *screen)
DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode) DEFINE_GET_FOCUS_STRING_HANDLER(dwarfmode)
{ {
/* TODO: understand how this changes for v50 std::string newFocusString;
using namespace df::enums::ui_sidebar_mode;
if (game->main_interface.info.open) {
using df::global::ui_workshop_in_add; newFocusString = baseFocus;
using df::global::ui_build_selector; newFocusString += "/Info";
using df::global::ui_selected_unit; newFocusString += "/" + enum_item_key(game->main_interface.info.current_mode);
using df::global::ui_look_list; focusStrings.push_back(newFocusString);
using df::global::ui_look_cursor; }
using df::global::ui_building_item_cursor; if (game->main_interface.view_sheets.open) {
using df::global::ui_building_assign_type; newFocusString = baseFocus;
using df::global::ui_building_assign_is_marked; newFocusString += "/ViewSheets";
using df::global::ui_building_assign_units; newFocusString += "/" + enum_item_key(game->main_interface.view_sheets.active_sheet);
using df::global::ui_building_assign_items; focusStrings.push_back(newFocusString);
using df::global::ui_building_in_assign; }
if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::STOCKPILE) {
focus += "/" + enum_item_key(plotinfo->main.mode); newFocusString = baseFocus;
// TODO: learn more about where /Some was used previously to ensure proper/consistent usage
switch (plotinfo->main.mode) if (game->main_interface.stockpile.cur_bld) {
{ newFocusString += "/Some";
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";
}
} }
else newFocusString += "/Stockpile";
focus += "/None"; focusStrings.push_back(newFocusString);
break; }
if (game->main_interface.bottom_mode_selected == df::enums::main_bottom_mode_type::HAULING) {
case Build: newFocusString = baseFocus;
if (ui_build_selector) newFocusString += "/Hauling";
{ focusStrings.push_back(newFocusString);
// Not selecting, or no choices? }
if (ui_build_selector->building_type < 0) if (game->main_interface.trade.open) {
focus += "/Type"; newFocusString = baseFocus;
else if (ui_build_selector->stage != 2) newFocusString += "/Trade";
{ focusStrings.push_back(newFocusString);
if (ui_build_selector->stage != 1) }
focus += "/NoMaterials"; if (game->main_interface.job_details.open) {
else newFocusString = baseFocus;
focus += "/Position"; newFocusString += "/JobDetails";
focusStrings.push_back(newFocusString);
focus += "/" + enum_item_key(ui_build_selector->building_type); }
} if (game->main_interface.assign_trade.open) {
else newFocusString = baseFocus;
{ newFocusString += "/AssignTrade";
focus += "/Material"; focusStrings.push_back(newFocusString);
if (ui_build_selector->is_grouped) }
focus += "/Groups"; if (game->main_interface.diplomacy.open) {
else newFocusString = baseFocus;
focus += "/Items"; newFocusString += "/Diplomacy";
} focusStrings.push_back(newFocusString);
} }
break; if (game->main_interface.petitions.open) {
newFocusString = baseFocus;
case ViewUnits: newFocusString += "/Petitions";
if (ui_selected_unit) focusStrings.push_back(newFocusString);
{ }
if (vector_get(world->units.active, *ui_selected_unit)) if (game->main_interface.stocks.open) {
{ newFocusString = baseFocus;
focus += "/Some"; newFocusString += "/Stocks";
focusStrings.push_back(newFocusString);
using df::global::ui_unit_view_mode; }
if (game->main_interface.assign_display_item.open) {
if (ui_unit_view_mode) newFocusString = baseFocus;
focus += "/" + enum_item_key(ui_unit_view_mode->value); newFocusString += "/AssignDisplayItem";
} focusStrings.push_back(newFocusString);
else }
focus += "/None"; if (game->main_interface.name_creator.open) {
} newFocusString = baseFocus;
break; newFocusString += "/NameCreator";
focusStrings.push_back(newFocusString);
case LookAround: }
if (ui_look_list && ui_look_cursor) if (game->main_interface.image_creator.open) {
{ newFocusString = baseFocus;
auto item = vector_get(ui_look_list->items, *ui_look_cursor); newFocusString += "/ImageCreator";
if (item) focusStrings.push_back(newFocusString);
focus += "/" + enum_item_key(item->type); }
else if (game->main_interface.unit_selector.open) {
focus += "/None"; newFocusString = baseFocus;
} newFocusString += "/UnitSelector";
break; focusStrings.push_back(newFocusString);
}
case BuildingItems: if (game->main_interface.announcement_alert.open) {
if (VIRTUAL_CAST_VAR(selected, df::building_actual, world->selected_building)) newFocusString = baseFocus;
{ newFocusString += "/AnnouncementAlert";
if (selected->contained_items.empty()) focusStrings.push_back(newFocusString);
focus += "/Some/Empty"; }
else if (game->main_interface.custom_symbol.open) {
focus += "/Some/Item"; newFocusString = baseFocus;
} newFocusString += "/CustomSymbol";
else focusStrings.push_back(newFocusString);
focus += "/None"; }
break; if (game->main_interface.patrol_routes.open) {
newFocusString = baseFocus;
case ZonesPenInfo: newFocusString += "/PatrolRoutes";
if (ui_building_assign_type && ui_building_assign_units && focusStrings.push_back(newFocusString);
ui_building_assign_is_marked && ui_building_assign_items && }
ui_building_assign_type->size() == ui_building_assign_units->size()) if (game->main_interface.squad_equipment.open) {
{ newFocusString = baseFocus;
focus += "/Assign"; newFocusString += "/SquadEquipment";
if (ui_building_item_cursor) focusStrings.push_back(newFocusString);
{ }
if (vector_get(*ui_building_assign_units, *ui_building_item_cursor)) if (game->main_interface.squad_schedule.open) {
focus += "/Unit"; newFocusString = baseFocus;
else if (vector_get(*ui_building_assign_items, *ui_building_item_cursor)) newFocusString += "/SquadSchedule";
focus += "/Vermin"; focusStrings.push_back(newFocusString);
else }
focus += "/None"; if (game->main_interface.squad_selector.open) {
} newFocusString = baseFocus;
} newFocusString += "/SquadSelector";
break; focusStrings.push_back(newFocusString);
}
case Burrows: if (game->main_interface.burrow_selector.open) {
if (plotinfo->burrows.in_confirm_delete) newFocusString = baseFocus;
focus += "/ConfirmDelete"; newFocusString += "/BurrowSelector";
else if (plotinfo->burrows.in_add_units_mode) focusStrings.push_back(newFocusString);
focus += "/AddUnits"; }
else if (plotinfo->burrows.in_edit_name_mode) if (game->main_interface.location_selector.open) {
focus += "/EditName"; newFocusString = baseFocus;
else if (plotinfo->burrows.in_define_mode) newFocusString += "/LocationSelector";
focus += "/Define"; focusStrings.push_back(newFocusString);
else }
focus += "/List"; if (game->main_interface.location_details.open) {
break; newFocusString = baseFocus;
newFocusString += "/LocationDetails";
case Hauling: focusStrings.push_back(newFocusString);
if (plotinfo->hauling.in_assign_vehicle) }
{ if (game->main_interface.hauling_stop_conditions.open) {
auto vehicle = vector_get(plotinfo->hauling.vehicles, plotinfo->hauling.cursor_vehicle); newFocusString = baseFocus;
focus += "/AssignVehicle/" + std::string(vehicle ? "Some" : "None"); newFocusString += "/HaulingStopConditions";
} focusStrings.push_back(newFocusString);
else }
{ if (game->main_interface.assign_vehicle.open) {
int idx = plotinfo->hauling.cursor_top; newFocusString = baseFocus;
auto route = vector_get(plotinfo->hauling.view_routes, idx); newFocusString += "/AssignVehicle";
auto stop = vector_get(plotinfo->hauling.view_stops, idx); focusStrings.push_back(newFocusString);
std::string tag = stop ? "Stop" : (route ? "Route" : "None"); }
if (game->main_interface.stockpile_link.open) {
if (plotinfo->hauling.in_name) newFocusString = baseFocus;
focus += "/Rename/" + tag; newFocusString += "/StockpileLink";
else if (plotinfo->hauling.in_stop) focusStrings.push_back(newFocusString);
{ }
int sidx = plotinfo->hauling.cursor_stop; if (game->main_interface.stockpile_tools.open) {
auto cond = vector_get(plotinfo->hauling.stop_conditions, sidx); newFocusString = baseFocus;
auto link = vector_get(plotinfo->hauling.stop_links, sidx); newFocusString += "/StockpileTools";
focusStrings.push_back(newFocusString);
focus += "/DefineStop"; }
if (game->main_interface.custom_stockpile.open) {
if (cond) newFocusString = baseFocus;
focus += "/Cond/" + enum_item_key(cond->mode); newFocusString += "/CustomStockpile";
else if (link) focusStrings.push_back(newFocusString);
{ }
focus += "/Link/"; if (game->main_interface.create_squad.open) {
if (link->mode.bits.give) focus += "Give"; newFocusString = baseFocus;
if (link->mode.bits.take) focus += "Take"; newFocusString += "/CreateSquad";
} focusStrings.push_back(newFocusString);
else }
focus += "/None"; if (game->main_interface.squad_supplies.open) {
} newFocusString = baseFocus;
else newFocusString += "/SquadSupplies";
focus += "/Select/" + tag; focusStrings.push_back(newFocusString);
} }
break; if (game->main_interface.assign_uniform.open) {
newFocusString = baseFocus;
default: newFocusString += "/AssignUniform";
break; 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);
} }
*/ if (game->main_interface.squad_equipment.open) {
} newFocusString = baseFocus;
newFocusString += "/SquadEquipment";
/* TODO: understand how this changes for v50 focusStrings.push_back(newFocusString);
DEFINE_GET_FOCUS_STRING_HANDLER(dungeonmode) }
{ // squads should be last because it's the only one not exclusive with the others? or something?
using df::global::adventure; if (game->main_interface.squads.open) {
newFocusString = baseFocus;
if (!adventure) newFocusString += "/Squads";
return; focusStrings.push_back(newFocusString);
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;
} }
focus += "/On"; if (!newFocusString.size()) {
focusStrings.push_back(baseFocus);
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");
} }
} }
DEFINE_GET_FOCUS_STRING_HANDLER(locations) bool Gui::matchFocusString(std::string focusString) {
{ focusString = toLower(focusString);
focus += "/" + enum_item_key(screen->menu); std::vector<std::string> currentFocus = getFocusStrings(Core::getTopViewscreen());
}
DEFINE_GET_FOCUS_STRING_HANDLER(jobmanagement) return std::find_if(currentFocus.begin(), currentFocus.end(), [&focusString](std::string item) {
{ return focusString == toLower(item);
focus += (screen->in_max_workshops ? "/MaxWorkshops" : "/Main"); }) != currentFocus.end();
} }
DEFINE_GET_FOCUS_STRING_HANDLER(workquota_condition) std::vector<std::string> Gui::getFocusStrings(df::viewscreen* top)
{ {
focus += "/" + enum_item_key(screen->mode); std::vector<std::string> focusStrings;
if (screen->item_count_edit)
focus += "/EditCount";
}
*/
std::string Gui::getFocusString(df::viewscreen *top)
{
if (!top) if (!top)
return ""; return focusStrings;
if (dfhack_viewscreen::is_instance(top)) if (dfhack_viewscreen::is_instance(top))
{ {
auto name = static_cast<dfhack_viewscreen*>(top)->getFocusString(); auto name = static_cast<dfhack_viewscreen*>(top)->getFocusStrings();
return name.empty() ? "dfhack" : "dfhack/"+name; focusStrings.push_back(name.empty() ? "dfhack" : "dfhack/" + name);
} }
else if (virtual_identity *id = virtual_identity::get(top)) else if (virtual_identity *id = virtual_identity::get(top))
{ {
std::string name = getNameChunk(id, 11, 2); std::string name = getNameChunk(id, 11, 2);
auto handler = map_find(getFocusStringHandlers, id); auto handler = map_find(getFocusStringsHandlers, id);
if (handler) if (handler)
handler(name, top); handler(name, focusStrings, top);
return name;
} }
else else
{ {
Core &core = Core::getInstance(); Core &core = Core::getInstance();
std::string name = core.p->readClassName(*(void**)top); 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 // Predefined common guard functions
@ -1269,6 +1051,28 @@ df::item *Gui::getSelectedItem(color_ostream &out, bool quiet)
return item; 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) df::building *Gui::getAnyBuilding(df::viewscreen *top)
{ {
using df::global::game; using df::global::game;

@ -1081,7 +1081,7 @@ using df::identity_traits;
#define CUR_STRUCT dfhack_viewscreen #define CUR_STRUCT dfhack_viewscreen
static const struct_field_info dfhack_viewscreen_fields[] = { static const struct_field_info dfhack_viewscreen_fields[] = {
{ METHOD(OBJ_METHOD, is_lua_screen), 0, 0 }, { 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, onShow), 0, 0 },
{ METHOD(OBJ_METHOD, onDismiss), 0, 0 }, { METHOD(OBJ_METHOD, onDismiss), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedUnit), 0, 0 }, { METHOD(OBJ_METHOD, getSelectedUnit), 0, 0 },

@ -97,7 +97,7 @@ add_subdirectory(channel-safely)
dfhack_plugin(cleanconst cleanconst.cpp) dfhack_plugin(cleanconst cleanconst.cpp)
dfhack_plugin(cleaners cleaners.cpp) dfhack_plugin(cleaners cleaners.cpp)
dfhack_plugin(cleanowned cleanowned.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(createitem createitem.cpp)
dfhack_plugin(cursecheck cursecheck.cpp) dfhack_plugin(cursecheck cursecheck.cpp)
dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua) dfhack_plugin(cxxrandom cxxrandom.cpp LINK_LIBRARIES lua)

@ -56,7 +56,7 @@ public:
void render(); void render();
std::string getFocusString() { return "buildingplan_choosemat"; } std::string getFocusStrings() { return "buildingplan_choosemat"; }
private: private:
ListColumn<df::dfhack_material_category> masks_column; ListColumn<df::dfhack_material_category> masks_column;

@ -16,15 +16,11 @@
#include "uicommon.h" #include "uicommon.h"
#include "df/building_tradedepotst.h" #include "df/building_tradedepotst.h"
#include "df/gamest.h"
#include "df/general_ref.h" #include "df/general_ref.h"
#include "df/general_ref_contained_in_itemst.h" #include "df/general_ref_contained_in_itemst.h"
#include "df/interfacest.h" #include "df/interfacest.h"
#include "df/viewscreen_dwarfmodest.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 DFHack;
using namespace df::enums; using namespace df::enums;
@ -32,11 +28,12 @@ using std::map;
using std::queue; using std::queue;
using std::string; using std::string;
using std::vector; using std::vector;
using df::global::game;
DFHACK_PLUGIN("confirm"); DFHACK_PLUGIN("confirm");
DFHACK_PLUGIN_IS_ENABLED(is_enabled); DFHACK_PLUGIN_IS_ENABLED(is_enabled);
REQUIRE_GLOBAL(game)
REQUIRE_GLOBAL(gps); REQUIRE_GLOBAL(gps);
REQUIRE_GLOBAL(plotinfo);
typedef std::set<df::interface_key> ikey_set; typedef std::set<df::interface_key> ikey_set;
command_result df_confirm (color_ostream &out, vector <string> & parameters); command_result df_confirm (color_ostream &out, vector <string> & parameters);
@ -51,6 +48,8 @@ bool paused = false;
// if set, confirm will unpause when this screen is no longer on the stack // if set, confirm will unpause when this screen is no longer on the stack
df::viewscreen *paused_screen = NULL; df::viewscreen *paused_screen = NULL;
std::string paused_focus = "";
namespace DFHack { namespace DFHack {
DBG_DECLARE(confirm,status); DBG_DECLARE(confirm,status);
} }
@ -121,25 +120,25 @@ public:
}; };
namespace trade { namespace trade {
static bool goods_selected (const std::vector<char> &selected) static bool goods_selected (std::vector<uint8_t> &selected)
{ {
for (char c : selected) for (uint8_t sel : selected)
if (c) if (sel == 1)
return true; return true;
return false; return false;
} }
inline bool trader_goods_selected (df::viewscreen_tradegoodsst *screen) inline bool trader_goods_selected (df::viewscreen_dwarfmodest *screen)
{ {
CHECK_NULL_POINTER(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); 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<char> &selected, const std::vector<df::item*> &items) \ /*static bool goods_all_selected(const std::vector<char>& selected, const std::vector<df::item*>& items) \
{ {
for (size_t i = 0; i < selected.size(); ++i) for (size_t i = 0; i < selected.size(); ++i)
{ {
@ -162,16 +161,16 @@ namespace trade {
} }
return true; 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); 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); 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 { namespace conf_lua {
@ -245,6 +244,7 @@ namespace conf_lua {
DEBUG(status).print("unpausing\n"); DEBUG(status).print("unpausing\n");
paused = false; paused = false;
paused_screen = NULL; paused_screen = NULL;
paused_focus = "";
return 0; return 0;
} }
int get_paused (lua_State *L) int get_paused (lua_State *L)
@ -259,9 +259,9 @@ namespace conf_lua {
DFHACK_PLUGIN_LUA_FUNCTIONS { DFHACK_PLUGIN_LUA_FUNCTIONS {
CONF_LUA_FUNC( , set_conf_state), CONF_LUA_FUNC( , set_conf_state),
CONF_LUA_FUNC(trade, broker_goods_selected), 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_selected),
CONF_LUA_FUNC(trade, trader_goods_all_selected), //CONF_LUA_FUNC(trade, trader_goods_all_selected),
DFHACK_LUA_END DFHACK_LUA_END
}; };
@ -306,16 +306,56 @@ public:
return true; return true;
} }
bool feed (ikey_set *input) { 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) if (paused)
{ {
// we can only detect that we've left the screen by intercepting the // TODO: fix
// ESC key if (paused_focus != "" && (input->count(df::interface_key::LEAVESCREEN) || mouseExit))
if (!paused_screen && input->count(df::interface_key::LEAVESCREEN)) if(mouseExit) {
df::global::enabler->mouse_rbut = 0;
df::global::enabler->mouse_rbut_down = 0;
}
conf_lua::api::unpause(NULL); conf_lua::api::unpause(NULL);
return false; return false;
} }
else if (state == INACTIVE) 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) for (df::interface_key key : *input)
{ {
if (intercept_key(key)) if (intercept_key(key))
@ -331,20 +371,31 @@ public:
} }
else if (state == ACTIVE) 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); set_state(INACTIVE);
else if (input->count(df::interface_key::SELECT)) } else if (input->count(df::interface_key::SELECT))
set_state(SELECTED); set_state(SELECTED);
else if (input->count(df::interface_key::CUSTOM_P)) else if (input->count(df::interface_key::CUSTOM_P))
{ {
// TODO: fix
DEBUG(status).print("pausing\n"); DEBUG(status).print("pausing\n");
paused = true; paused = true;
// only record the screen when we're not at the top viewscreen // only record the screen when we're not at the top viewscreen
// since this screen will *always* be on the stack. for // since this screen will *always* be on the stack. for
// dwarfmode screens, use ESC detection to discover when to // dwarfmode screens, use ESC detection to discover when to
// unpause // unpause
if (!df::viewscreen_dwarfmodest::_identity.is_instance(screen))
paused_screen = screen; std::vector<std::string> focusStrings = Gui::getFocusStrings(Gui::getCurViewscreen());
std::string current_focus = focusStrings[0];
if (current_focus != "dwarfmode") {
paused_focus = current_focus;
}
set_state(INACTIVE); set_state(INACTIVE);
} }
else if (input->count(df::interface_key::CUSTOM_S)) else if (input->count(df::interface_key::CUSTOM_S))
@ -429,8 +480,27 @@ public:
else if (state == SELECTED) else if (state == SELECTED)
{ {
ikey_set tmp; ikey_set tmp;
tmp.insert(last_key); if(last_key_is_left_click) {
screen->feed(&tmp); 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); set_state(INACTIVE);
} }
} }
@ -445,6 +515,15 @@ public:
else else
return false; 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() string get_title()
{ {
CONF_LUA_START; CONF_LUA_START;
@ -473,6 +552,9 @@ public:
protected: protected:
cstate state; cstate state;
df::interface_key last_key; df::interface_key last_key;
bool last_key_is_left_click;
bool last_key_is_right_click;
df::coord2d mouse_pos;
}; };
template<typename T> template<typename T>
@ -501,18 +583,12 @@ struct cls##_hooks : cls::screen_type { \
INTERPOSE_NEXT(render)(); \ INTERPOSE_NEXT(render)(); \
cls##_instance.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, feed, prio); \
IMPLEMENT_VMETHOD_INTERPOSE_PRIO(cls##_hooks, render, 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, {\ static int conf_register_##cls = conf_register(&cls##_instance, {\
&INTERPOSE_HOOK(cls##_hooks, feed), \ &INTERPOSE_HOOK(cls##_hooks, feed), \
&INTERPOSE_HOOK(cls##_hooks, render), \ &INTERPOSE_HOOK(cls##_hooks, render), \
&INTERPOSE_HOOK(cls##_hooks, key_conflict), \
}); });
#define DEFINE_CONFIRMATION(cls, screen) \ #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) 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 are obtained by replacing '_' with '-' in the first argument to DEFINE_CONFIRMATION
*/ */
DEFINE_CONFIRMATION(trade, viewscreen_tradegoodsst); DEFINE_CONFIRMATION(trade, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(trade_cancel, viewscreen_tradegoodsst); DEFINE_CONFIRMATION(trade_cancel, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(trade_seize, viewscreen_tradegoodsst); //DEFINE_CONFIRMATION(trade_seize, viewscreen_tradegoodsst);
DEFINE_CONFIRMATION(trade_offer, viewscreen_tradegoodsst); //DEFINE_CONFIRMATION(trade_offer, viewscreen_tradegoodsst);
DEFINE_CONFIRMATION(trade_select_all, viewscreen_tradegoodsst); //DEFINE_CONFIRMATION(trade_select_all, viewscreen_tradegoodsst);
DEFINE_CONFIRMATION(haul_delete, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(haul_delete_route, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest); DEFINE_CONFIRMATION(haul_delete_stop, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(squad_disband, viewscreen_layer_militaryst); DEFINE_CONFIRMATION(depot_remove, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(uniform_delete, viewscreen_layer_militaryst); DEFINE_CONFIRMATION(squad_disband, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(note_delete, viewscreen_dwarfmodest); //DEFINE_CONFIRMATION(uniform_delete, viewscreen_layer_militaryst);
DEFINE_CONFIRMATION(route_delete, viewscreen_dwarfmodest); //DEFINE_CONFIRMATION(note_delete, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(location_retire, viewscreen_locationsst); //DEFINE_CONFIRMATION(route_delete, viewscreen_dwarfmodest);
DEFINE_CONFIRMATION(convict, viewscreen_justicest); //DEFINE_CONFIRMATION(location_retire, viewscreen_locationsst);
DEFINE_CONFIRMATION(order_remove, viewscreen_jobmanagementst); //DEFINE_CONFIRMATION(convict, viewscreen_justicest);
//DEFINE_CONFIRMATION(order_remove, viewscreen_jobmanagementst);
DFhackCExport command_result plugin_init (color_ostream &out, vector <PluginCommand> &commands) DFhackCExport command_result plugin_init (color_ostream &out, vector <PluginCommand> &commands)
{ {
@ -587,18 +664,10 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out)
static bool screen_found(df::viewscreen *target_screen) static bool screen_found(df::viewscreen *target_screen)
{ {
if (!df::global::gview) if (!&game->main_interface)
return false; return false;
df::viewscreen *screen = &df::global::gview->view; return target_screen == Gui::getCurViewscreen();
while (screen)
{
if (screen == target_screen)
return true;
screen = screen->child;
}
return false;
} }
DFhackCExport command_result plugin_onupdate (color_ostream &out) 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 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); conf_lua::api::unpause(NULL);
return CR_OK; return CR_OK;

@ -410,7 +410,7 @@ public:
OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z);
} }
std::string getFocusString() { return "dwarfmonitor_dwarfstats"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"dwarfmonitor_dwarfstats"}; }
private: private:
ListColumn<df::unit *> dwarves_column; ListColumn<df::unit *> dwarves_column;
@ -1021,7 +1021,7 @@ public:
OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z); OutputHotkeyString(x, y, "Zoom Unit", CUSTOM_SHIFT_Z);
} }
std::string getFocusString() { return "dwarfmonitor_fortstats"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"dwarfmonitor_fortstats"}; }
private: private:
ListColumn<activity_type> fort_activity_column, category_breakdown_column; ListColumn<activity_type> fort_activity_column, category_breakdown_column;
@ -1652,7 +1652,7 @@ public:
getSelectedUnit() ? COLOR_WHITE : COLOR_DARKGREY); getSelectedUnit() ? COLOR_WHITE : COLOR_DARKGREY);
} }
std::string getFocusString() override { return "dwarfmonitor_preferences"; } std::vector<std::string> getFocusStrings() override { return std::vector<std::string>{"dwarfmonitor_preferences"}; }
private: private:
ListColumn<size_t> preferences_column; ListColumn<size_t> preferences_column;

@ -1613,7 +1613,7 @@ namespace embark_assist {
void render(); void render();
std::string getFocusString() { return "Finder UI"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"Finder UI"}; }
private: private:
}; };

@ -31,7 +31,7 @@ namespace embark_assist{
void render(); void render();
std::string getFocusString() { return "Help UI"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"Help UI"}; }
private: private:
pages current_page = pages::Intro; pages current_page = pages::Intro;

@ -572,7 +572,7 @@ public:
embark_tools_settings () { }; embark_tools_settings () { };
~embark_tools_settings () { }; ~embark_tools_settings () { };
void help () { }; void help () { };
std::string getFocusString () { return "embark-tools/options"; }; std::vector<std::string> getFocusStrings () { return std::vector<std::string>{"embark-tools/options"}; };
void render () void render ()
{ {
parent->render(); parent->render();

@ -90,7 +90,7 @@ static void find_active_keybindings(df::viewscreen *screen, bool filtermenu) {
valid_keys.push_back("`"); valid_keys.push_back("`");
current_focus = Gui::getFocusString(screen); current_focus = Gui::getFocusStrings(screen)[0];
for (int shifted = 0; shifted < 2; shifted++) { for (int shifted = 0; shifted < 2; shifted++) {
for (int alt = 0; alt < 2; alt++) { for (int alt = 0; alt < 2; alt++) {
for (int ctrl = 0; ctrl < 2; ctrl++) { 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) { static bool invoke_command(color_ostream &out, const size_t index) {
auto screen = Core::getTopViewscreen(); auto screen = Core::getTopViewscreen();
if (sorted_keys.size() <= index || if (sorted_keys.size() <= index ||
Gui::getFocusString(screen) != MENU_SCREEN_FOCUS_STRING) !Gui::matchFocusString(MENU_SCREEN_FOCUS_STRING))
return false; return false;
auto cmd = current_bindings[sorted_keys[index]]; auto cmd = current_bindings[sorted_keys[index]];

@ -11,6 +11,9 @@ setmetatable(keys, {
end, end,
__newindex = function() error('Table is read-only') 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 --[[ The screen where a confirmation has been triggered
Note that this is *not* necessarily the topmost viewscreen, so do not use Note that this is *not* necessarily the topmost viewscreen, so do not use
gui.getCurViewscreen() or related functions. ]] gui.getCurViewscreen() or related functions. ]]
@ -57,8 +60,7 @@ is equivalent to:
trade = defconf('trade') trade = defconf('trade')
function trade.intercept_key(key) function trade.intercept_key(key)
return screen.in_edit_count == 0 and return false--dfhack.gui.matchFocusString("dwarfmode/Trade") and key == MOUSE_LEFT and hovering over trade button?
key == keys.TRADE_TRADE
end end
trade.title = "Confirm trade" trade.title = "Confirm trade"
function trade.get_message() function trade.get_message()
@ -81,14 +83,14 @@ end
trade_cancel = defconf('trade-cancel') trade_cancel = defconf('trade-cancel')
function trade_cancel.intercept_key(key) function trade_cancel.intercept_key(key)
return screen.in_edit_count == 0 and return dfhack.gui.matchFocusString("dwarfmode/Trade") and
key == keys.LEAVESCREEN and (key == keys.LEAVESCREEN or key == MOUSE_RIGHT) and
(trader_goods_selected(screen) or broker_goods_selected(screen)) (trader_goods_selected(screen) or broker_goods_selected(screen))
end end
trade_cancel.title = "Cancel trade" trade_cancel.title = "Cancel trade"
trade_cancel.message = "Are you sure you want leave this screen?\nSelected items will not be saved." 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) function trade_seize.intercept_key(key)
return screen.in_edit_count == 0 and return screen.in_edit_count == 0 and
trader_goods_selected(screen) and trader_goods_selected(screen) and
@ -119,31 +121,28 @@ function trade_select_all.intercept_key(key)
end end
trade_select_all.title = "Confirm selection" trade_select_all.title = "Confirm selection"
trade_select_all.message = "Selecting all goods will overwrite your current selection\n" .. 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') haul_delete_route = defconf('haul-delete-route')
function haul_delete.intercept_key(key) function haul_delete_route.intercept_key(key)
if ui.main.mode == df.ui_sidebar_mode.Hauling and return df.global.game.main_interface.current_hover == 180 and key == MOUSE_LEFT
#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
end end
haul_delete.title = "Confirm deletion" haul_delete_route.title = "Confirm deletion"
function haul_delete.get_message() haul_delete_route.message = "Are you sure you want to delete this route?"
local t = ui.hauling.view_stops[ui.hauling.cursor_top] and "stop" or "route"
return "Are you sure you want to delete this " .. haul_delete_stop = defconf('haul-delete-stop')
(ui.hauling.view_stops[ui.hauling.cursor_top] and "stop" or "route") .. "?" function haul_delete_stop.intercept_key(key)
return df.global.game.main_interface.current_hover == 185 and key == MOUSE_LEFT
end 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') depot_remove = defconf('depot-remove')
function depot_remove.intercept_key(key) function depot_remove.intercept_key(key)
if df.building_tradedepotst:is_instance(dfhack.gui.getSelectedBuilding(true)) and if df.global.game.main_interface.current_hover == 299 and
key == keys.DESTROYBUILDING then key == MOUSE_LEFT and
for _, caravan in pairs(ui.caravans) do 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 if caravan.time_remaining > 0 then
return true return true
end end
@ -156,15 +155,12 @@ depot_remove.message = "Are you sure you want to remove this depot?\n" ..
squad_disband = defconf('squad-disband') squad_disband = defconf('squad-disband')
function squad_disband.intercept_key(key) function squad_disband.intercept_key(key)
return key == keys.D_MILITARY_DISBAND_SQUAD and return key == MOUSE_LEFT and df.global.game.main_interface.current_hover == 341
screen.page == screen._type.T_page.Positions and
screen.num_squads > 0 and
not screen.in_rename_alert
end end
squad_disband.title = "Disband squad" squad_disband.title = "Disband squad"
squad_disband.message = "Are you sure you want to disband this 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) function uniform_delete.intercept_key(key)
return key == keys.D_MILITARY_DELETE_UNIFORM and return key == keys.D_MILITARY_DELETE_UNIFORM and
screen.page == screen._type.T_page.Uniforms and screen.page == screen._type.T_page.Uniforms and
@ -226,7 +222,7 @@ function order_remove.intercept_key(key)
end end
order_remove.title = "Remove manager order" order_remove.title = "Remove manager order"
order_remove.message = "Are you sure you want to remove this order?" order_remove.message = "Are you sure you want to remove this order?"
]]--
-- End of confirmation definitions -- End of confirmation definitions
function check() function check()

@ -864,7 +864,7 @@ public:
} }
} }
} }
std::string getFocusString() { return "unitlabors/batch"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"unitlabors/batch"}; }
void select_page (page p) void select_page (page p)
{ {
if (p == NICKNAME || p == PROFNAME) if (p == NICKNAME || p == PROFNAME)
@ -1034,7 +1034,7 @@ public:
} }
} }
} }
std::string getFocusString() { return "unitlabors/profession"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"unitlabors/profession"}; }
void feed(set<df::interface_key> *events) void feed(set<df::interface_key> *events)
{ {
if (events->count(interface_key::LEAVESCREEN)) if (events->count(interface_key::LEAVESCREEN))
@ -1146,7 +1146,7 @@ public:
void help() { } void help() { }
std::string getFocusString() { return "unitlabors"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"unitlabors"}; }
df::unit *getSelectedUnit(); df::unit *getSelectedUnit();

@ -620,7 +620,7 @@ public:
for (auto line = lines.begin(); line != lines.end(); ++line) for (auto line = lines.begin(); line != lines.end(); ++line)
OutputString(COLOR_WHITE, x, y, line->c_str(), true, left_margin); OutputString(COLOR_WHITE, x, y, line->c_str(), true, left_margin);
} }
std::string getFocusString() { return "stocks_view/search_help"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"stocks_view/search_help"}; }
}; };
class ViewscreenStocks : public dfhack_viewscreen class ViewscreenStocks : public dfhack_viewscreen
@ -973,7 +973,7 @@ public:
OutputHotkeyString(x, y, "Search help", interface_key::HELP, true, left_margin); OutputHotkeyString(x, y, "Search help", interface_key::HELP, true, left_margin);
} }
std::string getFocusString() { return "stocks_view"; } std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"stocks_view"}; }
df::item *getSelectedItem() override df::item *getSelectedItem() override
{ {

@ -196,17 +196,6 @@ static inline char get_string_input(const std::set<df::interface_key> *input)
* Utility Functions * 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::building_stockpilest>(df::global::world->selected_building);
}
static inline bool can_trade() static inline bool can_trade()
{ {
if (df::global::plotinfo->caravans.size() == 0) if (df::global::plotinfo->caravans.size() == 0)

@ -26,7 +26,7 @@ function test.enterSidebarMode()
-- Simulate not being able to get to default from a screen via mocks. This -- 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 -- failure can actually happen in-game in some situations, such as when
-- naming a building with ctrl-N (no way to cancel changes). -- 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()}}, {gui, 'simulateInput', mock.func()}},
function() function()
expect.error_match('Unable to get into target sidebar mode', expect.error_match('Unable to get into target sidebar mode',