feat(slices): персист панов+слайсов, раздельно КВ/трансвертеры

Сохранение и восстановление панадаптеров, слайсов и раскладки per-device,
раздельно по контексту: КВ ('hf') и каждый трансвертер ('xvtr_N') — паны
одного контекста не имеют смысла в другом.

Settings: секция "pans"/<ctx> под MAC (grid-режим/доли, на пан —
freq/rate/adc/zoom/pan/доли стека+колонки+сплиттер спектр/водопад, на слайс —
freq/mode/фильтр/agc/vol/mute/squelch/аудиоустройство). TSliceCfg/TPanCfg/
TPansConfig + Default/Save/LoadPans(Ctx).

MainForm: CreatePanCore (вынос ядра из OnAddPanClick), SavePanWorkspaceCtx/
RestorePanWorkspaceCtx/TearDownWorkspace, PanContextKey. Хук контроллера
OnBeforeStop (снимок до гашения DDC в StopRunning). Переходы ActivateXvtrBand/
DeactivateXvtr обёрнуты save-старого/teardown/restore-нового (гейт FRunning);
restore XVTR-панов после ActivateXvtr (FCurrentXvtr целевой ⇒ XvtrTranslate
верный). PanafallPanel.DestroyAllSliceFlags. Смена КВ-банда контекст не меняет.

Round-trip сериализации и разделения контекстов проверены автономно; на
экране не проверено.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 23:06:12 +03:00
co-authored by Claude Opus 4.8
parent b3edf3a4ad
commit 543453007e
4 changed files with 427 additions and 22 deletions
+10
View File
@@ -244,6 +244,7 @@ type
function FindSliceFlag(SliceId: Integer): TVfoOverlay;
procedure CreateSliceFlag(SliceId: Integer);
procedure DestroySliceFlag(SliceId: Integer);
procedure DestroyAllSliceFlags; // сброс всех слайсов панели (+RemoveSlice)
// «×» на флаге: нельзя free внутри его же обработчика мыши — пометить...
procedure RequestSliceClose(SliceId: Integer);
// …и снять после возврата из диспетчера.
@@ -1004,6 +1005,15 @@ begin
if Assigned(FOnFlagsInvalidate) then FOnFlagsInvalidate(Self); // Self = панель (роутинг по пану)
end;
procedure TPanafallPanel.DestroyAllSliceFlags;
// Полный сброс слайсов панели (каждый DestroySliceFlag зовёт RemoveSlice +
// сжимает FSliceFlags). Нужен персисту: слайсы пана 0 переживают STOP, а
// восстановление создаёт их заново — иначе дубли.
begin
while Length(FSliceFlags) > 0 do
DestroySliceFlag(FSliceFlags[0].SliceId);
end;
procedure TPanafallPanel.RequestSliceClose(SliceId: Integer);
begin
FPendingSliceClose := SliceId;