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 end
-- Indsætter fanen i ESC-menuen foran Statistik/Priser (mønster fra FS25_FarmOperationsDashboard). -- 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 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 if inGameMenu == nil or inGameMenu.pagingElement == nil then
return false return false
@@ -717,7 +717,7 @@ local function insertInGameMenuPage(frame, pageName, iconFile, uvs, position, pr
inGameMenu.pagingElement:updateAbsolutePosition() inGameMenu.pagingElement:updateAbsolutePosition()
inGameMenu.pagingElement:updatePageMapping() inGameMenu.pagingElement:updatePageMapping()
inGameMenu:registerPage(frame, position, predicate) inGameMenu:registerPage(frame, position, predicate)
inGameMenu:addPageTab(frame, iconFile, GuiUtils.getUVs(uvs)) inGameMenu:addPageTab(frame, iconFile, iconUVs)
inGameMenu:rebuildTabList() inGameMenu:rebuildTabList()
return true return true
end end
@@ -730,7 +730,10 @@ local function loadGui()
local frame = SmartPickupFrame.new(g_i18n, getAutoDriveEnv) local frame = SmartPickupFrame.new(g_i18n, getAutoDriveEnv)
g_gui:loadGui(ADSmartPickup.dir .. "gui/SmartPickupFrame.xml", "smartPickupFrame", frame, true) g_gui:loadGui(ADSmartPickup.dir .. "gui/SmartPickupFrame.xml", "smartPickupFrame", frame, true)
local iconFile = Utils.getFilename("icon.dds", ADSmartPickup.dir) 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") Logging.warning(ADSmartPickup.LOG_PREFIX .. "kunne ikke indsætte fanen i ESC-menuen")
return return
end end
+109 -59
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.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.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.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 return self
end end
@@ -56,11 +58,20 @@ function SmartPickupFrame:initialize()
self.loopsOption:setTexts(loopTexts) self.loopsOption:setTexts(loopTexts)
end 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() function SmartPickupFrame:onFrameOpen()
SmartPickupFrame:superClass().onFrameOpen(self) SmartPickupFrame:superClass().onFrameOpen(self)
self.statusOverride = nil self.statusOverride = nil
self:safe(self.rebuild) self:safe(self.rebuild)
FocusManager:setFocus(self.runOption)
end end
function SmartPickupFrame:onFrameClose() function SmartPickupFrame:onFrameClose()
@@ -94,8 +105,11 @@ end
-- ---- opbygning ---- -- ---- 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() function SmartPickupFrame:clearRows()
for _, row in ipairs(self.clonedRows) do for _, row in ipairs(self.clonedRows) do
FocusManager:removeElement(row)
row:delete() row:delete()
end end
self.clonedRows, self.rowData = {}, {} self.clonedRows, self.rowData = {}, {}
@@ -128,10 +142,10 @@ function SmartPickupFrame:rebuild()
end end
if hasRun then if hasRun then
self:fillTargetOption(adEnv, run) 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:fillIngredientRows(adEnv, run)
self:fillWaitPoolOption(adEnv, run) self:fillWaitPoolOption(adEnv, run)
self.loopsOption:setState(run.loops + 1, true) self.loopsOption:setState(run.loops + 1, false)
self:fillVehicleRows(adEnv, run) self:fillVehicleRows(adEnv, run)
end end
self:fillPoolRows(adEnv) self:fillPoolRows(adEnv)
@@ -139,6 +153,7 @@ function SmartPickupFrame:rebuild()
box:invalidateLayout() box:invalidateLayout()
end end
self:refreshLive() self:refreshLive()
self:focusRunOption()
end end
function SmartPickupFrame:fillRunOption(adEnv) function SmartPickupFrame:fillRunOption(adEnv)
@@ -156,7 +171,7 @@ function SmartPickupFrame:fillRunOption(adEnv)
texts = {self.i18n:getText("spu_noRuns")} texts = {self.i18n:getText("spu_noRuns")}
end end
self.runOption:setTexts(texts) self.runOption:setTexts(texts)
self.runOption:setState(index, true) self.runOption:setState(index, false)
self.runOption:setDisabled(#self.runIds == 0) self.runOption:setDisabled(#self.runIds == 0)
end end
@@ -171,7 +186,7 @@ function SmartPickupFrame:fillTargetOption(adEnv, run)
end end
end end
self.targetOption:setTexts(texts) self.targetOption:setTexts(texts)
self.targetOption:setState(index, true) self.targetOption:setState(index, false)
end end
function SmartPickupFrame:fillIngredientRows(adEnv, run) function SmartPickupFrame:fillIngredientRows(adEnv, run)
@@ -185,7 +200,7 @@ function SmartPickupFrame:fillIngredientRows(adEnv, run)
for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, run)) do for _, fillType in ipairs(ADRunsController.getTargetAcceptedFillTypes(adEnv, run)) do
local row = self:addRow(self.ingredientPrefab, self.ingredientBox, {kind = "ingredient", name = fillType.name}) local row = self:addRow(self.ingredientPrefab, self.ingredientBox, {kind = "ingredient", name = fillType.name})
row:getDescendantByName("title"):setText(tostring(fillType.title)) 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
end end
@@ -200,7 +215,7 @@ function SmartPickupFrame:fillWaitPoolOption(adEnv, run)
end end
end end
self.waitPoolOption:setTexts(texts) self.waitPoolOption:setTexts(texts)
self.waitPoolOption:setState(index, true) self.waitPoolOption:setState(index, false)
end end
function SmartPickupFrame:fillVehicleRows(adEnv, run) function SmartPickupFrame:fillVehicleRows(adEnv, run)
@@ -212,7 +227,7 @@ function SmartPickupFrame:fillVehicleRows(adEnv, run)
local id = ADRunsController.getVehicleId(vehicle) local id = ADRunsController.getVehicleId(vehicle)
local row = self:addRow(self.vehiclePrefab, self.vehicleBox, {kind = "vehicle", vehicle = vehicle, row = nil}) local row = self:addRow(self.vehiclePrefab, self.vehicleBox, {kind = "vehicle", vehicle = vehicle, row = nil})
self.rowData[row:getDescendantByName("option")].row = row 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])) row:getDescendantByName("title"):setText(self:describeVehicle(adEnv, run, vehicle, inRun[id]))
end end
end end
@@ -254,6 +269,21 @@ function SmartPickupFrame:fillPoolRows(adEnv)
end end
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. -- Status + Start/Stop-knappens tekst, kaldes hvert sekund.
function SmartPickupFrame:refreshLive() function SmartPickupFrame:refreshLive()
local adEnv = self.getAdEnv() local adEnv = self.getAdEnv()
@@ -271,17 +301,20 @@ function SmartPickupFrame:refreshLive()
self.statusText:setText(self.statusOverride or text) self.statusText:setText(self.statusOverride or text)
for option, data in pairs(self.rowData) do for option, data in pairs(self.rowData) do
if data.kind == "vehicle" and data.row ~= nil then 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
end end
else else
self.statusText:setText(self.statusOverride or self.i18n:getText("spu_noRuns")) self.statusText:setText(self.statusOverride or self.i18n:getText("spu_noRuns"))
end 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.text = self.i18n:getText(isRunning and "spu_button_stop" or "spu_button_start")
self.startButtonInfo.disabled = run == nil self.startButtonInfo.disabled = run == nil
self.renameButtonInfo.disabled = run == nil self.renameButtonInfo.disabled = run == nil
self.deleteButtonInfo.disabled = run == nil self.deleteButtonInfo.disabled = run == nil
self:setMenuButtonInfoDirty() self:refreshButtons()
end end
-- ---- callbacks fra XML (state = 1-baseret indeks; element = det klikkede element) ---- -- ---- callbacks fra XML (state = 1-baseret indeks; element = det klikkede element) ----
@@ -293,58 +326,70 @@ function SmartPickupFrame:onClickRun(state)
end end
function SmartPickupFrame:onClickTarget(state) function SmartPickupFrame:onClickTarget(state)
local run = self:getRun() self:safe(function(frame)
if run == nil then return end local run = frame:getRun()
-- targetIds[1] er false = "(vælg mål)"; modellen tolker false som "ryd målet" if run == nil then return end
ADRunsController.updateRun(run.id, {targetWayPointId = self.targetIds[state] or false}) -- targetIds[1] er false = "(vælg mål)"; modellen tolker false som "ryd målet"
self:safe(self.rebuild) ADRunsController.updateRun(run.id, {targetWayPointId = frame.targetIds[state] or false})
frame:rebuild()
end)
end end
function SmartPickupFrame:onClickIngredientMode(state) function SmartPickupFrame:onClickIngredientMode(state)
local run = self:getRun() self:safe(function(frame)
if run == nil then return end local run = frame:getRun()
ADRunsController.updateRun(run.id, {ingredientMode = state == SmartPickupFrame.STATE_ON and "manual" or "auto"}) if run == nil then return end
self:safe(self.rebuild) ADRunsController.updateRun(run.id, {ingredientMode = state == SmartPickupFrame.STATE_ON and "manual" or "auto"})
frame:rebuild()
end)
end end
function SmartPickupFrame:onClickIngredient(state, element) function SmartPickupFrame:onClickIngredient(state, element)
local run, data = self:getRun(), self.rowData[element] self:safe(function(frame)
if run == nil or data == nil then return end local run, data = frame:getRun(), frame.rowData[element]
local names = {} if run == nil or data == nil then return end
for _, name in ipairs(run.ingredients) do local names = {}
if name ~= data.name then table.insert(names, name) end for _, name in ipairs(run.ingredients) do
end if name ~= data.name then table.insert(names, name) end
if state == SmartPickupFrame.STATE_ON then end
table.insert(names, data.name) if state == SmartPickupFrame.STATE_ON then
end table.insert(names, data.name)
ADRunsController.updateRun(run.id, {ingredients = names}) end
ADRunsController.updateRun(run.id, {ingredients = names})
end)
end end
function SmartPickupFrame:onClickWaitPool(state) function SmartPickupFrame:onClickWaitPool(state)
local run = self:getRun() self:safe(function(frame)
if run == nil then return end local run = frame:getRun()
ADRunsController.updateRun(run.id, {waitPoolGroup = self.poolGroups[state] or ""}) if run == nil then return end
ADRunsController.updateRun(run.id, {waitPoolGroup = frame.poolGroups[state] or ""})
end)
end end
function SmartPickupFrame:onClickLoops(state) function SmartPickupFrame:onClickLoops(state)
local run = self:getRun() self:safe(function(frame)
if run == nil then return end local run = frame:getRun()
ADRunsController.updateRun(run.id, {loops = state - 1}) if run == nil then return end
ADRunsController.updateRun(run.id, {loops = state - 1})
end)
end end
function SmartPickupFrame:onClickVehicle(state, element) function SmartPickupFrame:onClickVehicle(state, element)
local run, data = self:getRun(), self.rowData[element] self:safe(function(frame)
if run == nil or data == nil then return end local run, data = frame:getRun(), frame.rowData[element]
if state == SmartPickupFrame.STATE_ON then if run == nil or data == nil then return end
local previousRunId = ADRunsController.assignVehicle(run.id, data.vehicle) if state == SmartPickupFrame.STATE_ON then
if previousRunId ~= nil then local previousRunId = ADRunsController.assignVehicle(run.id, data.vehicle)
local previous = ADRuns.getRun(ADRunsController.store, previousRunId) if previousRunId ~= nil then
self.statusOverride = string.format(self.i18n:getText("spu_movedFrom"), previous ~= nil and previous.name or tostring(previousRunId)) local previous = ADRuns.getRun(ADRunsController.store, 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 end
else frame:refreshLive()
ADRunsController.unassignVehicle(data.vehicle) end)
end
self:safe(self.refreshLive)
end end
-- ---- knapper i bundlinjen ---- -- ---- knapper i bundlinjen ----
@@ -370,7 +415,8 @@ end
function SmartPickupFrame:onClickNew() function SmartPickupFrame:onClickNew()
self:safe(function(frame) 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.selectedRunId = run.id
frame.statusOverride = nil frame.statusOverride = nil
frame:rebuild() frame:rebuild()
@@ -384,11 +430,13 @@ function SmartPickupFrame:onClickRename()
end end
function SmartPickupFrame:onRenameResult(value, clickOk) function SmartPickupFrame:onRenameResult(value, clickOk)
local run = self:getRun() self:safe(function(frame)
if clickOk and run ~= nil and value ~= nil and value:match("%S") then local run = frame:getRun()
ADRunsController.updateRun(run.id, {name = value}) if clickOk and run ~= nil and value ~= nil and value:match("%S") then
self:safe(self.rebuild) ADRunsController.updateRun(run.id, {name = value})
end frame:rebuild()
end
end)
end end
function SmartPickupFrame:onClickDelete() function SmartPickupFrame:onClickDelete()
@@ -398,10 +446,12 @@ function SmartPickupFrame:onClickDelete()
end end
function SmartPickupFrame:onDeleteResult(yes) function SmartPickupFrame:onDeleteResult(yes)
local run = self:getRun() self:safe(function(frame)
if yes and run ~= nil then local run = frame:getRun()
ADRunsController.deleteRun(run.id) if yes and run ~= nil then
self.selectedRunId = nil ADRunsController.deleteRun(run.id)
self:safe(self.rebuild) frame.selectedRunId = nil
end frame:rebuild()
end
end)
end end