feat(gui): guiden "Opsæt mark" — markfanen får oversigt med resumé (klik på en linje = ret ét trin), trin-for-trin-guide på fælles guide-ramme (ADGuide/ADFieldGuide) og egen markflåde-visning (v1.31.0.0)
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
-- AD Guide
|
||||
-- Fælles guide-motor (ren Lua, ingen spil-API) for "Opsæt mark" og "Opsæt flow": ét trin ad gangen,
|
||||
-- trin der ikke er relevante springes over, tælling "Trin 3 af 7" over de synlige trin, og [Ret] der
|
||||
-- åbner ét enkelt trin. Motoren kender ikke marker eller flows; en trin-definition giver rækkefølgen og
|
||||
-- hvornår et trin vises:
|
||||
-- def = {order = {stepId, ...}, isVisible = function(stepId, guide) -> bool}
|
||||
-- Guiden er en tabel {def, draft, context, step, single, returnTo, snapshot}; alle funktioner returnerer
|
||||
-- en ny guide (kladden kopieres), så [Annullér] aldrig har ændret noget.
|
||||
|
||||
ADGuide = {}
|
||||
|
||||
-- Kopi af rene data (tabeller uden cykler); funktioner og tal/tekster deles.
|
||||
function ADGuide.deepCopy(value)
|
||||
if type(value) ~= "table" then return value end
|
||||
local copy = {}
|
||||
for key, item in pairs(value) do copy[key] = ADGuide.deepCopy(item) end
|
||||
return copy
|
||||
end
|
||||
|
||||
-- Ny liste med item tilføjet (on) eller fjernet; aldrig dubletter.
|
||||
function ADGuide.toggleItem(list, item, on)
|
||||
local result, found = {}, false
|
||||
for _, value in ipairs(list or {}) do
|
||||
if value == item then
|
||||
found = true
|
||||
if on then table.insert(result, value) end
|
||||
else
|
||||
table.insert(result, value)
|
||||
end
|
||||
end
|
||||
if on and not found then table.insert(result, item) end
|
||||
return result
|
||||
end
|
||||
|
||||
local function clone(guide, changes)
|
||||
local copy = {}
|
||||
for key, value in pairs(guide) do copy[key] = value end
|
||||
for key, value in pairs(changes or {}) do
|
||||
if value == false and (key == "snapshot") then copy[key] = nil else copy[key] = value end
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
local function isVisible(guide, step)
|
||||
return guide.def.isVisible == nil or guide.def.isVisible(step, guide) == true
|
||||
end
|
||||
|
||||
function ADGuide.visibleSteps(guide)
|
||||
local steps = {}
|
||||
for _, step in ipairs(guide.def.order) do
|
||||
if isVisible(guide, step) then table.insert(steps, step) end
|
||||
end
|
||||
return steps
|
||||
end
|
||||
|
||||
local function orderIndex(guide, step)
|
||||
for index, id in ipairs(guide.def.order) do
|
||||
if id == step then return index end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Første synlige trin efter (dir = 1) eller før (dir = -1) det aktuelle i rækkefølgen.
|
||||
local function neighbour(guide, dir)
|
||||
local order = guide.def.order
|
||||
local index = orderIndex(guide, guide.step) + dir
|
||||
while index >= 1 and index <= #order do
|
||||
if isVisible(guide, order[index]) then return order[index] end
|
||||
index = index + dir
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- options = {step = id, single = bool, returnTo = "close"|"summary"}
|
||||
function ADGuide.new(def, draft, context, options)
|
||||
options = options or {}
|
||||
local guide = {def = def, draft = draft, context = context, step = options.step,
|
||||
single = options.single == true, returnTo = options.returnTo or "close"}
|
||||
if guide.step == nil then
|
||||
guide.step = ADGuide.visibleSteps(guide)[1] or def.order[1]
|
||||
end
|
||||
if guide.single then guide.snapshot = ADGuide.deepCopy(draft) end
|
||||
return guide
|
||||
end
|
||||
|
||||
-- index (nil hvis det aktuelle trin ikke er synligt), total synlige trin.
|
||||
function ADGuide.position(guide)
|
||||
local steps = ADGuide.visibleSteps(guide)
|
||||
local index = nil
|
||||
for position, step in ipairs(steps) do
|
||||
if step == guide.step then index = position end
|
||||
end
|
||||
return index, #steps
|
||||
end
|
||||
|
||||
-- Sidste trin: ingen synlige efter, eller et [Ret]-trin der gemmer direkte.
|
||||
function ADGuide.isLast(guide)
|
||||
if guide.single and guide.returnTo == "close" then return true end
|
||||
if guide.single then return false end
|
||||
return neighbour(guide, 1) == nil
|
||||
end
|
||||
|
||||
-- Næste: event "done" når der gemmes (sidste trin, eller [Ret] fra fanens oversigt).
|
||||
function ADGuide.next(guide)
|
||||
if guide.single then
|
||||
if guide.returnTo == "summary" then
|
||||
return clone(guide, {step = "summary", single = false, snapshot = false}), nil
|
||||
end
|
||||
return guide, "done"
|
||||
end
|
||||
local step = neighbour(guide, 1)
|
||||
if step == nil then return guide, "done" end
|
||||
return clone(guide, {step = step}), nil
|
||||
end
|
||||
|
||||
-- Tilbage: event "cancel" når guiden lukkes (første trin, eller [Ret] fra fanens oversigt).
|
||||
-- I et [Ret]-trin fra guidens eget oversigts-trin fortrydes trinnets ændringer.
|
||||
function ADGuide.back(guide)
|
||||
if guide.single then
|
||||
if guide.returnTo == "summary" then
|
||||
return clone(guide, {step = "summary", single = false, draft = guide.snapshot or guide.draft, snapshot = false}), nil
|
||||
end
|
||||
return guide, "cancel"
|
||||
end
|
||||
local step = neighbour(guide, -1)
|
||||
if step == nil then return guide, "cancel" end
|
||||
return clone(guide, {step = step}), nil
|
||||
end
|
||||
|
||||
-- [Ret]: åbn kun ét trin. returnTo "summary" (guidens oversigt) eller "close" (gem og luk).
|
||||
function ADGuide.jump(guide, step, returnTo)
|
||||
return clone(guide, {step = step, single = true, returnTo = returnTo or "close", snapshot = ADGuide.deepCopy(guide.draft)})
|
||||
end
|
||||
|
||||
function ADGuide.withDraft(guide, draft)
|
||||
return clone(guide, {draft = draft})
|
||||
end
|
||||
|
||||
function ADGuide.withContext(guide, context)
|
||||
return clone(guide, {context = context})
|
||||
end
|
||||
|
||||
-- label = {key = "...", args = {...}} (oversættes) eller {text = "..."} (færdig tekst).
|
||||
function ADGuide.labelText(label, getText)
|
||||
if type(label) ~= "table" then return "" end
|
||||
if label.text ~= nil then return tostring(label.text) end
|
||||
local template = getText(label.key)
|
||||
if label.args == nil or #label.args == 0 then return tostring(template) end
|
||||
local unpackFn = table.unpack or unpack
|
||||
local ok, text = pcall(string.format, template, unpackFn(label.args))
|
||||
return ok and text or tostring(template)
|
||||
end
|
||||
|
||||
-- Resumé-linje {step, title, parts} -> "Afgrøde: Behold det der gror, så igen".
|
||||
function ADGuide.lineText(line, getText)
|
||||
local parts = {}
|
||||
for _, part in ipairs(line.parts or {}) do
|
||||
local text = ADGuide.labelText(part, getText)
|
||||
if text ~= "" then table.insert(parts, text) end
|
||||
end
|
||||
return ADGuide.labelText(line.title, getText) .. ": " .. (#parts > 0 and table.concat(parts, ", ") or "-")
|
||||
end
|
||||
Reference in New Issue
Block a user