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*,
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])``

@ -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<std::string> 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",

@ -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<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)
{
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 }
};

@ -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<std::string> 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<T>(getViewscreenByIdentity(T::_identity, n));
}
inline std::string getCurFocus(bool skip_dismissed = false) {
return getFocusString(getCurViewscreen(skip_dismissed));
inline std::vector<std::string> getCurFocus(bool skip_dismissed = false) {
return getFocusStrings(getCurViewscreen(skip_dismissed));
}
/// get the size of the window buffer

@ -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();

@ -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

@ -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<virtual_identity*, getFocusStringHandler> getFocusStringHandlers;
typedef void (*getFocusStringsHandler)(std::string &str, std::vector<std::string> &strList, df::viewscreen *screen);
static std::map<virtual_identity*, getFocusStringsHandler> 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<std::string> &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<std::string> &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<std::string> 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<std::string> Gui::getFocusStrings(df::viewscreen* top)
{
focus += "/" + enum_item_key(screen->mode);
if (screen->item_count_edit)
focus += "/EditCount";
}
*/
std::vector<std::string> focusStrings;
std::string Gui::getFocusString(df::viewscreen *top)
{
if (!top)
return "";
return focusStrings;
if (dfhack_viewscreen::is_instance(top))
{
auto name = static_cast<dfhack_viewscreen*>(top)->getFocusString();
return name.empty() ? "dfhack" : "dfhack/"+name;
auto name = static_cast<dfhack_viewscreen*>(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;

@ -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 },

@ -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)

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

@ -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<df::interface_key> ikey_set;
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
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<char> &selected)
static bool goods_selected (std::vector<uint8_t> &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<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)
{
@ -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<std::string> 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<typename T>
@ -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 <PluginCommand> &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;

@ -410,7 +410,7 @@ public:
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:
ListColumn<df::unit *> dwarves_column;
@ -1021,7 +1021,7 @@ public:
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:
ListColumn<activity_type> 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<std::string> getFocusStrings() override { return std::vector<std::string>{"dwarfmonitor_preferences"}; }
private:
ListColumn<size_t> preferences_column;

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

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

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

@ -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]];

@ -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()

@ -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)
{
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)
{
if (events->count(interface_key::LEAVESCREEN))
@ -1146,7 +1146,7 @@ public:
void help() { }
std::string getFocusString() { return "unitlabors"; }
std::vector<std::string> getFocusStrings() { return std::vector<std::string>{"unitlabors"}; }
df::unit *getSelectedUnit();

@ -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<std::string> getFocusStrings() { return std::vector<std::string>{"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<std::string> getFocusStrings() { return std::vector<std::string>{"stocks_view"}; }
df::item *getSelectedItem() override
{

@ -196,17 +196,6 @@ static inline char get_string_input(const std::set<df::interface_key> *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::building_stockpilest>(df::global::world->selected_building);
}
static inline bool can_trade()
{
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
-- 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',