fix(kæde): pausede kæder kan genoptages, træet folder ud og teksten passer i cellerne

This commit is contained in:
2026-09-30 14:07:37 +02:00
parent f0b8df8ae3
commit b77bda5334
5 changed files with 263 additions and 109 deletions
+175 -66
View File
@@ -1,31 +1,73 @@
-- AD Chain View (ren logik) -- AD Chain View (ren logik)
-- Rækkerne til fanen Kæder ud fra ADChain.last: rod -> noder rekursivt (kilde, behov pr. måned, flowstatus), -- Rækkerne til fanen Kæder ud fra ADChain.last. Hver rod er foldet sammen som standard (én linje med samlet
-- forslag/auto-skift under afgrøden, og nederst frie marker + antal automatiske skift. Ingen spiladgang: -- status + antal forslag); foldet ud vises varerne rekursivt (kilde, behov pr. måned, status, lager). Forslag
-- titleOf(navn) -> visningstitel, fmt(liter) -> kompakt tal, textOf(l10n-nøgle) -> tekst gives udefra. -- vises altid (under roden eller under afgrøden). Pausede rødder (som modellen ikke længere ser som rødder)
-- Række: {kind="root"|"node"|"suggest"|"dismiss"|"auto"|"info"|"empty", indent, text, level, cells={c1..c5}, -- får en grå linje med [Genoptag]. Nederst frie marker + antal automatiske skift. Ingen spiladgang:
-- action={kind="approve"|"dismiss"|"pause"|"resume", fieldId|rootKey}, fillType} -- titleOf(navn) -> titel, fmt(liter) -> kompakt tal, textOf(l10n-nøgle) -> tekst gives udefra.
-- Række: {kind="header"|"root"|"node"|"suggest"|"auto"|"info"|"empty", indent, level, cells={c1..c5}, text,
-- actions={[celle]={kind="approve"|"dismiss"|"pause"|"resume", fieldId|rootKey}}, toggle=rootKey, fillType}
ADChainView = {} ADChainView = {}
ADChainView.INDENT = " " ADChainView.INDENT = " "
-- nøgler ud over spu_tab_chains/spu_chain_empty/... som rækkerne bruger (til l10n-testen) -- Tegnbudget pr. celle i flowListPrefab (gui/SmartPickupFrame.xml: textMaxWidth 192/162/62/244/92 px).
ADChainView.TEXT_KEYS = {"spu_chain_perMonth", "spu_chain_noField", "spu_chain_buySource", "spu_chain_cycle", -- Antaget ~11,5 px pr. tegn (spuInfoText: 800 px ~ 70 tegn), fed ~12,5 px; c3 har kun tal (~10 px pr. tegn).
"spu_chain_fieldShort", "spu_chain_flowOk", "spu_chain_flowNone", "spu_chain_flowStopped", "spu_chain_approve", ADChainView.CELL_CHARS = {15, 14, 6, 21, 8}
"spu_chain_dismiss", "spu_chain_switches", "spu_chain_rootOk", "spu_chain_rootError", "spu_chain_rootWaiting", ADChainView.MAX_SOURCE_NAMES = 2
"spu_chain_rootPaused"} -- "stramt": lager for en mark-afgrøde under så mange måneders behov -> gul
ADChainView.TIGHT_MONTHS = 3
-- nøgler rækkerne bruger (til l10n-testen)
ADChainView.TEXT_KEYS = {"spu_chain_empty", "spu_chain_approve", "spu_chain_dismiss", "spu_chain_auto",
"spu_chain_flowOk", "spu_chain_flowMissing", "spu_chain_flowNone", "spu_chain_flowStopped", "spu_chain_bottleneck",
"spu_chain_buy", "spu_chain_cycle", "spu_chain_fieldShort", "spu_chain_tight", "spu_chain_noField", "spu_chain_buySource",
"spu_chain_pause", "spu_chain_resume", "spu_chain_rootOk", "spu_chain_rootError", "spu_chain_rootWaiting",
"spu_chain_rootPaused", "spu_chain_rootSummary", "spu_chain_free", "spu_chain_switches", "spu_chain_gone", "spu_chain_col_item",
"spu_chain_col_source", "spu_chain_col_perMonth", "spu_chain_col_status", "spu_chain_col_stock"}
local FLOW_KEYS = {ok = "spu_chain_flowOk", missing = "spu_chain_flowNone", stopped = "spu_chain_flowStopped", noVehicle = "spu_chain_flowMissing"} local FLOW_KEYS = {ok = "spu_chain_flowOk", missing = "spu_chain_flowNone", stopped = "spu_chain_flowStopped", noVehicle = "spu_chain_flowMissing"}
local ROOT_STATUS = {running = "spu_chain_rootOk", error = "spu_chain_rootError", waiting = "spu_chain_rootWaiting", stopped = "spu_chain_rootPaused"}
local function identity(value) return tostring(value) end local function identity(value) return tostring(value) end
local function plannedFruit(field) -- ---- UTF-8-sikker længde/afkortning (æøå og → er flere bytes) ----
if field.targetFruit ~= nil and field.targetFruit ~= "" then return field.targetFruit end
return field.fruit function ADChainView.textLength(text)
local count = 0
for _ in tostring(text):gmatch("[^\128-\191]") do count = count + 1 end
return count
end
local function utf8Prefix(text, chars)
local result, count = {}, 0
for char in text:gmatch("[^\128-\191][\128-\191]*") do
if count >= chars then break end
table.insert(result, char)
count = count + 1
end
return table.concat(result)
end
-- Afkort til celle-budgettet med ".." til sidst.
function ADChainView.fit(text, chars)
text = tostring(text or "")
if ADChainView.textLength(text) <= chars then return text end
return utf8Prefix(text, math.max(0, chars - 2)) .. ".."
end
local function fitCells(cells)
local result = {}
for index = 1, 5 do result[index] = ADChainView.fit(cells[index] or "", ADChainView.CELL_CHARS[index]) end
return result
end end
local function haText(ha) local function haText(ha)
return string.format("%.0f", ha or 0) return string.format("%.0f", ha or 0)
end end
local function plannedFruit(field)
if field.targetFruit ~= nil and field.targetFruit ~= "" then return field.targetFruit end
return field.fruit
end
local function edgeKey(node) local function edgeKey(node)
return tostring(node.consumer) .. "|" .. tostring(node.fillType) return tostring(node.consumer) .. "|" .. tostring(node.fillType)
end end
@@ -36,7 +78,17 @@ local function shortageByFruit(last)
return result return result
end end
-- Alle noder under roden (dybde først). local function stockOf(last, fruit)
local stock = last.data ~= nil and last.data.stock or nil
return stock ~= nil and stock[fruit] or nil
end
-- Mark-afgrøde uden mangel men med lager under TIGHT_MONTHS måneders behov.
local function isTight(node, last, short)
if node.source ~= "field" or node.fruit == nil or short[node.fruit] ~= nil then return false end
return (stockOf(last, node.fruit) or 0) < ADChainView.TIGHT_MONTHS * (node.perMonth or 0)
end
local function eachNode(nodes, fn, depth) local function eachNode(nodes, fn, depth)
for _, node in ipairs(nodes or {}) do for _, node in ipairs(nodes or {}) do
fn(node, depth or 1) fn(node, depth or 1)
@@ -56,14 +108,22 @@ function ADChainView.rootLevel(root, last)
if node.fruit ~= nil and short[node.fruit] ~= nil then hasShortage = true end if node.fruit ~= nil and short[node.fruit] ~= nil then hasShortage = true end
if (node.shortPerMonth or 0) > 0 then hasShortage = true end if (node.shortPerMonth or 0) > 0 then hasShortage = true end
local status = node.consumer ~= nil and flowStatus[edgeKey(node)] or nil local status = node.consumer ~= nil and flowStatus[edgeKey(node)] or nil
if status ~= nil and status ~= "ok" then hasWaiting = true end if (status ~= nil and status ~= "ok") or isTight(node, last, short) then hasWaiting = true end
end) end)
if hasShortage then return "error" end if hasShortage then return "error" end
if hasWaiting then return "waiting" end if hasWaiting then return "waiting" end
return "running" return "running"
end end
-- Kilden som tekst: fabrikker / stalde / marker med planlagt afgrøde / køb. -- Højst MAX_SOURCE_NAMES navne + " +k".
local function joinCapped(names)
local shown = {}
for index = 1, math.min(#names, ADChainView.MAX_SOURCE_NAMES) do shown[index] = names[index] end
local text = table.concat(shown, ", ")
if #names > #shown then text = text .. " +" .. tostring(#names - #shown) end
return text
end
local function sourceText(node, last, textOf) local function sourceText(node, last, textOf)
local names = {} local names = {}
if node.source == "production" then if node.source == "production" then
@@ -72,9 +132,7 @@ local function sourceText(node, last, textOf)
for _, herd in ipairs(node.herds or {}) do table.insert(names, tostring(herd.buildingName)) end for _, herd in ipairs(node.herds or {}) do table.insert(names, tostring(herd.buildingName)) end
elseif node.source == "field" then elseif node.source == "field" then
for _, field in ipairs(last.fields or {}) do for _, field in ipairs(last.fields or {}) do
if plannedFruit(field) == node.fruit then if plannedFruit(field) == node.fruit then table.insert(names, "F" .. tostring(field.id)) end
table.insert(names, string.format("F%s (%s ha)", tostring(field.id), haText(field.areaHa)))
end
end end
if #names == 0 then return textOf("spu_chain_noField") end if #names == 0 then return textOf("spu_chain_noField") end
elseif node.source == "buy" then elseif node.source == "buy" then
@@ -82,29 +140,26 @@ local function sourceText(node, last, textOf)
else else
return "-" return "-"
end end
return table.concat(names, ", ") return joinCapped(names)
end end
-- Nodens status (tekst, niveau): mangel/flaskehals/køb/kredsløb først, ellers flowets status. -- Nodens status (tekst, niveau): kredsløb/flaskehals/køb/mangel (rød) > flow ikke ok (gul) > stramt lager (gul) > flow ok.
local function nodeStatus(node, last, titleOf, fmt, textOf, short) local function nodeStatus(node, last, fmt, textOf, short)
local title = titleOf(node.fillType)
if node.source == "cycle" then return textOf("spu_chain_cycle"), "error" end if node.source == "cycle" then return textOf("spu_chain_cycle"), "error" end
if (node.shortPerMonth or 0) > 0 then if (node.shortPerMonth or 0) > 0 then
if node.source == "production" then local key = node.source == "production" and "spu_chain_bottleneck" or "spu_chain_buy"
return string.format(textOf("spu_chain_bottleneck"), title, fmt(node.shortPerMonth)), "error" return string.format(textOf(key), fmt(node.shortPerMonth)), "error"
end
return string.format(textOf("spu_chain_buy"), title, fmt(node.shortPerMonth)), "error"
end end
local shortage = node.fruit ~= nil and short[node.fruit] or nil local shortage = node.fruit ~= nil and short[node.fruit] or nil
if shortage ~= nil then if shortage ~= nil then return string.format(textOf("spu_chain_fieldShort"), haText(shortage.missingHa)), "error" end
return string.format(textOf("spu_chain_fieldShort"), haText(shortage.missingHa)), "error"
end
local status = node.consumer ~= nil and (last.flowStatus or {})[edgeKey(node)] or nil local status = node.consumer ~= nil and (last.flowStatus or {})[edgeKey(node)] or nil
if status == nil then return "", "running" end if status ~= nil and status ~= "ok" then return textOf(FLOW_KEYS[status] or "spu_chain_flowNone"), "waiting" end
return textOf(FLOW_KEYS[status] or "spu_chain_flowNone"), status == "ok" and "running" or "waiting" if isTight(node, last, short) then return textOf("spu_chain_tight"), "waiting" end
if status == "ok" then return textOf("spu_chain_flowOk"), "running" end
return "", "running"
end end
local function joinCells(cells) local function joinText(cells)
local parts = {} local parts = {}
for _, cell in ipairs(cells) do for _, cell in ipairs(cells) do
if cell ~= nil and cell ~= "" then table.insert(parts, cell) end if cell ~= nil and cell ~= "" then table.insert(parts, cell) end
@@ -112,51 +167,98 @@ local function joinCells(cells)
return table.concat(parts, " · ") return table.concat(parts, " · ")
end end
local function makeRow(kind, indent, level, cells, action) local function makeRow(kind, indent, level, cells, actions, toggle)
return {kind = kind, indent = indent, level = level, cells = cells, action = action, text = joinCells(cells)} local fitted = fitCells(cells)
return {kind = kind, indent = indent, level = level, cells = fitted, actions = actions or {}, toggle = toggle, text = joinText(fitted)}
end end
-- Forslag (godkend + afvis) og auto-skift for afgrøden, lige under dens node. local function suggestRow(action, indent, titleOf, textOf)
local function actionRows(rows, fruit, indent, last, titleOf, textOf, shown)
if fruit == nil or shown[fruit] then return end
shown[fruit] = true
local pad = string.rep(ADChainView.INDENT, indent) local pad = string.rep(ADChainView.INDENT, indent)
for _, action in ipairs(last.actions or {}) do local from = (action.from ~= nil and action.from ~= "") and titleOf(action.from) or "-"
if action.to == fruit then local cells = {pad .. "F" .. tostring(action.fieldId) .. " " .. from, "→ " .. titleOf(action.to), haText(action.hectares) .. " ha"}
local fromTitle = (action.from ~= nil and action.from ~= "") and titleOf(action.from) or "-"
if action.mode == "suggest" then if action.mode == "suggest" then
local text = string.format(textOf("spu_chain_suggest"), action.fieldId, fromTitle, titleOf(action.to), action.hectares or 0) cells[4], cells[5] = textOf("spu_chain_approve"), textOf("spu_chain_dismiss")
table.insert(rows, makeRow("suggest", indent, "waiting", {pad, "", "", text, textOf("spu_chain_approve")}, return makeRow("suggest", indent, "waiting", cells,
{kind = "approve", fieldId = action.fieldId})) {[4] = {kind = "approve", fieldId = action.fieldId}, [5] = {kind = "dismiss", fieldId = action.fieldId}})
table.insert(rows, makeRow("dismiss", indent, "waiting", {pad, "", "", "", textOf("spu_chain_dismiss")},
{kind = "dismiss", fieldId = action.fieldId}))
else
local text = string.format(textOf("spu_chain_auto"), action.fieldId, titleOf(action.to))
table.insert(rows, makeRow("auto", indent, "running", {pad, "", "", text, ""}, nil))
end
end
end end
cells[4] = textOf("spu_chain_auto")
return makeRow("auto", indent, "running", cells)
end end
local ROOT_STATUS = {running = "spu_chain_rootOk", error = "spu_chain_rootError", waiting = "spu_chain_rootWaiting", stopped = "spu_chain_rootPaused"} -- Handlinger for de afgrøder (i rækkefølge) der hentes fra mark under roden.
local function rootFruits(root)
local fruits, seen = {}, {}
eachNode(root.children, function(node)
if node.source == "field" and node.fruit ~= nil and not seen[node.fruit] then
seen[node.fruit] = true
table.insert(fruits, node.fruit)
end
end)
return fruits
end
local function rootRows(rows, root, last, titleOf, fmt, textOf, short) local function actionsFor(last, fruit, modes)
local list = {}
for _, action in ipairs(last.actions or {}) do
if action.to == fruit and modes[action.mode] then table.insert(list, action) end
end
return list
end
local function rootRows(rows, root, last, ctx, isExpanded)
local level = ADChainView.rootLevel(root, last) local level = ADChainView.rootLevel(root, last)
local paused = (last.paused or {})[root.key] == true local paused = (last.paused or {})[root.key] == true
table.insert(rows, makeRow("root", 0, level, {titleOf(root.name), tostring(root.buildingName or ""), "", local suggestions = {}
textOf(ROOT_STATUS[level]), textOf(paused and "spu_chain_resume" or "spu_chain_pause")}, for _, fruit in ipairs(rootFruits(root)) do
{kind = paused and "resume" or "pause", rootKey = root.key})) for _, action in ipairs(actionsFor(last, fruit, {suggest = true})) do table.insert(suggestions, action) end
end
local status = ctx.textOf(ROOT_STATUS[level])
if #suggestions > 0 then status = string.format(ctx.textOf("spu_chain_rootSummary"), status, #suggestions) end
local marker = (#(root.children or {}) > 0) and (isExpanded and "– " or "+ ") or ""
table.insert(rows, makeRow("root", 0, level, {marker .. ctx.titleOf(root.name), tostring(root.buildingName or ""), "", status,
ctx.textOf(paused and "spu_chain_resume" or "spu_chain_pause")},
{[5] = {kind = paused and "resume" or "pause", rootKey = root.key}}, root.key))
if not isExpanded then
for _, action in ipairs(suggestions) do table.insert(rows, suggestRow(action, 1, ctx.titleOf, ctx.textOf)) end
return
end
local shown = {} local shown = {}
eachNode(root.children, function(node, depth) eachNode(root.children, function(node, depth)
local status, nodeLevel = nodeStatus(node, last, titleOf, fmt, textOf, short) local text, nodeLevel = nodeStatus(node, last, ctx.fmt, ctx.textOf, ctx.short)
local row = makeRow("node", depth, nodeLevel, {string.rep(ADChainView.INDENT, depth) .. titleOf(node.fillType), local stock = node.source == "field" and stockOf(last, node.fruit) or nil
sourceText(node, last, textOf), string.format(textOf("spu_chain_perMonth"), fmt(node.perMonth or 0)), status, ""}, nil) local row = makeRow("node", depth, nodeLevel, {string.rep(ADChainView.INDENT, depth) .. ctx.titleOf(node.fillType),
sourceText(node, last, ctx.textOf), ctx.fmt(node.perMonth or 0), text, stock ~= nil and ctx.fmt(stock) or ""})
row.fillType = node.fillType row.fillType = node.fillType
table.insert(rows, row) table.insert(rows, row)
if node.source == "field" then actionRows(rows, node.fruit, depth + 1, last, titleOf, textOf, shown) end if node.source == "field" and node.fruit ~= nil and not shown[node.fruit] then
shown[node.fruit] = true
for _, action in ipairs(actionsFor(last, node.fruit, {suggest = true, auto = true})) do
table.insert(rows, suggestRow(action, depth + 1, ctx.titleOf, ctx.textOf))
end
end
end) end)
end end
-- Pausede rødder: recipesWithPaused slukker dem, så modellen ser dem ikke som rødder -> grå linje med [Genoptag].
local function pausedRows(rows, last, ctx)
local present, keys = {}, {}
for _, root in ipairs(last.chain ~= nil and last.chain.roots or {}) do present[root.key] = true end
for key in pairs(last.paused or {}) do
if not present[key] then table.insert(keys, key) end
end
table.sort(keys, function(a, b) return tostring(a) < tostring(b) end)
for _, key in ipairs(keys) do
local recipe
for _, candidate in ipairs(last.data ~= nil and last.data.recipes or {}) do
if candidate.key == key then recipe = candidate end
end
local name = recipe ~= nil and recipe.name ~= nil and ctx.titleOf(recipe.name) or tostring(key)
table.insert(rows, makeRow("root", 0, "stopped", {name, recipe ~= nil and tostring(recipe.buildingName or "") or "", "",
ctx.textOf("spu_chain_rootPaused"), ctx.textOf("spu_chain_resume")}, {[5] = {kind = "resume", rootKey = key}}))
end
return #keys
end
local function footerRows(rows, last, textOf) local function footerRows(rows, last, textOf)
local free = {} local free = {}
for _, field in ipairs(last.fields or {}) do for _, field in ipairs(last.fields or {}) do
@@ -170,18 +272,25 @@ local function footerRows(rows, last, textOf)
table.insert(rows, {kind = "info", indent = 0, text = string.format(textOf("spu_chain_switches"), last.switchCount or 0)}) table.insert(rows, {kind = "info", indent = 0, text = string.format(textOf("spu_chain_switches"), last.switchCount or 0)})
end end
function ADChainView.rows(last, titleOf, fmt, textOf) -- expanded = {[rodnøgle] = true} for udfoldede rødder (alle andre er foldet sammen).
textOf, fmt = textOf or identity, fmt or identity function ADChainView.rows(last, titleOf, fmt, textOf, expanded)
textOf, fmt, expanded = textOf or identity, fmt or identity, expanded or {}
local titles = last ~= nil and last.titles or {} local titles = last ~= nil and last.titles or {}
titleOf = titleOf or function(name) return (titles or {})[name] or tostring(name) end titleOf = titleOf or function(name) return (titles or {})[name] or tostring(name) end
local rows = {} local rows = {}
local roots = last ~= nil and last.chain ~= nil and last.chain.roots or {} local roots = last ~= nil and last.chain ~= nil and last.chain.roots or {}
if #roots == 0 then local hasPaused = last ~= nil and next(last.paused or {}) ~= nil
if #roots == 0 and not hasPaused then
table.insert(rows, {kind = "empty", indent = 0, text = textOf("spu_chain_empty")}) table.insert(rows, {kind = "empty", indent = 0, text = textOf("spu_chain_empty")})
end end
if last == nil then return rows end if last == nil then return rows end
local short = shortageByFruit(last) if #roots > 0 or hasPaused then
for _, root in ipairs(roots) do rootRows(rows, root, last, titleOf, fmt, textOf, short) end table.insert(rows, makeRow("header", 0, nil, {textOf("spu_chain_col_item"), textOf("spu_chain_col_source"),
textOf("spu_chain_col_perMonth"), textOf("spu_chain_col_status"), textOf("spu_chain_col_stock")}))
end
local ctx = {titleOf = titleOf, fmt = fmt, textOf = textOf, short = shortageByFruit(last)}
for _, root in ipairs(roots) do rootRows(rows, root, last, ctx, expanded[root.key] == true) end
pausedRows(rows, last, ctx)
footerRows(rows, last, textOf) footerRows(rows, last, textOf)
return rows return rows
end end
+43 -17
View File
@@ -26,21 +26,46 @@ function SmartPickupFrame:chainTitle(last, name)
return title or self:fruitTitle(name) return title or self:fruitTitle(name)
end end
-- Klik på en kæde-knap: udfør, evaluér straks (ny visning) og genopbyg. -- Klik på en kæde-knap: udfør, evaluér straks (ny visning) og genopbyg. Et forslag der ikke længere findes
-- (approve/dismiss -> false) meldes i statuslinjen som i "Kræver handling".
function SmartPickupFrame:onChainAction(action) function SmartPickupFrame:onChainAction(action)
local ok = true
if action.kind == "approve" then if action.kind == "approve" then
ADChain.approve(action.fieldId) ok = ADChain.approve(action.fieldId)
elseif action.kind == "dismiss" then elseif action.kind == "dismiss" then
ADChain.dismiss(action.fieldId) ok = ADChain.dismiss(action.fieldId)
elseif action.kind == "pause" or action.kind == "resume" then elseif action.kind == "pause" or action.kind == "resume" then
ADChain.setPaused(action.rootKey, action.kind == "pause") ADChain.setPaused(action.rootKey, action.kind == "pause")
end end
if ok == false then
local text = self.i18n:getText("spu_chain_gone")
self.statusOverride, self.statusOverrideTicks = text, SmartPickupFrame.STATUS_OVERRIDE_TICKS
if ADFieldJobs ~= nil and ADFieldJobs.notify ~= nil then ADFieldJobs.notify(text, false) end
end
local adEnv = self.getAdEnv() local adEnv = self.getAdEnv()
if adEnv ~= nil then self:chainLast(adEnv, true) end if adEnv ~= nil then self:chainLast(adEnv, true) end
self:requestRebuild() self:requestRebuild()
end end
-- Række med prik + c1..c5 (flowListPrefab); knappen er c5 (klik i c5 = handling, som Flows-listen). -- Klik på rodens linje uden for knappen: fold ud/sammen (huskes kun mens spillet kører).
function SmartPickupFrame:toggleChainRoot(rootKey)
self.chainExpanded = self.chainExpanded or {}
self.chainExpanded[rootKey] = not self.chainExpanded[rootKey] or nil
self:requestRebuild()
end
-- Hvilken celle blev der klikket i? Højeste celle hvis venstre kant ligger til venstre for klikket.
local function clickedCell(frame, cells)
if frame.lastClickX == nil then return nil end
for index = #cells, 1, -1 do
local cell = cells[index]
if cell ~= nil and cell.absPosition ~= nil and frame.lastClickX >= cell.absPosition[1] then return index end
end
return nil
end
-- Række med prik + c1..c5 (flowListPrefab). Knapper er celler (entry.actions[celle], som [Start]/[Stop] i
-- Flows-listen); et klik andetsteds på en rodlinje folder den ud/sammen.
function SmartPickupFrame:addChainRow(entry) function SmartPickupFrame:addChainRow(entry)
local unpackFn = table.unpack or unpack local unpackFn = table.unpack or unpack
local row = self:addRow(self.flowListPrefab, {kind = "chainRow"}) local row = self:addRow(self.flowListPrefab, {kind = "chainRow"})
@@ -48,21 +73,22 @@ function SmartPickupFrame:addChainRow(entry)
for index = 1, 5 do cells[index] = row:getDescendantByName("c" .. index) end for index = 1, 5 do cells[index] = row:getDescendantByName("c" .. index) end
SmartPickupFrame.setCells(cells, entry.cells) SmartPickupFrame.setCells(cells, entry.cells)
local dot = row:getDescendantByName("dot") local dot = row:getDescendantByName("dot")
local color = ADFlowView.LEVEL_COLORS[entry.level or "running"] local color = entry.level ~= nil and ADFlowView.LEVEL_COLORS[entry.level] or nil
if dot ~= nil and dot.setImageColor ~= nil and color ~= nil then if dot ~= nil and dot.setImageColor ~= nil and color ~= nil then dot:setImageColor(nil, unpackFn(color)) end
dot:setImageColor(nil, unpackFn(color)) -- kun rod og varer har en status-prik (overskrift, forslag og auto-skift har ingen)
-- rækker uden egen status (forslag/afvis/auto) får ingen prik — kun rod og noder har status if dot ~= nil and dot.setVisible ~= nil and entry.kind ~= "root" and entry.kind ~= "node" then dot:setVisible(false) end
if entry.kind ~= "root" and entry.kind ~= "node" and dot.setVisible ~= nil then dot:setVisible(false) end if cells[4] ~= nil and cells[4].setTextColor ~= nil and color ~= nil and entry.level ~= "running" and entry.kind ~= "suggest" then
end
if cells[4] ~= nil and cells[4].setTextColor ~= nil and color ~= nil and entry.level ~= "running" then
cells[4]:setTextColor(unpackFn(color)) cells[4]:setTextColor(unpackFn(color))
end end
if entry.action ~= nil then local actions, toggle = entry.actions or {}, entry.toggle
local action, actionCell = entry.action, cells[5] if next(actions) ~= nil or toggle ~= nil then
self:makeRowClickable(row, function(frame) self:makeRowClickable(row, function(frame)
local onButton = frame.lastClickX == nil or actionCell == nil or actionCell.absPosition == nil local action = actions[clickedCell(frame, cells) or 0]
or frame.lastClickX >= actionCell.absPosition[1] if action ~= nil then
if onButton then frame:onChainAction(action) end frame:onChainAction(action)
elseif toggle ~= nil then
frame:toggleChainRoot(toggle)
end
end) end)
end end
return row return row
@@ -72,7 +98,7 @@ function SmartPickupFrame:fillChainRows(adEnv)
local i18n = self.i18n local i18n = self.i18n
local last = self:chainLast(adEnv, false) local last = self:chainLast(adEnv, false)
local rows = ADChainView.rows(last, function(name) return self:chainTitle(last, name) end, local rows = ADChainView.rows(last, function(name) return self:chainTitle(last, name) end,
SmartPickupFrame.formatCompact, function(key) return i18n:getText(key) end) SmartPickupFrame.formatCompact, function(key) return i18n:getText(key) end, self.chainExpanded or {})
for _, entry in ipairs(rows) do for _, entry in ipairs(rows) do
if entry.kind == "info" or entry.kind == "empty" then if entry.kind == "info" or entry.kind == "empty" then
self:addTextRow(entry.text) self:addTextRow(entry.text)
@@ -473,6 +473,7 @@ function SmartPickupFrame:rebuild()
if adEnv ~= nil then if adEnv ~= nil then
self:fillChainRows(adEnv) self:fillChainRows(adEnv)
end end
self.statusRow = self:addTextRow(self.statusOverride or "")
self.monthRebuildTicks = SmartPickupFrame.CHAIN_REBUILD_TICKS self.monthRebuildTicks = SmartPickupFrame.CHAIN_REBUILD_TICKS
self:colorRows() self:colorRows()
self.boxLayout:invalidateLayout() self.boxLayout:invalidateLayout()
@@ -653,6 +654,12 @@ function SmartPickupFrame:refreshLive()
end end
if self.currentTab == SmartPickupFrame.TAB_MONTH or self.currentTab == SmartPickupFrame.TAB_FIELDSILOS if self.currentTab == SmartPickupFrame.TAB_MONTH or self.currentTab == SmartPickupFrame.TAB_FIELDSILOS
or self.currentTab == SmartPickupFrame.TAB_CHAINS then or self.currentTab == SmartPickupFrame.TAB_CHAINS then
if self.currentTab == SmartPickupFrame.TAB_CHAINS then
-- "forslaget gælder ikke længere" står STATUS_OVERRIDE_TICKS sekunder
self.statusOverrideTicks = (self.statusOverrideTicks or 0) - 1
if self.statusOverrideTicks <= 0 then self.statusOverride = nil end
self:setStatusText(self.statusOverride or "")
end
self.monthRebuildTicks = (self.monthRebuildTicks or 0) - 1 self.monthRebuildTicks = (self.monthRebuildTicks or 0) - 1
if self.monthRebuildTicks <= 0 then self:requestRebuild() end if self.monthRebuildTicks <= 0 then self:requestRebuild() end
self:refreshButtons() self:refreshButtons()
+18 -12
View File
@@ -577,27 +577,33 @@
<text name="spu_item_share" text="%d %% af varen"/> <text name="spu_item_share" text="%d %% af varen"/>
<text name="spu_tab_chains" text="Kæder"/> <text name="spu_tab_chains" text="Kæder"/>
<text name="spu_chain_empty" text="Ingen kæder: slå et produkt til i en fabrik"/> <text name="spu_chain_empty" text="Ingen kæder: slå et produkt til i en fabrik"/>
<text name="spu_chain_suggest" text="Forslag: F%d %s → %s (%.1f ha)"/> <text name="spu_chain_col_item" text="Vare"/>
<text name="spu_chain_approve" text="[Godkend]"/> <text name="spu_chain_col_source" text="Kilde"/>
<text name="spu_chain_col_perMonth" text="l/md"/>
<text name="spu_chain_col_status" text="Status"/>
<text name="spu_chain_col_stock" text="Lager"/>
<text name="spu_chain_approve" text="Forslag [Godkend]"/>
<text name="spu_chain_dismiss" text="[Afvis]"/> <text name="spu_chain_dismiss" text="[Afvis]"/>
<text name="spu_chain_auto" text="F%d skiftet til %s (fri til kæden)"/> <text name="spu_chain_auto" text="skiftet (fri mark)"/>
<text name="spu_chain_gone" text="Forslaget gælder ikke længere"/>
<text name="spu_chain_flowOk" text="flow kører"/> <text name="spu_chain_flowOk" text="flow kører"/>
<text name="spu_chain_flowMissing" text="flow mangler traktor/vogn"/> <text name="spu_chain_flowMissing" text="mangler traktor/vogn"/>
<text name="spu_chain_flowNone" text="intet flow endnu"/> <text name="spu_chain_flowNone" text="intet flow"/>
<text name="spu_chain_flowStopped" text="flow stoppet"/> <text name="spu_chain_flowStopped" text="flow stoppet"/>
<text name="spu_chain_bottleneck" text="flaskehals: %s mangler %s/md"/> <text name="spu_chain_bottleneck" text="flaskehals: -%s/md"/>
<text name="spu_chain_buy" text="%s skal købes (%s/md)"/> <text name="spu_chain_buy" text="skal købes: %s/md"/>
<text name="spu_chain_cycle" text="kredsløb i kæden"/> <text name="spu_chain_cycle" text="kredsløb"/>
<text name="spu_chain_fieldShort" text="mangler %s ha"/> <text name="spu_chain_fieldShort" text="mangler %s ha"/>
<text name="spu_chain_perMonth" text="%s/md"/> <text name="spu_chain_tight" text="lager &lt; 3 md"/>
<text name="spu_chain_noField" text="ingen mark"/> <text name="spu_chain_noField" text="ingen mark"/>
<text name="spu_chain_buySource" text="køb"/> <text name="spu_chain_buySource" text="køb"/>
<text name="spu_chain_pause" text="[Pause]"/> <text name="spu_chain_pause" text="[Pause]"/>
<text name="spu_chain_resume" text="[Genoptag]"/> <text name="spu_chain_resume" text="[Start]"/>
<text name="spu_chain_rootOk" text="dækket"/> <text name="spu_chain_rootOk" text="dækket"/>
<text name="spu_chain_rootError" text="mangler noget"/> <text name="spu_chain_rootError" text="mangel"/>
<text name="spu_chain_rootWaiting" text="venter på flow"/> <text name="spu_chain_rootWaiting" text="venter"/>
<text name="spu_chain_rootPaused" text="på pause"/> <text name="spu_chain_rootPaused" text="på pause"/>
<text name="spu_chain_rootSummary" text="%s · %d forslag"/>
<text name="spu_chain_free" text="Marker fri til kæden: %s"/> <text name="spu_chain_free" text="Marker fri til kæden: %s"/>
<text name="spu_chain_switches" text="Automatiske skift siden indlæsning: %d"/> <text name="spu_chain_switches" text="Automatiske skift siden indlæsning: %d"/>
<text name="spu_attention_chainSuggest" text="Kæde: F%d %s → %s"/> <text name="spu_attention_chainSuggest" text="Kæde: F%d %s → %s"/>
+18 -12
View File
@@ -577,27 +577,33 @@
<text name="spu_item_share" text="%d %% of the good"/> <text name="spu_item_share" text="%d %% of the good"/>
<text name="spu_tab_chains" text="Chains"/> <text name="spu_tab_chains" text="Chains"/>
<text name="spu_chain_empty" text="No chains: enable a product in a factory"/> <text name="spu_chain_empty" text="No chains: enable a product in a factory"/>
<text name="spu_chain_suggest" text="Suggestion: F%d %s → %s (%.1f ha)"/> <text name="spu_chain_col_item" text="Goods"/>
<text name="spu_chain_approve" text="[Approve]"/> <text name="spu_chain_col_source" text="Source"/>
<text name="spu_chain_dismiss" text="[Dismiss]"/> <text name="spu_chain_col_perMonth" text="l/mo"/>
<text name="spu_chain_auto" text="F%d switched to %s (free for the chain)"/> <text name="spu_chain_col_status" text="Status"/>
<text name="spu_chain_col_stock" text="Stock"/>
<text name="spu_chain_approve" text="Suggest [Approve]"/>
<text name="spu_chain_dismiss" text="[Reject]"/>
<text name="spu_chain_auto" text="switched (free field)"/>
<text name="spu_chain_gone" text="The suggestion no longer applies"/>
<text name="spu_chain_flowOk" text="flow running"/> <text name="spu_chain_flowOk" text="flow running"/>
<text name="spu_chain_flowMissing" text="flow lacks tractor/trailer"/> <text name="spu_chain_flowMissing" text="no tractor/trailer"/>
<text name="spu_chain_flowNone" text="no flow yet"/> <text name="spu_chain_flowNone" text="no flow"/>
<text name="spu_chain_flowStopped" text="flow stopped"/> <text name="spu_chain_flowStopped" text="flow stopped"/>
<text name="spu_chain_bottleneck" text="bottleneck: %s short %s/mo"/> <text name="spu_chain_bottleneck" text="bottleneck: -%s/mo"/>
<text name="spu_chain_buy" text="%s must be bought (%s/mo)"/> <text name="spu_chain_buy" text="must buy: %s/mo"/>
<text name="spu_chain_cycle" text="loop in the chain"/> <text name="spu_chain_cycle" text="loop"/>
<text name="spu_chain_fieldShort" text="short %s ha"/> <text name="spu_chain_fieldShort" text="short %s ha"/>
<text name="spu_chain_perMonth" text="%s/mo"/> <text name="spu_chain_tight" text="stock &lt; 3 mo"/>
<text name="spu_chain_noField" text="no field"/> <text name="spu_chain_noField" text="no field"/>
<text name="spu_chain_buySource" text="buy"/> <text name="spu_chain_buySource" text="buy"/>
<text name="spu_chain_pause" text="[Pause]"/> <text name="spu_chain_pause" text="[Pause]"/>
<text name="spu_chain_resume" text="[Resume]"/> <text name="spu_chain_resume" text="[Resume]"/>
<text name="spu_chain_rootOk" text="covered"/> <text name="spu_chain_rootOk" text="covered"/>
<text name="spu_chain_rootError" text="something is short"/> <text name="spu_chain_rootError" text="short"/>
<text name="spu_chain_rootWaiting" text="waiting for flow"/> <text name="spu_chain_rootWaiting" text="waiting"/>
<text name="spu_chain_rootPaused" text="paused"/> <text name="spu_chain_rootPaused" text="paused"/>
<text name="spu_chain_rootSummary" text="%s · %d sugg."/>
<text name="spu_chain_free" text="Fields free for the chain: %s"/> <text name="spu_chain_free" text="Fields free for the chain: %s"/>
<text name="spu_chain_switches" text="Automatic switches since load: %d"/> <text name="spu_chain_switches" text="Automatic switches since load: %d"/>
<text name="spu_attention_chainSuggest" text="Chain: F%d %s → %s"/> <text name="spu_attention_chainSuggest" text="Chain: F%d %s → %s"/>