diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua index 173f55e..2fbd6db 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.lua +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.lua @@ -21,6 +21,10 @@ function SmartPickupFrame.new(i18n, getAdEnv) self.clonedRows = {} self.refreshTimer = 0 self.statusOverride = nil + self.statusRow = nil -- Text-elementet i den klonede statusrække + self.pendingRebuild = false + self.frameOpen = false + self.focusLost = false self.backButtonInfo = {inputAction = InputAction.MENU_BACK} self.startButtonInfo = {inputAction = InputAction.MENU_ACTIVATE, text = i18n:getText("spu_button_start"), callback = function() self:onClickStartStop() end} self.newButtonInfo = {inputAction = InputAction.MENU_EXTRA_1, text = i18n:getText("spu_button_new"), callback = function() self:onClickNew() end} @@ -43,7 +47,7 @@ end -- Prefabs skal ud af layoutet før de klones, ellers vises de selv som rækker. function SmartPickupFrame:initialize() - for _, name in ipairs({"ingredientPrefab", "vehiclePrefab", "poolPrefab"}) do + for _, name in ipairs({"ingredientPrefab", "vehiclePrefab", "textPrefab"}) do local prefab = self[name] if prefab ~= nil then prefab:unlinkElement() @@ -58,29 +62,69 @@ function SmartPickupFrame:initialize() self.loopsOption:setTexts(loopTexts) end +-- FocusManager har skiftet API mellem FS-versioner; her hentes det fokuserede element +-- så blødt som muligt, fordi det kun bruges til at opdage at fokus lige er slettet. +function SmartPickupFrame.getFocusedElement() + if FocusManager == nil then + return nil + end + if FocusManager.getFocusedElement ~= nil then + local ok, element = pcall(FocusManager.getFocusedElement, FocusManager) + if ok then + return element + end + end + local data = FocusManager.currentFocusData + if type(data) == "table" then + return data.focusElement + end + return nil +end + +function SmartPickupFrame.isDescendantOf(element, ancestor) + while element ~= nil do + if element == ancestor then + return true + end + element = element.parent + end + return false +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) +function SmartPickupFrame:restoreFocusIfLost() + if self.focusLost and self.frameOpen and self.boxLayout ~= nil then + FocusManager:setFocus(self.boxLayout) + end + self.focusLost = false end function SmartPickupFrame:onFrameOpen() SmartPickupFrame:superClass().onFrameOpen(self) self.statusOverride = nil + self.frameOpen = true + self.pendingRebuild = false self:safe(self.rebuild) + -- Som AutoDrive's egne sider: fokus på selve listen, ikke på en enkelt række. + self:safe(function(frame) + FocusManager:setFocus(frame.boxLayout) + end) end function SmartPickupFrame:onFrameClose() + self.frameOpen = false SmartPickupFrame:superClass().onFrameClose(self) end --- Live-opdatering af status/traktor-rækker mens siden er åben (uden at genopbygge rækkerne). +-- Genopbygning sker her og aldrig inde i et klik-callback: sletter man elementet midt i +-- dets egen klik-håndtering, hopper værdien frem og tilbage og fokus ryger. function SmartPickupFrame:update(dt) SmartPickupFrame:superClass().update(self, dt) + if self.pendingRebuild then + self.pendingRebuild = false + self:safe(self.rebuild) + end self.refreshTimer = self.refreshTimer + dt if self.refreshTimer >= SmartPickupFrame.REFRESH_INTERVAL_MS then self.refreshTimer = 0 @@ -93,9 +137,13 @@ function SmartPickupFrame:safe(fn, ...) if not ok then Logging.warning(SmartPickupFrame.LOG_PREFIX .. "GUI-fejl: %s", tostring(err)) self.statusOverride = string.format(self.i18n:getText("spu_status_error"), tostring(err)) - if self.statusText ~= nil then - self.statusText:setText(self.statusOverride) - end + self:setStatusText(self.statusOverride) + end +end + +function SmartPickupFrame:setStatusText(text) + if self.statusRow ~= nil then + self.statusRow:setText(text) end end @@ -108,15 +156,20 @@ end -- 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() + local focused = SmartPickupFrame.getFocusedElement() for _, row in ipairs(self.clonedRows) do + if focused ~= nil and SmartPickupFrame.isDescendantOf(focused, row) then + self.focusLost = true + end FocusManager:removeElement(row) row:delete() end self.clonedRows, self.rowData = {}, {} + self.statusRow = nil end -function SmartPickupFrame:addRow(prefab, parent, data) - local row = prefab:clone(parent) +function SmartPickupFrame:addRow(prefab, data) + local row = prefab:clone(self.boxLayout) FocusManager:loadElementFromCustomValues(row) table.insert(self.clonedRows, row) local option = row:getDescendantByName("option") @@ -127,6 +180,30 @@ function SmartPickupFrame:addRow(prefab, parent, data) return row end +-- Tekstrække uden valgmuligheder: sektionstitler, Wait-pulje-linjer og statuslinjen. +function SmartPickupFrame:addTextRow(text) + local title = self:addRow(self.textPrefab, {kind = "text"}):getDescendantByName("title") + title:setText(text) + return title +end + +-- AutoDrive farver hver række i onCreate; her sker det efter hver genopbygning, +-- fordi rækkerne først findes på det tidspunkt. +function SmartPickupFrame:colorRows() + local colors = InGameMenuSettingsFrame ~= nil and InGameMenuSettingsFrame.COLOR_ALTERNATING or nil + if colors == nil or self.boxLayout == nil then + return + end + local unpackFn = table.unpack or unpack + local isEvenRow = false + for _, element in ipairs(self.boxLayout.elements) do + if element.setImageColor ~= nil and colors[isEvenRow] ~= nil then + element:setImageColor(nil, unpackFn(colors[isEvenRow])) + end + isEvenRow = not isEvenRow + end +end + function SmartPickupFrame:rebuild() local adEnv = self.getAdEnv() local store = ADRunsController.store @@ -146,14 +223,18 @@ function SmartPickupFrame:rebuild() self:fillIngredientRows(adEnv, run) self:fillWaitPoolOption(adEnv, run) self.loopsOption:setState(run.loops + 1, false) + self:addTextRow(self.i18n:getText("spu_vehicles")) self:fillVehicleRows(adEnv, run) end - self:fillPoolRows(adEnv) - for _, box in ipairs({self.ingredientBox, self.vehicleBox, self.poolBox, self.boxLayout}) do - box:invalidateLayout() + if adEnv ~= nil then + self:addTextRow(self.i18n:getText("spu_waitPools")) + self:fillPoolRows(adEnv) end + self.statusRow = self:addTextRow("") + self:colorRows() + self.boxLayout:invalidateLayout() self:refreshLive() - self:focusRunOption() + self:restoreFocusIfLost() end function SmartPickupFrame:fillRunOption(adEnv) @@ -198,7 +279,7 @@ function SmartPickupFrame:fillIngredientRows(adEnv, run) chosen[name] = true end 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, {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, false) end @@ -225,7 +306,7 @@ function SmartPickupFrame:fillVehicleRows(adEnv, run) end for _, vehicle in ipairs(ADRunsController.getFarmVehicles(adEnv)) do 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, {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, false) row:getDescendantByName("title"):setText(self:describeVehicle(adEnv, run, vehicle, inRun[id])) @@ -264,8 +345,7 @@ function SmartPickupFrame:fillPoolRows(adEnv) for _, pool in ipairs(ADRunsController.getWaitPools(adEnv)) do local names = occupied[pool.group] or {} local suffix = #names > 0 and (" (" .. table.concat(names, ", ") .. ")") or "" - local row = self:addRow(self.poolPrefab, self.poolBox, {kind = "pool"}) - row:getDescendantByName("title"):setText(string.format(self.i18n:getText("spu_waitPoolRow"), pool.group, pool.count, #names, suffix)) + self:addTextRow(string.format(self.i18n:getText("spu_waitPoolRow"), pool.group, pool.count, #names, suffix)) end end @@ -289,7 +369,7 @@ function SmartPickupFrame:refreshLive() local adEnv = self.getAdEnv() local run = self:getRun() if adEnv == nil then - self.statusText:setText(self.i18n:getText("spu_status_noAd")) + self:setStatusText(self.i18n:getText("spu_status_noAd")) return end local isRunning = false @@ -298,14 +378,14 @@ function SmartPickupFrame:refreshLive() isRunning = status.running > 0 local text = isRunning 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.statusText:setText(self.statusOverride or text) + self:setStatusText(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, SmartPickupFrame.getOptionState(option) == SmartPickupFrame.STATE_ON)) end end else - self.statusText:setText(self.statusOverride or self.i18n:getText("spu_noRuns")) + self:setStatusText(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. @@ -322,7 +402,7 @@ end function SmartPickupFrame:onClickRun(state) self.selectedRunId = self.runIds[state] self.statusOverride = nil - self:safe(self.rebuild) + self.pendingRebuild = true end function SmartPickupFrame:onClickTarget(state) @@ -331,7 +411,7 @@ function SmartPickupFrame:onClickTarget(state) 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() + frame.pendingRebuild = true end) end @@ -340,7 +420,7 @@ function SmartPickupFrame:onClickIngredientMode(state) 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() + frame.pendingRebuild = true end) end @@ -409,7 +489,7 @@ function SmartPickupFrame:onClickStartStop() frame.statusOverride = string.format(frame.i18n:getText("spu_status_started"), started, skipped) end ADRunsController.save() - frame:rebuild() + frame.pendingRebuild = true end) end @@ -419,7 +499,7 @@ function SmartPickupFrame:onClickNew() local run = ADRunsController.createRun(string.format(frame.i18n:getText("spu_newRunName"), ADRunsController.store.nextId)) frame.selectedRunId = run.id frame.statusOverride = nil - frame:rebuild() + frame.pendingRebuild = true end) end @@ -438,7 +518,7 @@ function SmartPickupFrame:onRenameResult(value, clickOk) 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() + frame.pendingRebuild = true end end) end @@ -458,7 +538,7 @@ function SmartPickupFrame:onDeleteResult(yes) if yes and run ~= nil then ADRunsController.deleteRun(run.id) frame.selectedRunId = nil - frame:rebuild() + frame.pendingRebuild = true end end) end diff --git a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml index 76d1c52..a6b5c31 100644 --- a/FS25_ADSmartPickup/gui/SmartPickupFrame.xml +++ b/FS25_ADSmartPickup/gui/SmartPickupFrame.xml @@ -8,66 +8,60 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - + + + + + + + + + - + - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FS25_ADSmartPickup/gui/guiProfiles.xml b/FS25_ADSmartPickup/gui/guiProfiles.xml index e75ec5c..f9571f7 100644 --- a/FS25_ADSmartPickup/gui/guiProfiles.xml +++ b/FS25_ADSmartPickup/gui/guiProfiles.xml @@ -5,14 +5,8 @@ - - - - - - - - + diff --git a/FS25_ADSmartPickup/modDesc.xml b/FS25_ADSmartPickup/modDesc.xml index 5084c00..4e647fd 100644 --- a/FS25_ADSmartPickup/modDesc.xml +++ b/FS25_ADSmartPickup/modDesc.xml @@ -1,7 +1,7 @@ masterdraco - 1.9.0.0 + 1.9.1.0 <en>AD Smart Pickup</en>