feat(flows): Flows-fanen som farvet liste med Start/Stop pr. række; klik åbner redigeringsvinduet

This commit is contained in:
2026-09-29 17:43:52 +02:00
parent f068c910c7
commit 9435db929d
6 changed files with 143 additions and 52 deletions
+121 -37
View File
@@ -8,7 +8,8 @@ function SmartPickupFrame:initFlowButtons(i18n)
self.flowSetupButtonInfo = {inputAction = InputAction.MENU_EXTRA_1, text = i18n:getText("spu_guide_flow_open"), callback = function() self:onClickFlowSetup() end} self.flowSetupButtonInfo = {inputAction = InputAction.MENU_EXTRA_1, text = i18n:getText("spu_guide_flow_open"), callback = function() self:onClickFlowSetup() end}
self.flowNewButtonInfo = {inputAction = InputAction.MENU_EXTRA_2, text = i18n:getText("spu_button_new"), callback = function() self:onClickFlowNew() end} self.flowNewButtonInfo = {inputAction = InputAction.MENU_EXTRA_2, text = i18n:getText("spu_button_new"), callback = function() self:onClickFlowNew() end}
-- Flows-fanen: [Start/Stop] [Opsæt flow] [Nyt flow] [Slet]; omdøb ligger på resuméets Navn-linje -- Flows-fanen: [Start/Stop] [Opsæt flow] [Nyt flow] [Slet]; omdøb ligger på resuméets Navn-linje
self.flowButtons = {self.backButtonInfo, self.startButtonInfo, self.flowSetupButtonInfo, self.flowNewButtonInfo, self.deleteButtonInfo} -- v1.58: listen har Start/Stop pr. række; Opsæt/Slet ligger i redigeringsvinduet
self.flowButtons = {self.backButtonInfo, self.flowNewButtonInfo}
end end
-- ---- traktor-tekst (også til guidens traktorliste) ---- -- ---- traktor-tekst (også til guidens traktorliste) ----
@@ -160,33 +161,94 @@ end
-- Flows: det valgte flow øverst (indstillinger som "ret"-linjer + status), derunder en kort liste til at vælge -- Flows: det valgte flow øverst (indstillinger som "ret"-linjer + status), derunder en kort liste til at vælge
-- et andet flow. Tavlen med alle flows står i Oversigt (in-game 27/9: de to faner så ens ud). -- et andet flow. Tavlen med alle flows står i Oversigt (in-game 27/9: de to faner så ens ud).
function SmartPickupFrame:fillFlowTabRows(adEnv) function SmartPickupFrame:fillFlowTabRows(adEnv)
if self:hasGuideOn(SmartPickupFrame.TAB_FLOWS) and self:fillGuideRows(adEnv) then return end if not self.editPanelOpen and self:hasGuideOn(SmartPickupFrame.TAB_FLOWS) and self:fillGuideRows(adEnv) then return end
self.overviewRows = {} self.flowListRows = {}
local run = self:getRun() local i18n = self.i18n
if run == nil then if #ADRunsController.store.runs == 0 then
self:addTextRow(self.i18n:getText("spu_noRuns")) self:addTextRow(i18n:getText("spu_noRuns"))
return return
end end
self:fillFlowSummary(adEnv, run) self:addTextRow(i18n:getText("spu_flowList_hint"))
self:addTextRow(self.i18n:getText("spu_flow_pickOther"))
for _, group in ipairs(ADFlowView.boardGroups(ADRunsController.store.runs)) do for _, group in ipairs(ADFlowView.boardGroups(ADRunsController.store.runs)) do
for _, other in ipairs(group.runs) do self:addTextRow(i18n:getText(group.kind == "outbound" and "spu_board_outbound" or "spu_board_supply"))
if other.id ~= run.id then for _, run in ipairs(group.runs) do
local cells = self:flowCells(adEnv, other) local row = self:addRow(self.flowListPrefab, {kind = "flowList"})
local otherId = other.id local entry = {runId = run.id, row = row, dot = row:getDescendantByName("dot"),
self:addLinkRow(string.format(" %s — %s", tostring(other.name), tostring(cells[6] or "")), function(frame) cells = {row:getDescendantByName("c1"), row:getDescendantByName("c2"), row:getDescendantByName("c3"),
frame.selectedRunId = otherId row:getDescendantByName("c4"), row:getDescendantByName("c5")}}
frame.statusOverride = nil local runId = run.id
local actionCell = entry.cells[5]
self:makeRowClickable(row, function(frame)
-- klik på [Start]/[Stop]-feltet = handling (kun dette flow); ellers åbn redigeringsvinduet
if frame.lastClickX ~= nil and actionCell.absPosition ~= nil and frame.lastClickX >= actionCell.absPosition[1] then
frame:toggleRun(frame.getAdEnv(), runId)
frame:requestRebuild() frame:requestRebuild()
end) else
end frame.selectedRunId = runId
frame:openEditPanel("flow", runId)
end
end)
table.insert(self.flowListRows, entry)
end
end
self:refreshFlowListRows(adEnv)
end
-- Listens celler, prik og statusfarve (hvert sekund og efter opbygning).
function SmartPickupFrame:refreshFlowListRows(adEnv)
local unpackFn = table.unpack or unpack
for _, entry in ipairs(self.flowListRows or {}) do
local run = ADRuns.getRun(ADRunsController.store, entry.runId)
if run ~= nil then
local cells = self:flowCells(adEnv, run)
local color = ADFlowView.LEVEL_COLORS[self:flowLevel(adEnv, run)]
local canStop = ADRunsController.getRunStatus(adEnv, run).canStop
entry.cells[1]:setText(tostring(run.name))
entry.cells[2]:setText(tostring(cells[2]))
entry.cells[3]:setText(tostring(cells[5]))
entry.cells[4]:setText(tostring(cells[6]))
entry.cells[5]:setText(self.i18n:getText(canStop and "spu_flowList_stop" or "spu_flowList_start"))
if entry.dot ~= nil and entry.dot.setImageColor ~= nil then entry.dot:setImageColor(nil, unpackFn(color)) end
if entry.cells[4].setTextColor ~= nil then entry.cells[4]:setTextColor(unpackFn(color)) end
end end
end end
end end
-- Start/Stop for ét flow (rækkens felt og vinduets knap). Husker om sidste start blev helt afvist (rød).
function SmartPickupFrame:toggleRun(adEnv, runId)
local run = ADRuns.getRun(ADRunsController.store, runId)
if adEnv == nil or run == nil then return end
self.lastStartFailed = self.lastStartFailed or {}
if ADRunsController.getRunStatus(adEnv, run).canStop then
ADRunsController.stopRun(adEnv, run.id)
self.lastStartFailed[run.id] = nil
self.statusOverride = nil
else
local started, skipped, reasons = self:summarizeStartResults(ADRunsController.startRun(adEnv, run.id))
self.lastStartFailed[run.id] = started == 0 and skipped > 0
local text = string.format(self.i18n:getText("spu_status_started"), started, skipped)
self:showOverride(text, reasons)
-- statuslinjen kan ligge uden for skærmen: sprunget over vises også som besked i højre side
if skipped > 0 and ADFieldJobs ~= nil and ADFieldJobs.notify ~= nil then
ADFieldJobs.notify(string.format("%s: %s — %s", tostring(run.name), text, table.concat(reasons, "; ")), started == 0)
end
end
ADRunsController.save()
end
function SmartPickupFrame:flowLevel(adEnv, run)
local row = ADRunsController.getFlowRow(adEnv, run)
local status = ADRunsController.getRunStatus(adEnv, run)
return ADFlowView.flowStatus({running = row.running, total = row.total, stopped = run.stopped == true,
canStop = status.canStop == true, warning = row.warning, reason = row.reason,
lastStartFailed = (self.lastStartFailed or {})[run.id] == true})
end
function SmartPickupFrame:fillFlowSummary(adEnv, run) function SmartPickupFrame:fillFlowSummary(adEnv, run)
local i18n = self.i18n local i18n = self.i18n
self:addTextRow(string.format(i18n:getText("spu_guide_flow_section"), tostring(run.name))) if not self.editPanelOpen then
self:addTextRow(string.format(i18n:getText("spu_guide_flow_section"), tostring(run.name)))
end
self:addTextRow(" " .. i18n:getText("spu_guide_sum_hint")) self:addTextRow(" " .. i18n:getText("spu_guide_sum_hint"))
local guide = ADFlowGuide.open(run, self:flowGuideContext(adEnv, run)) local guide = ADFlowGuide.open(run, self:flowGuideContext(adEnv, run))
local getText = function(key) return i18n:getText(key) end local getText = function(key) return i18n:getText(key) end
@@ -199,31 +261,53 @@ function SmartPickupFrame:fillFlowSummary(adEnv, run)
end end
local cells = self:flowCells(adEnv, run) local cells = self:flowCells(adEnv, run)
self:addTextRow(" " .. string.format(i18n:getText("spu_field_statusFull"), tostring(cells[6]))) self:addTextRow(" " .. string.format(i18n:getText("spu_field_statusFull"), tostring(cells[6])))
self:addLinkRow(" " .. i18n:getText("spu_guide_flow_open"), function(frame)
frame.selectedRunId = runId
frame:onClickFlowSetup()
end)
end end
-- Redigeringsvinduet for et flow (gui/SmartPickupEditPanel.lua).
SmartPickupFrame.EDIT_PANEL_KINDS = SmartPickupFrame.EDIT_PANEL_KINDS or {}
SmartPickupFrame.EDIT_PANEL_KINDS.flow = {
canDelete = true,
exists = function(frame, id) return ADRuns.getRun(ADRunsController.store, id) ~= nil end,
title = function(frame, id)
local run = ADRuns.getRun(ADRunsController.store, id)
return string.format(frame.i18n:getText("spu_panel_flowTitle"), tostring(run ~= nil and run.name or "?"))
end,
fill = function(frame, adEnv, id)
local run = ADRuns.getRun(ADRunsController.store, id)
if run ~= nil then frame:fillFlowSummary(adEnv, run) end
end,
status = function(frame, adEnv, id)
local run = ADRuns.getRun(ADRunsController.store, id)
return {text = tostring(frame:flowCells(adEnv, run)[6]), level = frame:flowLevel(adEnv, run)}
end,
startStopText = function(frame, adEnv, id)
local run = ADRuns.getRun(ADRunsController.store, id)
return frame.i18n:getText(ADRunsController.getRunStatus(adEnv, run).canStop and "spu_button_stop" or "spu_button_start")
end,
startStop = function(frame, id) frame:toggleRun(frame.getAdEnv(), id) end,
delete = function(frame, id)
frame.selectedRunId = id
frame:onClickDelete()
end,
}
-- Hvert sekund: tavlens celler, statuslinje og knapper. -- Hvert sekund: tavlens celler, statuslinje og knapper.
function SmartPickupFrame:refreshFlowTab(adEnv) function SmartPickupFrame:refreshFlowTab(adEnv)
local run = self:getRun() if not self.editPanelOpen and self:hasGuideOn(SmartPickupFrame.TAB_FLOWS) then
local isRunning = false
if self:hasGuideOn(SmartPickupFrame.TAB_FLOWS) then
self:setStatusText(self.statusOverride or "") self:setStatusText(self.statusOverride or "")
else return
self:refreshOverview(adEnv)
if run ~= nil then
local status = ADRunsController.getRunStatus(adEnv, run)
isRunning = status.canStop == true
local text = status.running > 0 and string.format(self.i18n:getText("spu_status_running"), status.running, status.total)
or string.format(self.i18n:getText("spu_status_stopped"), status.total)
self:setStatusText(self.statusOverride or text)
else
-- tavlen siger allerede "Ingen flows"
self:setStatusText(self.statusOverride or "")
end
end end
self.startButtonInfo.text = self.i18n:getText(isRunning and "spu_button_stop" or "spu_button_start") self:refreshFlowListRows(adEnv)
self.startButtonInfo.disabled = run == nil local running, total = 0, 0
self.flowSetupButtonInfo.disabled = run == nil for _, run in ipairs(ADRunsController.store.runs) do
self.deleteButtonInfo.disabled = run == nil total = total + 1
if ADRunsController.getRunStatus(adEnv, run).running > 0 then running = running + 1 end
end
self:setStatusText(self.statusOverride or string.format(self.i18n:getText("spu_flowList_status"), running, total))
end end
-- ---- hooks til den fælles guide-skærm ---- -- ---- hooks til den fælles guide-skærm ----
@@ -85,6 +85,7 @@ function SmartPickupFrame:makeRowClickable(row, action)
local blockedByPanel = frame.editPanelOpen and layout == frame.boxLayout local blockedByPanel = frame.editPanelOpen and layout == frame.boxLayout
if not used and not eventUsed and not blockedByPanel and isUp and button == MOUSE_LEFT and (element.getIsVisible == nil or element:getIsVisible()) if not used and not eventUsed and not blockedByPanel and isUp and button == MOUSE_LEFT and (element.getIsVisible == nil or element:getIsVisible())
and isInside(element, posX, posY) and isInside(layout, posX, posY) then and isInside(element, posX, posY) and isInside(layout, posX, posY) then
frame.lastClickX = posX
frame.pendingClick = action frame.pendingClick = action
return true return true
end end
+2 -15
View File
@@ -86,7 +86,7 @@ end
-- Prefabs skal ud af layoutet før de klones, ellers vises de selv som rækker. -- Prefabs skal ud af layoutet før de klones, ellers vises de selv som rækker.
function SmartPickupFrame:initialize() function SmartPickupFrame:initialize()
for _, name in ipairs({"ingredientPrefab", "vehiclePrefab", "textPrefab", "productPrefab", "statPrefab", "gridPrefab", "flowPrefab", "fieldPrefab", "kpiPrefab", "alertPrefab", "itemPrefab", "subPrefab"}) do for _, name in ipairs({"ingredientPrefab", "vehiclePrefab", "textPrefab", "productPrefab", "statPrefab", "gridPrefab", "flowPrefab", "flowListPrefab", "fieldPrefab", "kpiPrefab", "alertPrefab", "itemPrefab", "subPrefab"}) do
local prefab = self[name] local prefab = self[name]
if prefab ~= nil then if prefab ~= nil then
prefab:unlinkElement() prefab:unlinkElement()
@@ -785,20 +785,7 @@ function SmartPickupFrame:onClickStartStop()
self:safe(function(frame) self:safe(function(frame)
local adEnv, run = frame.getAdEnv(), frame:getRun() local adEnv, run = frame.getAdEnv(), frame:getRun()
if adEnv == nil or run == nil then return end if adEnv == nil or run == nil then return end
if ADRunsController.getRunStatus(adEnv, run).canStop then frame:toggleRun(adEnv, run.id)
ADRunsController.stopRun(adEnv, run.id)
frame.statusOverride = nil
else
local started, skipped, reasons = frame:summarizeStartResults(ADRunsController.startRun(adEnv, run.id))
local text = string.format(frame.i18n:getText("spu_status_started"), started, skipped)
frame:showOverride(text, reasons)
-- statuslinjen ligger nederst i den rullende liste: sprunget over vises også som besked i højre side
-- (in-game 29/9: Start på Milk Store 'gjorde ingenting' — begge traktorer afvist, grunden sås ikke)
if skipped > 0 and ADFieldJobs ~= nil and ADFieldJobs.notify ~= nil then
ADFieldJobs.notify(string.format("%s: %s — %s", tostring(run.name), text, table.concat(reasons, "; ")), started == 0)
end
end
ADRunsController.save()
frame:requestRebuild() frame:requestRebuild()
end) end)
end end
@@ -89,6 +89,15 @@
<Text profile="spuCell" name="c5" position="620px 0px" size="50px 40px" textMaxWidth="42px"/> <Text profile="spuCell" name="c5" position="620px 0px" size="50px 40px" textMaxWidth="42px"/>
<Text profile="spuCell" name="c6" position="670px 0px" size="140px 40px" textMaxWidth="132px"/> <Text profile="spuCell" name="c6" position="670px 0px" size="140px 40px" textMaxWidth="132px"/>
</Bitmap> </Bitmap>
<!-- Flows-listen (v1.58): ● · navn · varer · kører/i alt · status · [Start]/[Stop]. ~835 px. -->
<Bitmap profile="fs25_multiTextOptionContainer" id="flowListPrefab">
<Bitmap profile="spuDot" name="dot" position="20px 0px"/>
<Text profile="spuCellBold" name="c1" position="38px 0px" size="200px 40px" textMaxWidth="192px"/>
<Text profile="spuCell" name="c2" position="238px 0px" size="170px 40px" textMaxWidth="162px"/>
<Text profile="spuCell" name="c3" position="408px 0px" size="70px 40px" textMaxWidth="62px"/>
<Text profile="spuCell" name="c4" position="478px 0px" size="252px 40px" textMaxWidth="244px"/>
<Text profile="spuCellBold" name="c5" position="730px 0px" size="100px 40px" textMaxWidth="92px"/>
</Bitmap>
<!-- Marker: én række pr. mark (mark · afgrøde · plan · auto/man. · status · maskiner). Rækken er ~815 px bred. --> <!-- Marker: én række pr. mark (mark · afgrøde · plan · auto/man. · status · maskiner). Rækken er ~815 px bred. -->
<Bitmap profile="fs25_multiTextOptionContainer" id="fieldPrefab"> <Bitmap profile="fs25_multiTextOptionContainer" id="fieldPrefab">
<Text profile="spuCellBold" name="c1" position="20px 0px" size="100px 40px" textMaxWidth="92px"/> <Text profile="spuCellBold" name="c1" position="20px 0px" size="100px 40px" textMaxWidth="92px"/>
+5
View File
@@ -254,6 +254,11 @@
<text name="spu_button_delete" text="Slet flow"/> <text name="spu_button_delete" text="Slet flow"/>
<text name="spu_panel_close" text="Luk"/> <text name="spu_panel_close" text="Luk"/>
<text name="spu_panel_gone" text="Posten findes ikke længere — vinduet er lukket"/> <text name="spu_panel_gone" text="Posten findes ikke længere — vinduet er lukket"/>
<text name="spu_flowList_hint" text="Klik på et flow for at rette det. [Start]/[Stop] yderst til højre styrer flowet direkte."/>
<text name="spu_flowList_start" text="[Start]"/>
<text name="spu_flowList_stop" text="[Stop]"/>
<text name="spu_flowList_status" text="Flows i gang: %d af %d"/>
<text name="spu_panel_flowTitle" text="Rediger flow: %s"/>
<text name="spu_newRunName" text="Flow %d"/> <text name="spu_newRunName" text="Flow %d"/>
<text name="spu_renameTitle" text="Navn på flow"/> <text name="spu_renameTitle" text="Navn på flow"/>
<text name="spu_deleteConfirm" text="Slet run '%s'? Dets kørende traktorer stoppes. Kan ikke fortrydes."/> <text name="spu_deleteConfirm" text="Slet run '%s'? Dets kørende traktorer stoppes. Kan ikke fortrydes."/>
+5
View File
@@ -254,6 +254,11 @@
<text name="spu_button_delete" text="Delete flow"/> <text name="spu_button_delete" text="Delete flow"/>
<text name="spu_panel_close" text="Close"/> <text name="spu_panel_close" text="Close"/>
<text name="spu_panel_gone" text="That item no longer exists — window closed"/> <text name="spu_panel_gone" text="That item no longer exists — window closed"/>
<text name="spu_flowList_hint" text="Click a flow to edit it. [Start]/[Stop] on the far right controls the flow directly."/>
<text name="spu_flowList_start" text="[Start]"/>
<text name="spu_flowList_stop" text="[Stop]"/>
<text name="spu_flowList_status" text="Flows running: %d of %d"/>
<text name="spu_panel_flowTitle" text="Edit flow: %s"/>
<text name="spu_newRunName" text="Flow %d"/> <text name="spu_newRunName" text="Flow %d"/>
<text name="spu_renameTitle" text="Flow name"/> <text name="spu_renameTitle" text="Flow name"/>
<text name="spu_deleteConfirm" text="Delete run '%s'? Its running tractors will be stopped. This cannot be undone."/> <text name="spu_deleteConfirm" text="Delete run '%s'? Its running tractors will be stopped. This cannot be undone."/>