diff --git a/FS25_ADSmartPickup/adSmartPickup.lua b/FS25_ADSmartPickup/adSmartPickup.lua index 0cbb5f7..72c9645 100644 --- a/FS25_ADSmartPickup/adSmartPickup.lua +++ b/FS25_ADSmartPickup/adSmartPickup.lua @@ -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 diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index b44168d..7cd3a0f 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -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,58 +326,70 @@ function SmartPickupFrame:onClickRun(state) end function SmartPickupFrame:onClickTarget(state) - local run = self: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) + 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 = frame.targetIds[state] or false}) + frame:rebuild() + end) end function SmartPickupFrame:onClickIngredientMode(state) - local run = self:getRun() - if run == nil then return end - ADRunsController.updateRun(run.id, {ingredientMode = state == SmartPickupFrame.STATE_ON and "manual" or "auto"}) - self:safe(self.rebuild) + 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"}) + frame:rebuild() + end) end function SmartPickupFrame:onClickIngredient(state, element) - local run, data = self:getRun(), self.rowData[element] - if run == nil or data == nil then return end - local names = {} - for _, name in ipairs(run.ingredients) do - if name ~= data.name then table.insert(names, name) end - end - if state == SmartPickupFrame.STATE_ON then - table.insert(names, data.name) - end - ADRunsController.updateRun(run.id, {ingredients = names}) + 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 + if name ~= data.name then table.insert(names, name) end + end + if state == SmartPickupFrame.STATE_ON then + table.insert(names, data.name) + end + ADRunsController.updateRun(run.id, {ingredients = names}) + end) end function SmartPickupFrame:onClickWaitPool(state) - local run = self:getRun() - if run == nil then return end - ADRunsController.updateRun(run.id, {waitPoolGroup = self.poolGroups[state] or ""}) + self:safe(function(frame) + local run = frame:getRun() + if run == nil then return end + ADRunsController.updateRun(run.id, {waitPoolGroup = frame.poolGroups[state] or ""}) + end) end function SmartPickupFrame:onClickLoops(state) - local run = self:getRun() - if run == nil then return end - ADRunsController.updateRun(run.id, {loops = state - 1}) + 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] - 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)) + 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) + frame.statusOverride = string.format(frame.i18n:getText("spu_movedFrom"), previous ~= nil and previous.name or tostring(previousRunId)) + end + else + ADRunsController.unassignVehicle(data.vehicle) 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() - if clickOk and run ~= nil and value ~= nil and value:match("%S") then - ADRunsController.updateRun(run.id, {name = value}) - self:safe(self.rebuild) - end + 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}) + frame:rebuild() + end + end) end function SmartPickupFrame:onClickDelete() @@ -398,10 +446,12 @@ function SmartPickupFrame:onClickDelete() end function SmartPickupFrame:onDeleteResult(yes) - local run = self:getRun() - if yes and run ~= nil then - ADRunsController.deleteRun(run.id) - self.selectedRunId = nil - self:safe(self.rebuild) - end + self:safe(function(frame) + local run = frame:getRun() + if yes and run ~= nil then + ADRunsController.deleteRun(run.id) + frame.selectedRunId = nil + frame:rebuild() + end + end) end