fix: Smart Pickup-fane — ingen callbacks under opbygning, fokus-oprydning, pcall på alle klik, navn/ikon/status

This commit is contained in:
2026-09-22 23:03:49 +02:00
parent 254274f926
commit 0176921603
2 changed files with 115 additions and 62 deletions
+6 -3
View File
@@ -677,7 +677,7 @@ local function restoreStoppedVehicles()
end
-- Indsætter fanen i ESC-menuen foran Statistik/Priser (mønster fra FS25_FarmOperationsDashboard).
local function insertInGameMenuPage(frame, pageName, iconFile, uvs, position, predicate)
local function insertInGameMenuPage(frame, pageName, iconFile, iconUVs, position, predicate)
local inGameMenu = g_gui ~= nil and g_gui.screenControllers ~= nil and g_gui.screenControllers[InGameMenu] or nil
if inGameMenu == nil or inGameMenu.pagingElement == nil then
return false
@@ -717,7 +717,7 @@ local function insertInGameMenuPage(frame, pageName, iconFile, uvs, position, pr
inGameMenu.pagingElement:updateAbsolutePosition()
inGameMenu.pagingElement:updatePageMapping()
inGameMenu:registerPage(frame, position, predicate)
inGameMenu:addPageTab(frame, iconFile, GuiUtils.getUVs(uvs))
inGameMenu:addPageTab(frame, iconFile, iconUVs)
inGameMenu:rebuildTabList()
return true
end
@@ -730,7 +730,10 @@ local function loadGui()
local frame = SmartPickupFrame.new(g_i18n, getAutoDriveEnv)
g_gui:loadGui(ADSmartPickup.dir .. "gui/SmartPickupFrame.xml", "smartPickupFrame", frame, true)
local iconFile = Utils.getFilename("icon.dds", ADSmartPickup.dir)
if not insertInGameMenuPage(frame, "smartPickupFrame", iconFile, {0, 0, 512, 512}, 2, function() return g_currentMission ~= nil end) then
-- icon.dds er 512×512. Uden referencestørrelsen normaliserer getUVs mod 1024×1024,
-- og fanen ville kun vise ikonets øverste venstre fjerdedel.
local iconUVs = GuiUtils.getUVs({0, 0, 512, 512}, {512, 512})
if not insertInGameMenuPage(frame, "smartPickupFrame", iconFile, iconUVs, 2, function() return g_currentMission ~= nil end) then
Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke indsætte fanen i ESC-menuen")
return
end
+79 -29
View File
@@ -26,7 +26,9 @@ function SmartPickupFrame.new(i18n, getAdEnv)
self.newButtonInfo = {inputAction = InputAction.MENU_EXTRA_1, text = i18n:getText("spu_button_new"), callback = function() self:onClickNew() end}
self.renameButtonInfo = {inputAction = InputAction.MENU_EXTRA_2, text = i18n:getText("spu_button_rename"), callback = function() self:onClickRename() end}
self.deleteButtonInfo = {inputAction = InputAction.MENU_CANCEL, text = i18n:getText("spu_button_delete"), callback = function() self:onClickDelete() end}
self:setMenuButtonInfo({self.backButtonInfo, self.startButtonInfo, self.newButtonInfo, self.renameButtonInfo, self.deleteButtonInfo})
-- Egen reference til listen: fallback'en i refreshButtons skal kunne sende den igen.
self.menuButtonInfo = {self.backButtonInfo, self.startButtonInfo, self.newButtonInfo, self.renameButtonInfo, self.deleteButtonInfo}
self:setMenuButtonInfo(self.menuButtonInfo)
return self
end
@@ -56,11 +58,20 @@ function SmartPickupFrame:initialize()
self.loopsOption:setTexts(loopTexts)
end
-- rebuild sletter de rækker der kan have fokus (fx vare-rækkerne når Varer skifter til
-- Auto); uden dette står fokus på et slettet element og tastatur/controller er død.
function SmartPickupFrame:focusRunOption()
self:safe(function(frame)
if frame.runOption ~= nil then
FocusManager:setFocus(frame.runOption)
end
end)
end
function SmartPickupFrame:onFrameOpen()
SmartPickupFrame:superClass().onFrameOpen(self)
self.statusOverride = nil
self:safe(self.rebuild)
FocusManager:setFocus(self.runOption)
end
function SmartPickupFrame:onFrameClose()
@@ -94,8 +105,11 @@ end
-- ---- opbygning ----
-- addRow melder hver klonet række ind i FocusManager; uden afmelding her ville
-- FocusManager beholde slettede rækker og fokus-navigationen gå i stå.
function SmartPickupFrame:clearRows()
for _, row in ipairs(self.clonedRows) do
FocusManager:removeElement(row)
row:delete()
end
self.clonedRows, self.rowData = {}, {}
@@ -128,10 +142,10 @@ function SmartPickupFrame:rebuild()
end
if hasRun then
self:fillTargetOption(adEnv, run)
self.ingredientModeOption:setState(run.ingredientMode == "manual" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, true)
self.ingredientModeOption:setState(run.ingredientMode == "manual" and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
self:fillIngredientRows(adEnv, run)
self:fillWaitPoolOption(adEnv, run)
self.loopsOption:setState(run.loops + 1, true)
self.loopsOption:setState(run.loops + 1, false)
self:fillVehicleRows(adEnv, run)
end
self:fillPoolRows(adEnv)
@@ -139,6 +153,7 @@ function SmartPickupFrame:rebuild()
box:invalidateLayout()
end
self:refreshLive()
self:focusRunOption()
end
function SmartPickupFrame:fillRunOption(adEnv)
@@ -156,7 +171,7 @@ function SmartPickupFrame:fillRunOption(adEnv)
texts = {self.i18n:getText("spu_noRuns")}
end
self.runOption:setTexts(texts)
self.runOption:setState(index, true)
self.runOption:setState(index, false)
self.runOption:setDisabled(#self.runIds == 0)
end
@@ -171,7 +186,7 @@ function SmartPickupFrame:fillTargetOption(adEnv, run)
end
end
self.targetOption:setTexts(texts)
self.targetOption:setState(index, true)
self.targetOption:setState(index, false)
end
function SmartPickupFrame:fillIngredientRows(adEnv, run)
@@ -185,7 +200,7 @@ function SmartPickupFrame:fillIngredientRows(adEnv, run)
for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, run)) do
local row = self:addRow(self.ingredientPrefab, self.ingredientBox, {kind = "ingredient", name = fillType.name})
row:getDescendantByName("title"):setText(tostring(fillType.title))
row:getDescendantByName("option"):setState(chosen[fillType.name] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, true)
row:getDescendantByName("option"):setState(chosen[fillType.name] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
end
end
@@ -200,7 +215,7 @@ function SmartPickupFrame:fillWaitPoolOption(adEnv, run)
end
end
self.waitPoolOption:setTexts(texts)
self.waitPoolOption:setState(index, true)
self.waitPoolOption:setState(index, false)
end
function SmartPickupFrame:fillVehicleRows(adEnv, run)
@@ -212,7 +227,7 @@ function SmartPickupFrame:fillVehicleRows(adEnv, run)
local id = ADRunsController.getVehicleId(vehicle)
local row = self:addRow(self.vehiclePrefab, self.vehicleBox, {kind = "vehicle", vehicle = vehicle, row = nil})
self.rowData[row:getDescendantByName("option")].row = row
row:getDescendantByName("option"):setState(inRun[id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, true)
row:getDescendantByName("option"):setState(inRun[id] and SmartPickupFrame.STATE_ON or SmartPickupFrame.STATE_OFF, false)
row:getDescendantByName("title"):setText(self:describeVehicle(adEnv, run, vehicle, inRun[id]))
end
end
@@ -254,6 +269,21 @@ function SmartPickupFrame:fillPoolRows(adEnv)
end
end
-- BinaryOption har :getState() i FS25, men feltet .state i ældre GUI-versioner.
function SmartPickupFrame.getOptionState(option)
return option.getState ~= nil and option:getState() or option.state
end
-- setMenuButtonInfoDirty findes i FS22/25's TabbedMenuFrameElement; mangler den,
-- sendes listen igen. Kaldes hvert sekund, så den må aldrig kaste.
function SmartPickupFrame:refreshButtons()
if self.setMenuButtonInfoDirty ~= nil then
self:setMenuButtonInfoDirty()
else
self:setMenuButtonInfo(self.menuButtonInfo)
end
end
-- Status + Start/Stop-knappens tekst, kaldes hvert sekund.
function SmartPickupFrame:refreshLive()
local adEnv = self.getAdEnv()
@@ -271,17 +301,20 @@ function SmartPickupFrame:refreshLive()
self.statusText:setText(self.statusOverride or text)
for option, data in pairs(self.rowData) do
if data.kind == "vehicle" and data.row ~= nil then
data.row:getDescendantByName("title"):setText(self:describeVehicle(adEnv, run, data.vehicle, option:getState() == SmartPickupFrame.STATE_ON))
data.row:getDescendantByName("title"):setText(self:describeVehicle(adEnv, run, data.vehicle, SmartPickupFrame.getOptionState(option) == SmartPickupFrame.STATE_ON))
end
end
else
self.statusText:setText(self.statusOverride or self.i18n:getText("spu_noRuns"))
end
-- Engangsbesked ("Startet 2, sprunget over 1", "flyttet fra ..."): vist nu, ryddet her,
-- så næste opdatering om et sekund viser den levende status igen.
self.statusOverride = nil
self.startButtonInfo.text = self.i18n:getText(isRunning and "spu_button_stop" or "spu_button_start")
self.startButtonInfo.disabled = run == nil
self.renameButtonInfo.disabled = run == nil
self.deleteButtonInfo.disabled = run == nil
self:setMenuButtonInfoDirty()
self:refreshButtons()
end
-- ---- callbacks fra XML (state = 1-baseret indeks; element = det klikkede element) ----
@@ -293,22 +326,27 @@ function SmartPickupFrame:onClickRun(state)
end
function SmartPickupFrame:onClickTarget(state)
local run = self:getRun()
self:safe(function(frame)
local run = frame:getRun()
if run == nil then return end
-- targetIds[1] er false = "(vælg mål)"; modellen tolker false som "ryd målet"
ADRunsController.updateRun(run.id, {targetWayPointId = self.targetIds[state] or false})
self:safe(self.rebuild)
ADRunsController.updateRun(run.id, {targetWayPointId = frame.targetIds[state] or false})
frame:rebuild()
end)
end
function SmartPickupFrame:onClickIngredientMode(state)
local run = self:getRun()
self:safe(function(frame)
local run = frame:getRun()
if run == nil then return end
ADRunsController.updateRun(run.id, {ingredientMode = state == SmartPickupFrame.STATE_ON and "manual" or "auto"})
self:safe(self.rebuild)
frame:rebuild()
end)
end
function SmartPickupFrame:onClickIngredient(state, element)
local run, data = self:getRun(), self.rowData[element]
self:safe(function(frame)
local run, data = frame:getRun(), frame.rowData[element]
if run == nil or data == nil then return end
local names = {}
for _, name in ipairs(run.ingredients) do
@@ -318,33 +356,40 @@ function SmartPickupFrame:onClickIngredient(state, element)
table.insert(names, data.name)
end
ADRunsController.updateRun(run.id, {ingredients = names})
end)
end
function SmartPickupFrame:onClickWaitPool(state)
local run = self:getRun()
self:safe(function(frame)
local run = frame:getRun()
if run == nil then return end
ADRunsController.updateRun(run.id, {waitPoolGroup = self.poolGroups[state] or ""})
ADRunsController.updateRun(run.id, {waitPoolGroup = frame.poolGroups[state] or ""})
end)
end
function SmartPickupFrame:onClickLoops(state)
local run = self:getRun()
self:safe(function(frame)
local run = frame:getRun()
if run == nil then return end
ADRunsController.updateRun(run.id, {loops = state - 1})
end)
end
function SmartPickupFrame:onClickVehicle(state, element)
local run, data = self:getRun(), self.rowData[element]
self:safe(function(frame)
local run, data = frame:getRun(), frame.rowData[element]
if run == nil or data == nil then return end
if state == SmartPickupFrame.STATE_ON then
local previousRunId = ADRunsController.assignVehicle(run.id, data.vehicle)
if previousRunId ~= nil then
local previous = ADRuns.getRun(ADRunsController.store, previousRunId)
self.statusOverride = string.format(self.i18n:getText("spu_movedFrom"), previous ~= nil and previous.name or tostring(previousRunId))
frame.statusOverride = string.format(frame.i18n:getText("spu_movedFrom"), previous ~= nil and previous.name or tostring(previousRunId))
end
else
ADRunsController.unassignVehicle(data.vehicle)
end
self:safe(self.refreshLive)
frame:refreshLive()
end)
end
-- ---- knapper i bundlinjen ----
@@ -370,7 +415,8 @@ end
function SmartPickupFrame:onClickNew()
self:safe(function(frame)
local run = ADRunsController.createRun(string.format(frame.i18n:getText("spu_newRunName"), ADRunsController.store.nextId - 1))
-- nextId er id'et det NYE run får; navnet skal matche det, ikke det forrige.
local run = ADRunsController.createRun(string.format(frame.i18n:getText("spu_newRunName"), ADRunsController.store.nextId))
frame.selectedRunId = run.id
frame.statusOverride = nil
frame:rebuild()
@@ -384,11 +430,13 @@ function SmartPickupFrame:onClickRename()
end
function SmartPickupFrame:onRenameResult(value, clickOk)
local run = self:getRun()
self:safe(function(frame)
local run = frame:getRun()
if clickOk and run ~= nil and value ~= nil and value:match("%S") then
ADRunsController.updateRun(run.id, {name = value})
self:safe(self.rebuild)
frame:rebuild()
end
end)
end
function SmartPickupFrame:onClickDelete()
@@ -398,10 +446,12 @@ function SmartPickupFrame:onClickDelete()
end
function SmartPickupFrame:onDeleteResult(yes)
local run = self:getRun()
self:safe(function(frame)
local run = frame:getRun()
if yes and run ~= nil then
ADRunsController.deleteRun(run.id)
self.selectedRunId = nil
self:safe(self.rebuild)
frame.selectedRunId = nil
frame:rebuild()
end
end)
end