ui(pans): флаг A появляется вместе с первым доп. панадаптером

Доп. панадаптер приходит со своим слайсом и сразу рисует его флаг, а
главный приёмник оставался без флага — [A] показывался только когда слайс
создавали на самом пане 0 (инвариант «панель скрыта ИЛИ есть слайсы B+»).

TPanafallPanel: третье слагаемое инварианта — MainFlagForced, выставляется
хозяином. MainForm.ResizeSpectrumPanels после LayoutPanStack ставит
FPan.MainFlagForced := PanCount > 1 и зовёт UpdateMainFlagVisibility; это
единая точка — добавление/закрытие пана, restore персиста и STOP все идут
через пересчёт раскладки. Сворачивание левой панели (MainFlagPinned) на
это не влияет: пока жив хотя бы один доп. пан, флаг A остаётся.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 12:11:57 +03:00
co-authored by Claude Opus 4.8
parent 6c38777c58
commit 3bdcfa0674
3 changed files with 22 additions and 7 deletions
+11 -5
View File
@@ -84,6 +84,7 @@ type
FActiveFlag: TVfoOverlay; // чьё событие сейчас обрабатывается
FPendingSliceClose: Integer; // SliceId к удалению после мыши (0=нет)
FMainFlagPinned: Boolean; // True = левая панель хозяина скрыта
FMainFlagForced: Boolean; // True = есть доп. панадаптеры
FLastSliceMeterMs: QWord; // троттлинг S-метра слайсов (~10 Гц)
FLastSliceDMRMs: QWord; // семантический DMR-статус (~4 Гц)
FOnWireFlag: TWireFlagEvent;
@@ -278,7 +279,8 @@ type
function CursorOverDisplay: Boolean; // курсор над спектром/водопадом пана
// Единый packing всех флагов (A+B+) в один ряд, без наездов.
procedure LayoutFlags;
// Флаг A виден: панель хозяина скрыта (MainFlagPinned) ИЛИ есть слайсы B+.
// Флаг A виден: панель хозяина скрыта (MainFlagPinned) ИЛИ есть слайсы B+
// ИЛИ хозяин требует его принудительно (MainFlagForced — доп. панадаптеры).
procedure UpdateMainFlagVisibility;
procedure PushSliceFlagState(SliceId: Integer);
procedure PushAllSliceFlagStates; // все флаги панели (после смены rate и т.п.)
@@ -297,6 +299,9 @@ type
property Controller: TRadioController read FController write FController;
property MainFlag: TVfoOverlay read FMainFlag;
property MainFlagPinned: Boolean read FMainFlagPinned write FMainFlagPinned;
// Хозяин форсит флаг A (есть доп. панадаптеры — их слайсы должны быть
// представлены флагом и на главном приёмнике).
property MainFlagForced: Boolean read FMainFlagForced write FMainFlagForced;
// Проводка семантических событий флага (MODE/DSP/AUD/…) — хозяин.
property OnWireFlag: TWireFlagEvent read FOnWireFlag write FOnWireFlag;
// Слайс закрыт «×» — хозяину сбросить активный слайс и т.п.
@@ -1286,15 +1291,16 @@ begin
end;
// Флаг главного приёмника (слайс A) виден, когда СКРЫТА левая панель хозяина
// (MainFlagPinned) ЛИБО есть доп. слайсы (B+): тогда A всегда представлен
// флагом рядом с B/C…, а сворачивание/раскрытие панели при живых слайсах не
// дёргает его появление.
// (MainFlagPinned) ЛИБО есть доп. слайсы (B+) ЛИБО живут доп. панадаптеры
// (MainFlagForced — их слайсы уже показаны флагами, главный приёмник должен
// быть представлен так же): тогда A всегда представлен флагом рядом с B/C…,
// а сворачивание/раскрытие панели при живых слайсах не дёргает его появление.
procedure TPanafallPanel.UpdateMainFlagVisibility;
var
ShouldShow: Boolean;
begin
if (FMainFlag = nil) or (FController = nil) then Exit;
ShouldShow := FMainFlagPinned or (Length(FSliceFlags) > 0);
ShouldShow := FMainFlagPinned or FMainFlagForced or (Length(FSliceFlags) > 0);
if ShouldShow = FMainFlag.Visible then Exit;
if ShouldShow then
begin