mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
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:
+163
-4
@@ -187,6 +187,8 @@ type
|
||||
// Меню rate пана («NNN kHz» в шапке панов N; создаётся лениво).
|
||||
FPanRateMenu: TPopupMenu;
|
||||
FPanRateMenuPanId: Integer;
|
||||
// Pop-out (3.5): пан N в отдельном OS-окне. nil = пан в стеке.
|
||||
FPanFloatForm: array[1..MAX_PANS-1] of TForm;
|
||||
FSpecView: TSpectrumView;
|
||||
FWidebandView: TWidebandView;
|
||||
FUseOpenGLSpectrum: Boolean;
|
||||
@@ -575,6 +577,15 @@ type
|
||||
procedure UpdateAddPanButton;
|
||||
procedure OnPanRateClick(Sender: TObject); // «NNN kHz» шапки пана N
|
||||
procedure PanRateMenuItemClick(Sender: TObject);
|
||||
procedure OnPanADCClick(Sender: TObject); // бейдж «A1/A2» (3.4)
|
||||
// Pop-out (3.5): «⧉» выносит пан в отдельное окно, [×] окна возвращает в стек.
|
||||
procedure OnPanPopOutClick(Sender: TObject);
|
||||
procedure PopOutPan(PanId: Integer);
|
||||
procedure DockBackPan(PanId: Integer);
|
||||
procedure PanFloatClose(Sender: TObject; var CloseAction: TCloseAction);
|
||||
procedure PanFloatResize(Sender: TObject);
|
||||
function PanFloating(PanId: Integer): Boolean;
|
||||
procedure ResetPanGLCaches(P: TPanafallPanel); // после смены родителя
|
||||
procedure AddSliceAtFreqPan(P: TPanafallPanel; Hz: Double);
|
||||
procedure PanClickTune(P: TPanafallPanel; X, W: Integer); // клик по фону пана N
|
||||
procedure PanViewWindow(P: TPanafallPanel; out VC, VS: Double);
|
||||
@@ -1061,6 +1072,7 @@ begin
|
||||
// Стек панов (этап 3.3): пан 0 — в общем массиве; «⊞» добавляет пан.
|
||||
FPan.PanId := 0;
|
||||
FPan.OnAddPan := OnAddPanClick;
|
||||
FPan.OnADCClick := OnPanADCClick; // бейдж A1/A2 (3.4, платы с 2 АЦП)
|
||||
FPans[0] := FPan;
|
||||
FPanShare[0] := 1.0;
|
||||
FPanCbLock := SyncObjs.TCriticalSection.Create;
|
||||
@@ -2566,9 +2578,11 @@ var
|
||||
N, i, k, TotalH, UsableH, Y, Hi: Integer;
|
||||
ShareSum: Double;
|
||||
begin
|
||||
// Плавающие (pop-out) паны в стеке не участвуют — у них своё окно.
|
||||
N := 0;
|
||||
for i := 0 to MAX_PANS - 1 do
|
||||
if FPans[i] <> nil then begin ActiveIds[N] := i; Inc(N); end;
|
||||
if (FPans[i] <> nil) and not PanFloating(i) then
|
||||
begin ActiveIds[N] := i; Inc(N); end;
|
||||
if N = 0 then Exit;
|
||||
TotalH := PanelRight.ClientHeight;
|
||||
UsableH := TotalH - (N - 1) * PAN_SPLIT_H;
|
||||
@@ -2679,6 +2693,7 @@ begin
|
||||
if FPans[i] <> nil then
|
||||
begin
|
||||
if FPans[i].BtnAddPan <> nil then StyleButton(FPans[i].BtnAddPan, False);
|
||||
if FPans[i].BtnPopPan <> nil then StyleButton(FPans[i].BtnPopPan, False);
|
||||
if FPans[i].BtnClosePan <> nil then StyleButton(FPans[i].BtnClosePan, False);
|
||||
if FPans[i].HeaderPanel <> nil then
|
||||
begin
|
||||
@@ -2689,11 +2704,17 @@ begin
|
||||
FPans[i].HeaderRateLabel.Font.Color := T.BtnTextActive
|
||||
else
|
||||
FPans[i].HeaderRateLabel.Font.Color := T.Text;
|
||||
// ADC-бейдж кликабелен на 2-АЦПшных платах — тоже акцент.
|
||||
if FPans[i].ADCClickable then
|
||||
FPans[i].HeaderADCLabel.Font.Color := T.BtnTextActive
|
||||
else
|
||||
FPans[i].HeaderADCLabel.Font.Color := T.Text;
|
||||
end;
|
||||
if i > 0 then
|
||||
begin
|
||||
FPans[i].SetFlagsTheme(T);
|
||||
if FPanSplitters[i] <> nil then FPanSplitters[i].Color := T.Border;
|
||||
if FPanFloatForm[i] <> nil then FPanFloatForm[i].Color := T.BG;
|
||||
end;
|
||||
end;
|
||||
if FPanZoomBar <> nil then
|
||||
@@ -5355,6 +5376,120 @@ begin
|
||||
MarkPanDirty(P);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnPanADCClick(Sender: TObject);
|
||||
// Бейдж «A1/A2»: тумблер ADC-источника пана (двух-АЦПшные платы, 3.4).
|
||||
var
|
||||
P: TPanafallPanel;
|
||||
N: Integer;
|
||||
begin
|
||||
if not (Sender is TPanafallPanel) then Exit;
|
||||
P := TPanafallPanel(Sender);
|
||||
N := FController.BackendCaps.NumADCs;
|
||||
if N < 2 then Exit;
|
||||
if not FController.SetPanADC(P.PanId, (FController.PanADC(P.PanId) + 1) mod N) then
|
||||
Exit;
|
||||
UpdatePanHeaders;
|
||||
end;
|
||||
|
||||
// ---- Pop-out (3.5): пан N в отдельном OS-окне ----
|
||||
|
||||
function TMainForm.PanFloating(PanId: Integer): Boolean;
|
||||
begin
|
||||
Result := (PanId >= 1) and (PanId < MAX_PANS) and (FPanFloatForm[PanId] <> nil);
|
||||
end;
|
||||
|
||||
procedure TMainForm.ResetPanGLCaches(P: TPanafallPanel);
|
||||
// Смена родителя пересоздаёт хэндлы GL-канв → старые контексты (и все
|
||||
// текстуры) мертвы; кэши вьюхи обнуляем БЕЗ glDelete (как при смене MSAA).
|
||||
begin
|
||||
if FUseOpenGLSpectrum and (P.View is TSpectrumViewOpenGL) then
|
||||
TSpectrumViewOpenGL(P.View).ResetGLCache;
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnPanPopOutClick(Sender: TObject);
|
||||
begin
|
||||
if Sender is TPanafallPanel then
|
||||
PopOutPan(TPanafallPanel(Sender).PanId);
|
||||
end;
|
||||
|
||||
procedure TMainForm.PopOutPan(PanId: Integer);
|
||||
var
|
||||
P: TPanafallPanel;
|
||||
F: TForm;
|
||||
T: TAppTheme;
|
||||
begin
|
||||
if (PanId < 1) or (PanId >= MAX_PANS) then Exit;
|
||||
P := FPans[PanId];
|
||||
if (P = nil) or PanFloating(PanId) then Exit;
|
||||
if FLightTheme then T := LightTheme else T := DarkTheme;
|
||||
F := TForm.CreateNew(Self);
|
||||
F.Caption := Format('EWSDR · PAN %d', [PanId]);
|
||||
F.Color := T.BG;
|
||||
F.Position := poDefault;
|
||||
F.Width := PanelRight.ClientWidth;
|
||||
F.Height := Max(300, P.StackHeight);
|
||||
F.Tag := PanId;
|
||||
F.OnClose := PanFloatClose;
|
||||
F.OnResize := PanFloatResize;
|
||||
F.OnMouseWheel := FormMouseWheel; // колесо над флагами/фоном как в стеке
|
||||
FPanFloatForm[PanId] := F;
|
||||
P.ReparentTo(F);
|
||||
ResetPanGLCaches(P);
|
||||
P.StackTop := 0;
|
||||
P.StackHeight := 0; // 0/0 = весь родитель (окно)
|
||||
if FPanSplitters[PanId] <> nil then FPanSplitters[PanId].Visible := False;
|
||||
F.Show;
|
||||
UpdatePanHeaders;
|
||||
ResizeSpectrumPanels; // пере-стек оставшихся в главном окне
|
||||
P.Layout(0, FController.FShowSpectrum, FController.FShowWaterfall);
|
||||
P.LayoutFlags;
|
||||
MarkPanDirty(P);
|
||||
end;
|
||||
|
||||
procedure TMainForm.DockBackPan(PanId: Integer);
|
||||
var
|
||||
P: TPanafallPanel;
|
||||
F: TForm;
|
||||
begin
|
||||
P := FPans[PanId];
|
||||
F := FPanFloatForm[PanId];
|
||||
if F = nil then Exit;
|
||||
FPanFloatForm[PanId] := nil;
|
||||
if P <> nil then
|
||||
begin
|
||||
P.ReparentTo(PanelRight);
|
||||
ResetPanGLCaches(P);
|
||||
end;
|
||||
if FPanSplitters[PanId] <> nil then FPanSplitters[PanId].Visible := True;
|
||||
F.Release; // безопасно из событий самого окна
|
||||
UpdatePanHeaders;
|
||||
ResizeSpectrumPanels;
|
||||
if P <> nil then
|
||||
begin
|
||||
P.LayoutFlags;
|
||||
MarkPanDirty(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.PanFloatClose(Sender: TObject; var CloseAction: TCloseAction);
|
||||
// [×] окна = вернуть пан в стек (не закрыть пан). Release — в DockBackPan.
|
||||
begin
|
||||
CloseAction := caNone;
|
||||
DockBackPan(TForm(Sender).Tag);
|
||||
end;
|
||||
|
||||
procedure TMainForm.PanFloatResize(Sender: TObject);
|
||||
var P: TPanafallPanel; PanId: Integer;
|
||||
begin
|
||||
PanId := TForm(Sender).Tag;
|
||||
if (PanId < 1) or (PanId >= MAX_PANS) then Exit;
|
||||
P := FPans[PanId];
|
||||
if (P = nil) or (FPanFloatForm[PanId] <> Sender) then Exit;
|
||||
P.Layout(0, FController.FShowSpectrum, FController.FShowWaterfall);
|
||||
P.LayoutFlags;
|
||||
MarkPanDirty(P);
|
||||
end;
|
||||
|
||||
procedure TMainForm.PanClickTune(P: TPanafallPanel; X, W: Integer);
|
||||
// Клик по фону доп. пана = перестроить его слайс на частоту клика (аналог
|
||||
// DoSpectrumClick главного: снап к 100 Гц). Цель — активный слайс, если он
|
||||
@@ -5392,18 +5527,22 @@ begin
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdatePanHeaders;
|
||||
// Текст/видимость шапок: у одного пана шапки нет, у стека — у всех.
|
||||
// Текст/видимость шапок: у одного пана шапки нет, у стека — у всех;
|
||||
// у плавающего (pop-out) пана шапка всегда (там его rate/ADC/×).
|
||||
var
|
||||
i, N: Integer;
|
||||
i, N, NADC: Integer;
|
||||
P: TPanafallPanel;
|
||||
begin
|
||||
N := PanCount;
|
||||
NADC := FController.BackendCaps.NumADCs;
|
||||
for i := 0 to MAX_PANS - 1 do
|
||||
begin
|
||||
P := FPans[i];
|
||||
if P = nil then Continue;
|
||||
P.HeaderVisible := N > 1;
|
||||
P.HeaderVisible := (N > 1) or ((i > 0) and PanFloating(i));
|
||||
if P.BtnClosePan <> nil then P.BtnClosePan.Visible := (N > 1) and (i > 0);
|
||||
if P.BtnPopPan <> nil then
|
||||
P.BtnPopPan.Visible := (N > 1) and (i > 0) and not PanFloating(i);
|
||||
if i = 0 then
|
||||
begin
|
||||
P.SetHeaderText(Format('PAN 0 · %.6f MHz ·',
|
||||
@@ -5416,6 +5555,17 @@ begin
|
||||
[i, FController.PanDDCFreq(i) / 1e6]));
|
||||
P.SetHeaderRate(Format('%d kHz', [FController.PanDDCRateKHz(i)]));
|
||||
end;
|
||||
// Бейдж АЦП: только на двух-АЦПшных платах; клик = тумблер.
|
||||
if NADC > 1 then
|
||||
begin
|
||||
P.SetHeaderADC(Format('A%d', [FController.PanADC(i) + 1]));
|
||||
P.ADCClickable := True;
|
||||
end
|
||||
else
|
||||
begin
|
||||
P.SetHeaderADC('');
|
||||
P.ADCClickable := False;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -5466,6 +5616,8 @@ begin
|
||||
P.OnSplitterMoved := RightPanelResize; // ratio обновлён → полный re-layout
|
||||
P.OnRateClick := OnPanRateClick; // «NNN kHz» в шапке → меню rate
|
||||
P.RateClickable := True;
|
||||
P.OnADCClick := OnPanADCClick; // «A1/A2» тумблер АЦП (3.4)
|
||||
P.OnPanPopOut := OnPanPopOutClick; // «⧉» в отдельное окно (3.5)
|
||||
P.SpectrumMouseDown := PanNSpectrumMouseDown;
|
||||
P.SpectrumMouseMove := PanNSpectrumMouseMove;
|
||||
P.SpectrumMouseUp := PanNSpectrumMouseUp;
|
||||
@@ -5555,6 +5707,13 @@ begin
|
||||
FActiveSliceId := 0;
|
||||
FreeAndNil(FPanSplitters[PanId]);
|
||||
P.Free;
|
||||
// Пан был в отдельном окне — окно больше не нужно. Release (не Free):
|
||||
// сюда можно попасть из событий контролов этого же окна («×» шапки → тик).
|
||||
if FPanFloatForm[PanId] <> nil then
|
||||
begin
|
||||
FPanFloatForm[PanId].Release;
|
||||
FPanFloatForm[PanId] := nil;
|
||||
end;
|
||||
for i := 0 to MAX_PANS - 1 do
|
||||
if FPans[i] <> nil then FPanShare[i] := 1.0 / PanCount;
|
||||
UpdatePanHeaders;
|
||||
|
||||
Reference in New Issue
Block a user