Start working on gui for workflow.

develop
Alexander Gavrilov 2012-10-23 21:42:03 +04:00
parent cfbdf47f6e
commit 09f8e8e419
7 changed files with 401 additions and 29 deletions

@ -13,6 +13,7 @@ DFHack future
New GUI scripts:
- gui/guide-path: displays the cached path for minecart Guide orders.
- gui/workshop-job: displays inputs of a workshop job and allows tweaking them.
- gui/workflow: a front-end for the workflow plugin.
DFHack v0.34.11-r2

@ -78,6 +78,9 @@ keybinding add Alt-P@dwarfmode/Hauling/DefineStop/Cond/Guide gui/guide-path
# workshop job details
keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job
# workflow front-end
keybinding add Ctrl-W@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workflow
############################
# UI and game logic tweaks #
############################

@ -500,7 +500,9 @@ static void OpenPersistent(lua_State *state)
* Material info lookup *
************************/
static void push_matinfo(lua_State *state, MaterialInfo &info)
static int DFHACK_MATINFO_TOKEN = 0;
void Lua::Push(lua_State *state, MaterialInfo &info)
{
if (!info.isValid())
{
@ -509,7 +511,7 @@ static void push_matinfo(lua_State *state, MaterialInfo &info)
}
lua_newtable(state);
lua_pushvalue(state, lua_upvalueindex(1));
lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_MATINFO_TOKEN);
lua_setmetatable(state, -2);
lua_pushinteger(state, info.type);
@ -564,7 +566,7 @@ static int dfhack_matinfo_find(lua_State *state)
info.find(tokens);
}
push_matinfo(state, info);
Lua::Push(state, info);
return 1;
}
@ -632,7 +634,7 @@ static int dfhack_matinfo_decode(lua_State *state)
{
MaterialInfo info;
decode_matinfo(state, &info, true);
push_matinfo(state, info);
Lua::Push(state, info);
return 1;
}
@ -711,6 +713,9 @@ static void OpenMatinfo(lua_State *state)
{
luaL_getsubtable(state, lua_gettop(state), "matinfo");
lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_MATINFO_TOKEN);
lua_dup(state);
luaL_setfuncs(state, dfhack_matinfo_funcs, 1);

@ -36,6 +36,7 @@ distribution.
namespace DFHack {
class function_identity_base;
struct MaterialInfo;
namespace Units {
struct NoblePosition;
@ -283,6 +284,7 @@ namespace DFHack {namespace Lua {
DFHACK_EXPORT void Push(lua_State *state, df::coord obj);
DFHACK_EXPORT void Push(lua_State *state, df::coord2d obj);
void Push(lua_State *state, const Units::NoblePosition &pos);
DFHACK_EXPORT void Push(lua_State *state, MaterialInfo &info);
template<class T> inline void Push(lua_State *state, T *ptr) {
PushDFObject(state, ptr);
}

@ -0,0 +1,14 @@
local _ENV = mkmodule('plugins.workflow')
--[[
Native functions:
* isEnabled()
* setEnabled(enable)
* listConstraints([job]) -> {...}
* setConstraint(token, by_count, goal[, gap]) -> {...}
--]]
return _ENV

@ -4,6 +4,9 @@
#include "PluginManager.h"
#include "MiscUtils.h"
#include "LuaTools.h"
#include "DataFuncs.h"
#include "modules/Materials.h"
#include "modules/Items.h"
#include "modules/Gui.h"
@ -743,6 +746,20 @@ static void delete_constraint(ItemConstraint *cv)
delete cv;
}
static bool deleteConstraint(std::string name)
{
for (size_t i = 0; i < constraints.size(); i++)
{
if (constraints[i]->config.val() != name)
continue;
delete_constraint(constraints[i]);
return true;
}
return false;
}
/******************************
* JOB-CONSTRAINT MAPPING *
******************************/
@ -1347,6 +1364,153 @@ static void process_constraints(color_ostream &out)
update_jobs_by_constraints(out);
}
static void update_data_structures(color_ostream &out)
{
if (enabled) {
check_lost_jobs(out, 0);
recover_jobs(out);
update_job_data(out);
map_job_constraints(out);
map_job_items(out);
}
}
/*************
* LUA API *
*************/
static bool isEnabled() { return enabled; }
static void setEnabled(color_ostream &out, bool enable)
{
if (enable && !enabled)
{
enable_plugin(out);
}
else if (!enable && enabled)
{
enabled = false;
setOptionEnabled(CF_ENABLED, false);
stop_protect(out);
}
}
static void push_constraint(lua_State *L, ItemConstraint *cv)
{
lua_newtable(L);
int ctable = lua_gettop(L);
Lua::SetField(L, cv->config.entry_id(), ctable, "id");
Lua::SetField(L, cv->config.val(), ctable, "token");
// Constraint key
Lua::SetField(L, cv->item.type, ctable, "item_type");
Lua::SetField(L, cv->item.subtype, ctable, "item_subtype");
Lua::SetField(L, cv->is_craft, ctable, "is_craft");
Lua::PushDFObject(L, &cv->mat_mask);
lua_setfield(L, -2, "mat_mask");
Lua::SetField(L, cv->material.type, ctable, "mat_type");
Lua::SetField(L, cv->material.index, ctable, "mat_index");
Lua::SetField(L, (int)cv->min_quality, ctable, "min_quality");
// Constraint value
Lua::SetField(L, cv->goalByCount(), ctable, "goal_by_count");
Lua::SetField(L, cv->goalCount(), ctable, "goal_value");
Lua::SetField(L, cv->goalGap(), ctable, "goal_gap");
Lua::SetField(L, cv->item_amount, ctable, "cur_amount");
Lua::SetField(L, cv->item_count, ctable, "cur_count");
Lua::SetField(L, cv->item_inuse, ctable, "cur_in_use");
// Current state value
if (cv->request_resume)
Lua::SetField(L, "resume", ctable, "request");
else if (cv->request_suspend)
Lua::SetField(L, "suspend", ctable, "request");
lua_newtable(L);
for (size_t i = 0, j = 0; i < cv->jobs.size(); i++)
{
if (!cv->jobs[i]->isLive()) continue;
Lua::PushDFObject(L, cv->jobs[i]->actual_job);
lua_rawseti(L, -2, ++j);
}
lua_setfield(L, ctable, "jobs");
}
static int listConstraints(lua_State *L)
{
auto job = Lua::CheckDFObject<df::job>(L, 1);
ProtectedJob *pj = NULL;
if (job)
pj = get_known(job->id);
if (!enabled || (job && !pj))
{
lua_pushnil(L);
return 1;
}
color_ostream &out = *Lua::GetOutput(L);
update_data_structures(out);
lua_newtable(L);
auto &vec = (pj ? pj->constraints : constraints);
for (size_t i = 0; i < vec.size(); i++)
{
push_constraint(L, vec[i]);
lua_rawseti(L, -2, i+1);
}
return 1;
}
static int setConstraint(lua_State *L)
{
auto token = luaL_checkstring(L, 1);
bool by_count = lua_toboolean(L, 2);
int count = luaL_checkint(L, 3);
int gap = luaL_optint(L, 4, -1);
color_ostream &out = *Lua::GetOutput(L);
ItemConstraint *icv = get_constraint(out, token);
if (!icv)
luaL_error(L, "invalid constraint: %s", token);
icv->setGoalByCount(by_count);
icv->setGoalCount(count);
icv->setGoalGap(gap);
process_constraints(out);
push_constraint(L, icv);
return 1;
}
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(isEnabled),
DFHACK_LUA_FUNCTION(setEnabled),
DFHACK_LUA_FUNCTION(deleteConstraint),
DFHACK_LUA_END
};
DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(listConstraints),
DFHACK_LUA_COMMAND(setConstraint),
DFHACK_LUA_END
};
/******************************
* PRINTING AND THE COMMAND *
******************************/
@ -1490,13 +1654,7 @@ static command_result workflow_cmd(color_ostream &out, vector <string> & paramet
return CR_FAILURE;
}
if (enabled) {
check_lost_jobs(out, 0);
recover_jobs(out);
update_job_data(out);
map_job_constraints(out);
map_job_items(out);
}
update_data_structures(out);
df::building *workshop = NULL;
//FIXME: unused variable!
@ -1514,18 +1672,11 @@ static command_result workflow_cmd(color_ostream &out, vector <string> & paramet
if (cmd == "enable" || cmd == "disable")
{
bool enable = (cmd == "enable");
if (enable && !enabled)
{
enable_plugin(out);
}
else if (!enable && parameters.size() == 1)
if (enable)
setEnabled(out, true);
else if (parameters.size() == 1)
{
if (enabled)
{
enabled = false;
setOptionEnabled(CF_ENABLED, false);
stop_protect(out);
}
setEnabled(out, false);
out << "The plugin is disabled." << endl;
return CR_OK;
@ -1643,14 +1794,8 @@ static command_result workflow_cmd(color_ostream &out, vector <string> & paramet
if (parameters.size() != 2)
return CR_WRONG_USAGE;
for (size_t i = 0; i < constraints.size(); i++)
{
if (constraints[i]->config.val() != parameters[1])
continue;
delete_constraint(constraints[i]);
if (deleteConstraint(parameters[1]))
return CR_OK;
}
out.printerr("Constraint not found: %s\n", parameters[1].c_str());
return CR_FAILURE;

@ -0,0 +1,202 @@
-- A GUI front-end for the workflow plugin.
local utils = require 'utils'
local gui = require 'gui'
local guidm = require 'gui.dwarfmode'
local guimat = require 'gui.materials'
local widgets = require 'gui.widgets'
local dlg = require 'gui.dialogs'
local workflow = require 'plugins.workflow'
function check_enabled(cb,...)
if workflow.isEnabled() then
return cb(...)
else
dlg.showYesNoPrompt(
'Enable Plugin',
{ 'The workflow plugin is not enabled currently.', NEWLINE, NEWLINE
'Press ', { key = 'MENU_CONFIRM' }, ' to enable it.' },
COLOR_YELLOW,
curry(function(...)
workflow.setEnabled(true)
return cb(...)
end,...)
)
end
end
JobConstraints = defclass(JobConstraints, guidm.MenuOverlay)
JobConstraints.focus_path = 'workflow-job'
JobConstraints.ATTRS {
job = DEFAULT_NIL,
frame_inset = 1,
frame_background = COLOR_BLACK,
}
function JobConstraints:init(args)
self.building = dfhack.job.getHolder(self.job)
local status = { text = 'No worker', pen = COLOR_DARKGREY }
local worker = dfhack.job.getWorker(self.job)
if self.job.flags.suspend then
status = { text = 'Suspended', pen = COLOR_RED }
elseif worker then
status = { text = dfhack.TranslateName(dfhack.units.getVisibleName(worker)), pen = COLOR_GREEN }
end
self:addviews{
widgets.Label{
frame = { l = 0, t = 0 },
text = {
'Workflow Constraints'
}
},
widgets.List{
view_id = 'list',
frame = { t = 2, b = 2 },
row_height = 4,
scroll_keys = widgets.SECONDSCROLL,
},
widgets.Label{
frame = { l = 0, b = 0 },
text = {
{ key = 'LEAVESCREEN', text = ': Back',
on_activate = self:callback('dismiss') }
}
},
}
self:initListChoices(args.clist)
end
function JobConstraints:onGetSelectedBuilding()
return self.building
end
function JobConstraints:onGetSelectedJob()
return self.job
end
function describe_item_type(iobj)
local itemline = 'any item'
if iobj.item_type >= 0 then
itemline = df.item_type.attrs[iobj.item_type].caption or iobj.item_type
local def = dfhack.items.getSubtypeDef(iobj.item_type, iobj.item_subtype)
local count = dfhack.items.getSubtypeCount(iobj.item_type, iobj.item_subtype)
if def then
itemline = def.name
elseif count >= 0 then
itemline = 'any '..itemline
end
end
return itemline
end
function is_caste_mat(iobj)
return dfhack.items.isCasteMaterial(iobj.item_type)
end
function describe_material(iobj)
local matline = 'any material'
if is_caste_mat(iobj) then
matline = 'material not applicable'
elseif iobj.mat_type >= 0 then
local info = dfhack.matinfo.decode(iobj.mat_type, iobj.mat_index)
if info then
matline = info:toString()
else
matline = iobj.mat_type..':'..iobj.mat_index
end
end
return matline
end
function list_flags(list, bitfield)
for name,val in pairs(bitfield) do
if val then
table.insert(list, name)
end
end
end
function JobConstraints:initListChoices(clist)
clist = clist or workflow.listConstraints(self.job)
local choices = {}
for i,cons in ipairs(clist) do
local goal = (cons.goal_value-cons.goal_gap)..'-'..cons.goal_value
local curval
if cons.goal_by_count then
goal = goal .. ' stacks'
curval = cons.cur_count
else
goal = goal .. ' items'
curval = cons.cur_amount
end
local order_pen = COLOR_GREY
if cons.request == 'resume' then
order_pen = COLOR_GREEN
elseif cons.request == 'suspend' then
order_pen = COLOR_RED
end
local itemstr
if cons.is_craft then
itemstr = 'any craft'
else
itemstr = describe_item_type(cons)
end
if cons.min_quality > 0 then
itemstr = itemstr .. ' ('..df.item_quality[cons.min_quality]..')'
end
local matstr = describe_material(cons)
local matflagstr = ''
local matflags = {}
list_flags(matflags, cons.mat_mask)
if #matflags > 0 then
matflagstr = 'class: '..table.concat(matflags, ', ')
end
table.insert(choices, {
text = {
goal, ' ', { text = '(now '..curval..')', pen = order_pen }, NEWLINE,
' ', itemstr, NEWLINE, ' ', matstr, NEWLINE, ' ', matflagstr
},
obj = cons
})
end
self.subviews.list:setChoices(choices)
end
function JobConstraints:onInput(keys)
if self:propagateMoveKeys(keys) then
if df.global.world.selected_building ~= self.building then
self:dismiss()
end
else
JobConstraints.super.onInput(self, keys)
end
end
if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then
qerror("This script requires a workshop job selected in the 'q' mode")
end
check_enabled(function()
local job = dfhack.gui.getSelectedJob()
if not job.flags['repeat'] then
dlg.showMessage('Not Supported', 'Workflow only tracks repeat jobs.', COLOR_LIGHTRED)
return
end
local clist = workflow.listConstraints(job)
if not clist then
dlg.showMessage('Not Supported', 'This type of job is not supported by workflow.', COLOR_LIGHTRED)
return
end
JobConstraints{ job = job, clist = clist }:show()
end)