Implement the history graph in the workflow status screen.

develop
Alexander Gavrilov 2012-12-01 16:50:03 +04:00
parent 05dce0d2f1
commit 58239e97ed
11 changed files with 248 additions and 96 deletions

@ -2787,6 +2787,14 @@ before rendering the token.</p>
<li><p class="first"><tt class="docutils literal">token.tile = pen</tt></p> <li><p class="first"><tt class="docutils literal">token.tile = pen</tt></p>
<p>Specifies a pen to paint as one tile before the main part of the token.</p> <p>Specifies a pen to paint as one tile before the main part of the token.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">token.width = ...</tt></p>
<p>If specified either as a value or a callback, the text field is padded
or truncated to the specified number.</p>
</li>
<li><p class="first"><tt class="docutils literal">token.pad_char = <span class="pre">'?'</span></tt></p>
<p>If specified together with <tt class="docutils literal">width</tt>, the padding area is filled with
this character instead of just being skipped over.</p>
</li>
<li><p class="first"><tt class="docutils literal">token.key = <span class="pre">'...'</span></tt></p> <li><p class="first"><tt class="docutils literal">token.key = <span class="pre">'...'</span></tt></p>
<p>Specifies the keycode associated with the token. The string description <p>Specifies the keycode associated with the token. The string description
of the key binding is added to the text content of the token.</p> of the key binding is added to the text content of the token.</p>
@ -2848,7 +2856,9 @@ this may be extended with mouse click support.</p>
</tr> </tr>
<tr class="field"><th class="field-name">icon_pen:</th><td class="field-body">Default pen for icons.</td> <tr class="field"><th class="field-name">icon_pen:</th><td class="field-body">Default pen for icons.</td>
</tr> </tr>
<tr class="field"><th class="field-name">on_select:</th><td class="field-body">Selection change callback; called as <tt class="docutils literal">on_select(index,choice)</tt>.</td> <tr class="field"><th class="field-name">on_select:</th><td class="field-body">Selection change callback; called as <tt class="docutils literal">on_select(index,choice)</tt>.
This is also called with <em>nil</em> arguments if <tt class="docutils literal">setChoices</tt> is called
with an empty list.</td>
</tr> </tr>
<tr class="field"><th class="field-name">on_submit:</th><td class="field-body">Enter key callback; if specified, the list reacts to the key <tr class="field"><th class="field-name">on_submit:</th><td class="field-body">Enter key callback; if specified, the list reacts to the key
and calls it as <tt class="docutils literal">on_submit(index,choice)</tt>.</td> and calls it as <tt class="docutils literal">on_submit(index,choice)</tt>.</td>
@ -2928,6 +2938,8 @@ supports:</p>
<tbody valign="top"> <tbody valign="top">
<tr class="field"><th class="field-name">edit_pen:</th><td class="field-body">If specified, used instead of <tt class="docutils literal">cursor_pen</tt> for the edit field.</td> <tr class="field"><th class="field-name">edit_pen:</th><td class="field-body">If specified, used instead of <tt class="docutils literal">cursor_pen</tt> for the edit field.</td>
</tr> </tr>
<tr class="field"><th class="field-name">edit_below:</th><td class="field-body">If true, the edit field is placed below the list instead of above.</td>
</tr>
<tr class="field"><th class="field-name" colspan="2">not_found_label:</th></tr> <tr class="field"><th class="field-name" colspan="2">not_found_label:</th></tr>
<tr class="field"><td>&nbsp;</td><td class="field-body">Specifies the text of the label shown when no items match the filter.</td> <tr class="field"><td>&nbsp;</td><td class="field-body">Specifies the text of the label shown when no items match the filter.</td>
</tr> </tr>

@ -2785,6 +2785,8 @@ It has the following attributes:
:inactive_pen: If specified, used for the cursor when the widget is not active. :inactive_pen: If specified, used for the cursor when the widget is not active.
:icon_pen: Default pen for icons. :icon_pen: Default pen for icons.
:on_select: Selection change callback; called as ``on_select(index,choice)``. :on_select: Selection change callback; called as ``on_select(index,choice)``.
This is also called with *nil* arguments if ``setChoices`` is called
with an empty list.
:on_submit: Enter key callback; if specified, the list reacts to the key :on_submit: Enter key callback; if specified, the list reacts to the key
and calls it as ``on_submit(index,choice)``. and calls it as ``on_submit(index,choice)``.
:on_submit2: Shift-Enter key callback; if specified, the list reacts to the key :on_submit2: Shift-Enter key callback; if specified, the list reacts to the key

@ -3034,11 +3034,11 @@ current job, and their current status.</p>
current count is below the lower bound of the range, the job is resumed; if it current count is below the lower bound of the range, the job is resumed; if it
is above or equal to the top bound, it will be suspended. Within the range, the is above or equal to the top bound, it will be suspended. Within the range, the
specific constraint has no effect on the job; others may still affect it.</p> specific constraint has no effect on the job; others may still affect it.</p>
<p>Pressing 'c' switches the current constraint between counting stacks or items. <p>Pressing 'I' switches the current constraint between counting stacks or items.
Pressing 'm' lets you input the range directly; 'e', 'r', 'd', 'f' adjust the Pressing 'R' lets you input the range directly; 'e', 'r', 'd', 'f' adjust the
bounds by 1, 5, or 25 depending on the direction and the 'c' setting (counting bounds by 5, 10, or 20 depending on the direction and the 'I' setting (counting
items and expanding the range each gives a 5x bonus).</p> items and expanding the range each gives a 2x bonus).</p>
<p>Pressing 'n' produces a list of possible outputs of this job as guessed by <p>Pressing 'A' produces a list of possible outputs of this job as guessed by
workflow, and lets you create a new constraint by choosing one as template. If you workflow, and lets you create a new constraint by choosing one as template. If you
don't see the choice you want in the list, it likely means you have to adjust don't see the choice you want in the list, it likely means you have to adjust
the job material first using <tt class="docutils literal">job <span class="pre">item-material</span></tt> or <tt class="docutils literal"><span class="pre">gui/workshop-job</span></tt>, the job material first using <tt class="docutils literal">job <span class="pre">item-material</span></tt> or <tt class="docutils literal"><span class="pre">gui/workshop-job</span></tt>,
@ -3050,6 +3050,23 @@ added to the list. If you use Shift-Enter, the interface proceeds to the
next dialog, which allows you to edit the suggested constraint parameters to next dialog, which allows you to edit the suggested constraint parameters to
suit your need, and set the item count range.</p> suit your need, and set the item count range.</p>
<img alt="images/workflow-new2.png" src="images/workflow-new2.png" /> <img alt="images/workflow-new2.png" src="images/workflow-new2.png" />
<p>Pressing 'S' (or, with the example config, Alt-W in the 'z' stocks screen)
opens the overall status screen, which was copied from the C++ implementation
by falconne for better integration with the rest of the lua script:</p>
<img alt="images/workflow-status.png" src="images/workflow-status.png" />
<p>This screen shows all currently existing workflow constraints, and allows
monitoring and/or changing them from one screen. The constraint list can
be filtered by typing text in the field below.</p>
<p>The color of the stock level number indicates how &quot;healthy&quot; the stock level
is, based on current count and trend. Bright green is very good, green is good,
red is bad, bright red is very bad.</p>
<p>The limit number is also color-coded. Red means that there are currently no
workshops producing that item (i.e. no jobs). If it's yellow, that means the
production has been delayed, possibly due to lack of input materials.</p>
<p>The chart on the right is a plot of the last 14 days (28 half day plots) worth
of stock history for the selected item, with the rightmost point representing
the current stock value. The bright green dashed line is the target
limit (maximum) and the dark green line is that minus the gap (minimum).</p>
</div> </div>
<div class="section" id="gui-assign-rack"> <div class="section" id="gui-assign-rack">
<h2><a class="toc-backref" href="#id146">gui/assign-rack</a></h2> <h2><a class="toc-backref" href="#id146">gui/assign-rack</a></h2>

@ -2295,12 +2295,12 @@ current count is below the lower bound of the range, the job is resumed; if it
is above or equal to the top bound, it will be suspended. Within the range, the is above or equal to the top bound, it will be suspended. Within the range, the
specific constraint has no effect on the job; others may still affect it. specific constraint has no effect on the job; others may still affect it.
Pressing 'c' switches the current constraint between counting stacks or items. Pressing 'I' switches the current constraint between counting stacks or items.
Pressing 'm' lets you input the range directly; 'e', 'r', 'd', 'f' adjust the Pressing 'R' lets you input the range directly; 'e', 'r', 'd', 'f' adjust the
bounds by 1, 5, or 25 depending on the direction and the 'c' setting (counting bounds by 5, 10, or 20 depending on the direction and the 'I' setting (counting
items and expanding the range each gives a 5x bonus). items and expanding the range each gives a 2x bonus).
Pressing 'n' produces a list of possible outputs of this job as guessed by Pressing 'A' produces a list of possible outputs of this job as guessed by
workflow, and lets you create a new constraint by choosing one as template. If you workflow, and lets you create a new constraint by choosing one as template. If you
don't see the choice you want in the list, it likely means you have to adjust don't see the choice you want in the list, it likely means you have to adjust
the job material first using ``job item-material`` or ``gui/workshop-job``, the job material first using ``job item-material`` or ``gui/workshop-job``,
@ -2316,6 +2316,29 @@ suit your need, and set the item count range.
.. image:: images/workflow-new2.png .. image:: images/workflow-new2.png
Pressing 'S' (or, with the example config, Alt-W in the 'z' stocks screen)
opens the overall status screen, which was copied from the C++ implementation
by falconne for better integration with the rest of the lua script:
.. image:: images/workflow-status.png
This screen shows all currently existing workflow constraints, and allows
monitoring and/or changing them from one screen. The constraint list can
be filtered by typing text in the field below.
The color of the stock level number indicates how "healthy" the stock level
is, based on current count and trend. Bright green is very good, green is good,
red is bad, bright red is very bad.
The limit number is also color-coded. Red means that there are currently no
workshops producing that item (i.e. no jobs). If it's yellow, that means the
production has been delayed, possibly due to lack of input materials.
The chart on the right is a plot of the last 14 days (28 half day plots) worth
of stock history for the selected item, with the rightmost point representing
the current stock value. The bright green dashed line is the target
limit (maximum) and the dark green line is that minus the gap (minimum).
gui/assign-rack gui/assign-rack
=============== ===============

@ -91,6 +91,7 @@ keybinding add Alt-A@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workshop-job
# workflow front-end # workflow front-end
keybinding add Alt-W@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workflow keybinding add Alt-W@dwarfmode/QueryBuilding/Some/Workshop/Job gui/workflow
keybinding add Alt-W@overallstatus "gui/workflow status"
# assign weapon racks to squads so that they can be used # assign weapon racks to squads so that they can be used
keybinding add P@dwarfmode/QueryBuilding/Some/Weaponrack gui/assign-rack keybinding add P@dwarfmode/QueryBuilding/Some/Weaponrack gui/assign-rack

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -418,7 +418,13 @@ List.ATTRS{
function List:init(info) function List:init(info)
self.page_top = 1 self.page_top = 1
self.page_size = 1 self.page_size = 1
self:setChoices(info.choices, info.selected)
if info.choices then
self:setChoices(info.choices, info.selected)
else
self.choices = {}
self.selected = 1
end
end end
function List:setChoices(choices, selected) function List:setChoices(choices, selected)
@ -481,6 +487,9 @@ function List:moveCursor(delta, force_cb)
if cnt < 1 then if cnt < 1 then
self.page_top = 1 self.page_top = 1
self.selected = 1 self.selected = 1
if force_cb and self.on_select then
self.on_select(nil,nil)
end
return return
end end
@ -657,13 +666,17 @@ function FilteredList:init(info)
end end
end end
self.not_found = Label{ self.not_found = Label{
visible = false, visible = true,
text = info.not_found_label or 'No matches', text = info.not_found_label or 'No matches',
text_pen = COLOR_LIGHTRED, text_pen = COLOR_LIGHTRED,
frame = { l = info.icon_width, t = self.list.frame.t }, frame = { l = info.icon_width, t = self.list.frame.t },
} }
self:addviews{ self.edit, self.list, self.not_found } self:addviews{ self.edit, self.list, self.not_found }
self:setChoices(info.choices, info.selected) if info.choices then
self:setChoices(info.choices, info.selected)
else
self.choices = {}
end
end end
function FilteredList:getChoices() function FilteredList:getChoices()

@ -130,12 +130,20 @@ RangeEditor = defclass(RangeEditor, widgets.Label)
RangeEditor.ATTRS { RangeEditor.ATTRS {
get_cb = DEFAULT_NIL, get_cb = DEFAULT_NIL,
save_cb = DEFAULT_NIL save_cb = DEFAULT_NIL,
keys = {
count = 'CUSTOM_SHIFT_I',
modify = 'CUSTOM_SHIFT_R',
min_dec = 'BUILDING_TRIGGER_MIN_SIZE_DOWN',
min_inc = 'BUILDING_TRIGGER_MIN_SIZE_UP',
max_dec = 'BUILDING_TRIGGER_MAX_SIZE_DOWN',
max_inc = 'BUILDING_TRIGGER_MAX_SIZE_UP',
}
} }
function RangeEditor:init(args) function RangeEditor:init(args)
self:setText{ self:setText{
{ key = 'BUILDING_TRIGGER_ENABLE_CREATURE', { key = self.keys.count,
text = function() text = function()
local cons = self.get_cb() or null_cons local cons = self.get_cb() or null_cons
if cons.goal_by_count then if cons.goal_by_count then
@ -145,21 +153,21 @@ function RangeEditor:init(args)
end end
end, end,
on_activate = self:callback('onChangeUnit') }, on_activate = self:callback('onChangeUnit') },
{ key = 'BUILDING_TRIGGER_ENABLE_MAGMA', text = ': Modify', { key = self.keys.modify, text = ': Range',
on_activate = self:callback('onEditRange') }, on_activate = self:callback('onEditRange') },
NEWLINE, ' ', NEWLINE, ' ',
{ key = 'BUILDING_TRIGGER_MIN_SIZE_DOWN', { key = self.keys.min_dec,
on_activate = self:callback('onIncRange', 'goal_gap', 5) }, on_activate = self:callback('onIncRange', 'goal_gap', 2) },
{ key = 'BUILDING_TRIGGER_MIN_SIZE_UP', { key = self.keys.min_inc,
on_activate = self:callback('onIncRange', 'goal_gap', -1) }, on_activate = self:callback('onIncRange', 'goal_gap', -1) },
{ text = function() { text = function()
local cons = self.get_cb() or null_cons local cons = self.get_cb() or null_cons
return string.format(': Min %-4d ', cons.goal_value - cons.goal_gap) return string.format(': Min %-4d ', cons.goal_value - cons.goal_gap)
end }, end },
{ key = 'BUILDING_TRIGGER_MAX_SIZE_DOWN', { key = self.keys.max_dec,
on_activate = self:callback('onIncRange', 'goal_value', -1) }, on_activate = self:callback('onIncRange', 'goal_value', -1) },
{ key = 'BUILDING_TRIGGER_MAX_SIZE_UP', { key = self.keys.max_inc,
on_activate = self:callback('onIncRange', 'goal_value', 5) }, on_activate = self:callback('onIncRange', 'goal_value', 2) },
{ text = function() { text = function()
local cons = self.get_cb() or null_cons local cons = self.get_cb() or null_cons
return string.format(': Max %-4d', cons.goal_value) return string.format(': Max %-4d', cons.goal_value)
@ -200,9 +208,9 @@ end
function RangeEditor:onIncRange(field, delta) function RangeEditor:onIncRange(field, delta)
local cons = self.get_cb() local cons = self.get_cb()
if not cons.goal_by_count then if not cons.goal_by_count then
delta = delta * 5 delta = delta * 2
end end
cons[field] = math.max(1, cons[field] + delta) cons[field] = math.max(1, cons[field] + delta*5)
self.save_cb(cons) self.save_cb(cons)
end end
@ -295,7 +303,7 @@ function NewConstraint:init(args)
} }
}, },
widgets.Label{ widgets.Label{
frame = { l = 0, t = 13 }, frame = { l = 0, t = 14 },
text = { text = {
'Desired range: ', 'Desired range: ',
{ pen = COLOR_LIGHTCYAN, { pen = COLOR_LIGHTCYAN,
@ -311,7 +319,7 @@ function NewConstraint:init(args)
} }
}, },
RangeEditor{ RangeEditor{
frame = { l = 1, t = 15 }, frame = { l = 1, t = 16 },
get_cb = self:cb_getfield('constraint'), get_cb = self:cb_getfield('constraint'),
save_cb = self:callback('onRangeChange'), save_cb = self:callback('onRangeChange'),
}, },
@ -353,7 +361,7 @@ function NewConstraint:postinit()
end end
function NewConstraint:isValid() function NewConstraint:isValid()
return self.constraint.item_type >= 0 return self.constraint.item_type >= 0 or self.constraint.is_craft
end end
function NewConstraint:onChange() function NewConstraint:onChange()
@ -455,6 +463,59 @@ function NewConstraint:onRangeChange()
cons.goal_gap = math.max(1, math.min(cons.goal_gap, cons.goal_value-1)) cons.goal_gap = math.max(1, math.min(cons.goal_gap, cons.goal_value-1))
end end
------------------------------
-- CONSTRAINT HISTORY GRAPH --
------------------------------
HistoryGraph = defclass(HistoryGraph, widgets.Widget)
HistoryGraph.ATTRS {
frame_inset = 1,
history_pen = COLOR_CYAN,
}
function HistoryGraph:init(info)
end
function HistoryGraph:setData(history, bars)
self.history = history or {}
self.bars = bars or {}
local maxval = 1
for i,v in ipairs(self.history) do
maxval = math.max(maxval, v)
end
for i,v in ipairs(self.bars) do
maxval = math.max(maxval, v.value)
end
self.max_value = maxval
end
function HistoryGraph:onRenderFrame(dc,rect)
dc:fill(rect.x1,rect.y1,rect.x1,rect.y2,{ch='\xb3', fg=COLOR_BROWN})
dc:fill(rect.x1,rect.y2,rect.x2,rect.y2,{ch='\xc4', fg=COLOR_BROWN})
dc:seek(rect.x1,rect.y1):char('\x1e', COLOR_BROWN)
dc:seek(rect.x1,rect.y2):char('\xc5', COLOR_BROWN)
dc:seek(rect.x2,rect.y2):char('\x10', COLOR_BROWN)
dc:seek(rect.x1,rect.y2-1):char('0', COLOR_BROWN)
end
function HistoryGraph:onRenderBody(dc)
local coeff = (dc.height-1)/self.max_value
for i,v in ipairs(self.bars) do
local y = dc.height-1-math.floor(0.5 + coeff*v.value)
dc:fill(0,y,dc.width-1,y,v.pen or {ch='-', fg=COLOR_GREEN})
end
local xbase = dc.width-1-#self.history
for i,v in ipairs(self.history) do
local x = xbase + i
local y = dc.height-1-math.floor(0.5 + coeff*v)
dc:seek(x,y):char('*', self.history_pen)
end
end
------------------------------ ------------------------------
-- GLOBAL CONSTRAINT SCREEN -- -- GLOBAL CONSTRAINT SCREEN --
------------------------------ ------------------------------
@ -478,47 +539,7 @@ function ConstraintList:init(args)
self:addviews{ self:addviews{
widgets.Panel{ widgets.Panel{
frame = { w = 31, r = 0, h = 6, t = 0 }, frame = { l = 0, r = 31 },
frame_inset = 1,
subviews = {
widgets.Label{
frame = { l = 0, t = 0 },
enabled = self:callback('isAnySelected'),
text = {
{ text = function()
local cur = self:getCurConstraint()
if cur then
return string.format(
'Currently %d (%d in use)',
current_stock(cur),
if_by_count(cur, cur.cur_in_use_count, cur.cur_in_use_amount)
)
else
return 'No constraint selected'
end
end }
}
},
RangeEditor{
frame = { l = 0, t = 2 },
enabled = self:callback('isAnySelected'),
get_cb = self:callback('getCurConstraint'),
save_cb = self:callback('saveConstraint'),
},
}
},
widgets.Widget{
active = false,
frame = { w = 1, r = 31 },
frame_background = gui.BOUNDARY_FRAME.frame_pen,
},
widgets.Widget{
active = false,
frame = { w = 31, r = 0, h = 1, t = 6 },
frame_background = gui.BOUNDARY_FRAME.frame_pen,
},
widgets.Panel{
frame = { l = 0, r = 32 },
frame_inset = 1, frame_inset = 1,
on_layout = function(body) on_layout = function(body)
self.fwidth = body.width - (12+1+1+7+1+1+1+7) self.fwidth = body.width - (12+1+1+7+1+1+1+7)
@ -541,6 +562,7 @@ function ConstraintList:init(args)
edit_pen = COLOR_LIGHTCYAN, edit_pen = COLOR_LIGHTCYAN,
text_pen = { fg = COLOR_GREY, bg = COLOR_BLACK }, text_pen = { fg = COLOR_GREY, bg = COLOR_BLACK },
cursor_pen = { fg = COLOR_WHITE, bg = COLOR_GREEN }, cursor_pen = { fg = COLOR_WHITE, bg = COLOR_GREEN },
on_select = self:callback('onSelectConstraint'),
}, },
widgets.Label{ widgets.Label{
frame = { b = 0, h = 1 }, frame = { b = 0, h = 1 },
@ -557,27 +579,66 @@ function ConstraintList:init(args)
else else
return COLOR_WHITE return COLOR_WHITE
end end
end }, ', ', end },
{ key = 'CUSTOM_SHIFT_S', text = ': Search', }
on_activate = function() }
self.subviews.list.edit.active = not self.subviews.list.edit.active }
end, },
pen = function() widgets.Panel{
if self.subviews.list.edit.active then frame = { w = 30, r = 0, h = 6, t = 0 },
return COLOR_LIGHTCYAN frame_inset = 1,
subviews = {
widgets.Label{
frame = { l = 0, t = 0 },
enabled = self:callback('isAnySelected'),
text = {
{ text = function()
local cur = self:getCurConstraint()
if cur then
return string.format(
'Currently %d (%d in use)',
current_stock(cur),
if_by_count(cur, cur.cur_in_use_count, cur.cur_in_use_amount)
)
else else
return COLOR_WHITE return 'No constraint selected'
end end
end } end }
} }
} },
RangeEditor{
frame = { l = 0, t = 2 },
enabled = self:callback('isAnySelected'),
get_cb = self:callback('getCurConstraint'),
save_cb = self:callback('saveConstraint'),
keys = {
count = 'CUSTOM_SHIFT_I',
modify = 'CUSTOM_SHIFT_R',
min_dec = 'SECONDSCROLL_PAGEUP',
min_inc = 'SECONDSCROLL_PAGEDOWN',
max_dec = 'SECONDSCROLL_UP',
max_inc = 'SECONDSCROLL_DOWN',
}
},
} }
}, },
widgets.Widget{
active = false,
frame = { w = 1, r = 30 },
frame_background = gui.BOUNDARY_FRAME.frame_pen,
},
widgets.Widget{
active = false,
frame = { w = 30, r = 0, h = 1, t = 6 },
frame_background = gui.BOUNDARY_FRAME.frame_pen,
},
HistoryGraph{
view_id = 'graph',
frame = { w = 30, r = 0, t = 7, b = 0 },
}
} }
self.subviews.list.edit.active = false self:initListChoices(nil, args.select_token)
self:initListChoices()
end end
function stock_trend_color(cons) function stock_trend_color(cons)
@ -733,6 +794,29 @@ function ConstraintList:onDeleteConstraint()
) )
end end
function ConstraintList:onSelectConstraint(idx,item)
local history, bars
if item then
local cons = item.obj
local vfield = if_by_count(cons, 'cur_count', 'cur_amount')
bars = {
{ value = cons.goal_value - cons.goal_gap, pen = {ch='-', fg=COLOR_GREEN} },
{ value = cons.goal_value, pen = {ch='-', fg=COLOR_LIGHTGREEN} },
}
history = {}
for i,v in ipairs(cons.history or {}) do
table.insert(history, v[vfield])
end
table.insert(history, cons[vfield])
end
self.subviews.graph:setData(history, bars)
end
------------------------------- -------------------------------
-- WORKSHOP JOB INFO OVERLAY -- -- WORKSHOP JOB INFO OVERLAY --
------------------------------- -------------------------------
@ -772,14 +856,20 @@ function JobConstraints:init(args)
widgets.Label{ widgets.Label{
frame = { l = 0, b = 0 }, frame = { l = 0, b = 0 },
text = { text = {
{ key = 'CUSTOM_N', text = ': New limit, ', { key = 'CUSTOM_SHIFT_A', text = ': Add limit, ',
on_activate = self:callback('onNewConstraint') }, on_activate = self:callback('onNewConstraint') },
{ key = 'CUSTOM_X', text = ': Delete', { key = 'CUSTOM_SHIFT_X', text = ': Delete',
enabled = self:callback('isAnySelected'), enabled = self:callback('isAnySelected'),
on_activate = self:callback('onDeleteConstraint') }, on_activate = self:callback('onDeleteConstraint') },
NEWLINE, NEWLINE, NEWLINE, NEWLINE,
{ key = 'LEAVESCREEN', text = ': Back', { key = 'LEAVESCREEN', text = ': Back',
on_activate = self:callback('dismiss') } on_activate = self:callback('dismiss') },
' ',
{ key = 'CUSTOM_SHIFT_S', text = ': Status',
on_activate = function()
local sel = self:getCurConstraint()
ConstraintList{ select_token = (sel or {}).token }:show()
end }
} }
}, },
} }
@ -873,22 +963,16 @@ function JobConstraints:onNewConstraint()
local choices = {} local choices = {}
for i,cons in ipairs(variants) do for i,cons in ipairs(variants) do
local itemstr = describe_item_type(cons) local itemstr = describe_item_type(cons)
local matstr = describe_material(cons) local matstr,matflags = describe_material(cons)
local matflags = utils.list_bitfield_flags(cons.mat_mask) if matflags then
if #matflags > 0 then matstr = matflags..' '..matstr
local fstr = table.concat(matflags, '/')
if matstr == 'any material' then
matstr = 'any '..fstr
else
matstr = 'any '..fstr..' '..matstr
end
end end
table.insert(choices, { text = itemstr..' of '..matstr, obj = cons }) table.insert(choices, { text = itemstr..' of '..matstr, obj = cons })
end end
dlg.ListBox{ dlg.ListBox{
frame_title = 'New limit', frame_title = 'Add limit',
text = 'Select one of the possible outputs:', text = 'Select one of the possible outputs:',
text_pen = COLOR_WHITE, text_pen = COLOR_WHITE,
choices = choices, choices = choices,
@ -930,7 +1014,7 @@ end
local args = {...} local args = {...}
if args[1] == 'list' then if args[1] == 'status' then
check_enabled(function() ConstraintList{}:show() end) check_enabled(function() ConstraintList{}:show() end)
else else
if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then