feat(slices): этап 3.4 ADC-селектор панов + этап 3.5 pop-out в отдельное окно

3.4 ADC-селектор:
- Caps.NumADCs (HPSDR: 2 для ORION/ORION2/SATURN, как Pkt.NumADCs; Pluto: 1)
- controller.PanADC/SetPanADC: пан 0 = FMainADCSrc + ConfigureDDCs (ADC
  прокинут и в штатные вызовы — смена rate не сбрасывает выбор), паны N =
  FPanADC[] + SetPanDDC (RebuildDDCSpecific)
- Бейдж «A1/A2» в шапке каждого пана (виден при NumADCs>1), клик = тумблер;
  пан 0 на ADC2 закрывает сценарий rx2-support

3.5 Pop-out:
- «⧉» в шапке панов N → PopOutPan: TForm.CreateNew + ReparentTo (перенос
  всех контролов панели), [×] окна = DockBackPan (возврат в стек), «×»
  шапки = закрыть пан; LayoutPanStack скипает плавающих; колесо окна =
  FormMouseWheel
- GL: reparent пересоздаёт контексты → ResetGLCache обеих канв без glDelete
  (по прецеденту MSAA); TWaterfallViewOpenGL.ResetGLCache новый, история
  водопада перезаливается из CPU-буфера

Проверено юзером на экране (беглый тест: селектор и pop-out работают).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 23:54:09 +03:00
co-authored by Claude Fable 5
parent e5921b8196
commit 8bf8bcfc76
9 changed files with 348 additions and 9 deletions
+87 -3
View File
@@ -98,6 +98,11 @@ type
FHeaderRateLabel: TLabel; // «NNN kHz»; у панов N>0 кликабелен (селектор rate)
FRateClickable: Boolean;
FOnRateClick: TNotifyEvent;
FHeaderADCLabel: TLabel; // «A1»/«A2»; кликабелен при 2 АЦП (тумблер, 3.4)
FADCClickable: Boolean;
FOnADCClick: TNotifyEvent;
FBtnPopPan: TFlatButton; // «⧉» pop-out в отдельное окно (паны N>0, 3.5)
FOnPanPopOut: TNotifyEvent;
FBtnClosePan: TFlatButton; // «×» в шапке (только паны N>0)
FBtnAddPan: TFlatButton; // «⊞» в ряду пан/зума (только пан 0)
FHeaderVisible: Boolean;
@@ -110,6 +115,9 @@ type
procedure BtnAddPanClick(Sender: TObject);
procedure HeaderRateClick(Sender: TObject);
procedure SetRateClickable(AValue: Boolean);
procedure HeaderADCClick(Sender: TObject);
procedure SetADCClickable(AValue: Boolean);
procedure BtnPopPanClick(Sender: TObject);
procedure PositionHeaderChildren(RW, H: Integer);
// Видимое частотное окно ЭТОГО пана: пан 0 — окно главного (зум/пан),
// паны N — центр их DDC ± rate/2 (пан-зум — этап 3.3+).
@@ -148,6 +156,10 @@ type
// Создаёт контролы в AParent и подвешивает обработчики (см. поля выше).
procedure Build(AParent: TWinControl; AMSAA: Integer);
// Переносит ВСЕ контролы панели в другого родителя (pop-out/dock, 3.5).
// GL-контексты канв при этом пересоздаются — хозяин обязан сбросить
// GL-кэши вьюхи (ResetGLCache) после вызова.
procedure ReparentTo(NewParent: TWinControl);
// Полная раскладка стека. ATopOffset — высота wideband-блока хозяина
// над спектром. Show-флаги приходят от контроллера (FShowSpectrum/
// FShowWaterfall).
@@ -182,9 +194,18 @@ type
procedure SetHeaderRate(const S: string);
property RateClickable: Boolean read FRateClickable write SetRateClickable;
property OnRateClick: TNotifyEvent read FOnRateClick write FOnRateClick;
// Бейдж ADC-источника «A1»/«A2» (3.4): показывается при NumADCs>1,
// клик = тумблер АЦП (хозяин).
procedure SetHeaderADC(const S: string);
property ADCClickable: Boolean read FADCClickable write SetADCClickable;
property OnADCClick: TNotifyEvent read FOnADCClick write FOnADCClick;
// «⧉» (3.5): вынести пан в отдельное окно (хозяин).
property OnPanPopOut: TNotifyEvent read FOnPanPopOut write FOnPanPopOut;
property HeaderPanel: TPanel read FHeaderPanel;
property HeaderLabel: TLabel read FHeaderLabel;
property HeaderRateLabel: TLabel read FHeaderRateLabel;
property HeaderADCLabel: TLabel read FHeaderADCLabel;
property BtnPopPan: TFlatButton read FBtnPopPan;
property BtnClosePan: TFlatButton read FBtnClosePan;
property BtnAddPan: TFlatButton read FBtnAddPan;
// Ряд пан/зума: у панов N>0 скрыт (их зум — этап 3.3+).
@@ -380,10 +401,35 @@ begin
FHeaderRateLabel.Parent := FHeaderPanel;
FHeaderRateLabel.AutoSize := True;
FHeaderRateLabel.OnClick := HeaderRateClick;
// ADC-бейдж «A1»/«A2» (3.4): пустой текст = скрыт (одноАЦПшные платы).
FHeaderADCLabel := TLabel.Create(Self);
FHeaderADCLabel.Parent := FHeaderPanel;
FHeaderADCLabel.AutoSize := True;
FHeaderADCLabel.OnClick := HeaderADCClick;
FBtnPopPan := MakeFlatBtn(FHeaderPanel, '⧉', 0, 0, 10, 10, BtnPopPanClick);
FBtnPopPan.Hint := 'Pop out';
FBtnPopPan.ShowHint := True;
FBtnPopPan.Visible := False; // хозяин включает у панов N>0
FBtnClosePan := MakeFlatBtn(FHeaderPanel, '×', 0, 0, 10, 10, BtnClosePanClick);
FBtnClosePan.Visible := False; // хозяин включает у панов N>0
end;
procedure TPanafallPanel.ReparentTo(NewParent: TWinControl);
begin
if (NewParent = nil) or (NewParent = FParent) then Exit;
FParent := NewParent;
FPbSpectrum.Parent := NewParent;
FPbRuler.Parent := NewParent;
FSplitter.Parent := NewParent;
FPbWaterfall.Parent := NewParent;
FPbPanZoom.Parent := NewParent;
FBtnZoomOut.Parent := NewParent;
FBtnZoomDef.Parent := NewParent;
FBtnZoomIn.Parent := NewParent;
FBtnAddPan.Parent := NewParent;
FHeaderPanel.Parent := NewParent; // дети шапки едут вместе с ней
end;
procedure TPanafallPanel.BtnClosePanClick(Sender: TObject);
begin
if Assigned(FOnPanClose) then FOnPanClose(Self);
@@ -410,6 +456,8 @@ begin
if FHeaderRateLabel.Caption <> S then FHeaderRateLabel.Caption := S;
if FHeaderLabel <> nil then
FHeaderRateLabel.Left := FHeaderLabel.Left + FHeaderLabel.Width + 6;
if FHeaderADCLabel <> nil then
FHeaderADCLabel.Left := FHeaderRateLabel.Left + FHeaderRateLabel.Width + 10;
end;
procedure TPanafallPanel.SetRateClickable(AValue: Boolean);
@@ -429,6 +477,37 @@ begin
if FRateClickable and Assigned(FOnRateClick) then FOnRateClick(Self);
end;
procedure TPanafallPanel.SetHeaderADC(const S: string);
begin
if FHeaderADCLabel = nil then Exit;
if FHeaderADCLabel.Caption <> S then FHeaderADCLabel.Caption := S;
FHeaderADCLabel.Visible := S <> '';
if FHeaderRateLabel <> nil then
FHeaderADCLabel.Left := FHeaderRateLabel.Left + FHeaderRateLabel.Width + 10;
end;
procedure TPanafallPanel.SetADCClickable(AValue: Boolean);
begin
FADCClickable := AValue;
if FHeaderADCLabel <> nil then
begin
if AValue then
FHeaderADCLabel.Cursor := crHandPoint
else
FHeaderADCLabel.Cursor := crArrow;
end;
end;
procedure TPanafallPanel.HeaderADCClick(Sender: TObject);
begin
if FADCClickable and Assigned(FOnADCClick) then FOnADCClick(Self);
end;
procedure TPanafallPanel.BtnPopPanClick(Sender: TObject);
begin
if Assigned(FOnPanPopOut) then FOnPanPopOut(Self);
end;
procedure TPanafallPanel.ViewWindow(out VC, VS: Double);
begin
VC := 0; VS := 0;
@@ -452,11 +531,16 @@ begin
FHeaderRateLabel.Top := (H - FHeaderRateLabel.Height) div 2;
FHeaderRateLabel.Left := FHeaderLabel.Left + FHeaderLabel.Width + 6;
end;
if FBtnClosePan <> nil then
if (FHeaderADCLabel <> nil) and (FHeaderRateLabel <> nil) then
begin
BtnS := H - 4;
FBtnClosePan.SetBounds(RW - BtnS - 2, 2, BtnS, BtnS);
FHeaderADCLabel.Top := (H - FHeaderADCLabel.Height) div 2;
FHeaderADCLabel.Left := FHeaderRateLabel.Left + FHeaderRateLabel.Width + 10;
end;
BtnS := H - 4;
if FBtnClosePan <> nil then
FBtnClosePan.SetBounds(RW - BtnS - 2, 2, BtnS, BtnS);
if FBtnPopPan <> nil then
FBtnPopPan.SetBounds(RW - 2 * BtnS - 6, 2, BtnS, BtnS);
end;
procedure TPanafallPanel.PositionPanZoomBar(BottomY, RW, H: Integer;