mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
feat(slices): per-pan дисплей — палитра/gamma/уровни на пан («◑»-поповер)
Кнопка «◑» в шапке каждого пана открывает компактный поповер настроек отображения (палитра, gamma, ref/range спектра, авто-уровень водопада + offset/ручные min-max). Модель «глобальный дефолт + override на пан»: пан 0 правит глобальные поля рендера (зеркалятся в контроллер), паны N ставят DisplayOverride и персистятся в воркспейс раздельно КВ/трансвертер. - PanDisplayPopup.pas (new): borderless fsStayOnTop форма, гаснет по OnDeactivate; Flat*-контролы; крестик «×» закрытия; дим-хинт «правки применяются и сохраняются сами»; кнопки «⟳ Reset»/«⇄ Copy to all». - TPanafallPanel: FBtnDisplay + OnDisplayClick + DisplayOverride; PositionHeaderChildren каскадит [×][⧉][◑] справа по Visible. - MainForm: OnPanDisplayClick/Applied/Reset/ApplyAll + ApplyGlobalRenderToView + MarkPanDisplayDirty. При смене палитры/сбросе чистим историю GL-водопада (TSpectrumView.ClearWaterfall) — GL хранит уже раскрашенные пиксели. - Settings/TPanCfg: под-объект 'display' (палитра/gamma/ref/range/wf-уровни), пишется только при override. Глобальными осознанно оставлены разрешение анализатора, MSAA, скорость водопада, детектор/усреднение (движок/перф, не косметика). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+170
-1
@@ -33,7 +33,7 @@ uses
|
||||
WebServer, WebAdapter,
|
||||
CATAdapter,
|
||||
SpectrumView, SpectrumViewOpengl,
|
||||
PanZoomBar, PanafallPanel,
|
||||
PanZoomBar, PanafallPanel, PanDisplayPopup,
|
||||
WidebandView,
|
||||
StatusBar,
|
||||
PlatformUtils,
|
||||
@@ -167,6 +167,7 @@ type
|
||||
FPanSpecDirty: array[0..MAX_PANS-1] of Boolean; // кадр пана готов к рендеру
|
||||
FPanWfDirty: array[0..MAX_PANS-1] of Boolean;
|
||||
FPanCbLock: SyncObjs.TCriticalSection; // жизнь FPans[i] vs пиксельные колбэки
|
||||
FPanDisplayPopup: TObject; // TPanDisplayPopup (3.6), лениво создаётся
|
||||
// Мышь на пане N>0: drag слайса или drag центра пана (ретюн DDC).
|
||||
FPanSliceDrag: Boolean;
|
||||
FPanSliceDragId: Integer;
|
||||
@@ -604,6 +605,13 @@ type
|
||||
procedure OnPanRateClick(Sender: TObject); // «NNN kHz» шапки пана N
|
||||
procedure PanRateMenuItemClick(Sender: TObject);
|
||||
procedure OnPanADCClick(Sender: TObject); // бейдж «RX1/RX2» (3.4)
|
||||
// Per-pan дисплей (3.6): «◑» → поповер палитры/gamma/уровней.
|
||||
procedure OnPanDisplayClick(Sender: TObject);
|
||||
procedure OnPanDisplayApplied(P: TPanafallPanel; FullRefresh: Boolean);
|
||||
procedure OnPanDisplayReset(P: TPanafallPanel); // сброс к глоб. дефолту
|
||||
procedure OnPanDisplayApplyAll(P: TPanafallPanel); // раскатать на все паны
|
||||
procedure ApplyGlobalRenderToView(V: TSpectrumView); // глоб. рендер → вьюха
|
||||
procedure MarkPanDisplayDirty(P: TPanafallPanel; ClearWf: Boolean); // репейнт (+сброс истории водопада)
|
||||
// Pop-out (3.5): «⧉» выносит пан в отдельное окно, [×] окна возвращает в стек.
|
||||
procedure OnPanPopOutClick(Sender: TObject);
|
||||
procedure PopOutPan(PanId: Integer);
|
||||
@@ -1111,6 +1119,7 @@ begin
|
||||
FPan.PanId := 0;
|
||||
FPan.OnAddPan := OnAddPanClick;
|
||||
FPan.OnADCClick := OnPanADCClick; // бейдж RX1/RX2 (3.4, платы с 2 АЦП)
|
||||
FPan.OnDisplayClick := OnPanDisplayClick; // «◑» дисплей-поповер (3.6)
|
||||
FPans[0] := FPan;
|
||||
FPanShare[0] := 1.0;
|
||||
FPanGridMode := False;
|
||||
@@ -2881,6 +2890,7 @@ begin
|
||||
if FPans[i].BtnGrid <> nil then StyleButton(FPans[i].BtnGrid, 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].BtnDisplay <> nil then StyleButton(FPans[i].BtnDisplay, False);
|
||||
ApplyPanHeaderTheme(FPans[i]);
|
||||
if i > 0 then
|
||||
begin
|
||||
@@ -5643,6 +5653,132 @@ begin
|
||||
UpdatePanHeaders;
|
||||
end;
|
||||
|
||||
// ---- Per-pan дисплей (3.6): «◑» палитра/gamma/уровни отдельно на пан ----
|
||||
|
||||
procedure TMainForm.ApplyGlobalRenderToView(V: TSpectrumView);
|
||||
// Глобальный дефолт рендера (контроллер) → вьюха пана. Используется при
|
||||
// создании пана (дефолт) и «Сбросе к дефолту».
|
||||
begin
|
||||
if V = nil then Exit;
|
||||
V.WfPalette := FController.FWfPalette;
|
||||
V.WfGamma := FController.FWfGamma;
|
||||
V.SpecRefLevel := FController.FSpecRefLevel;
|
||||
V.SpecRange := FController.FSpecRange;
|
||||
V.SpecGridStep := FController.FSpecGridStep;
|
||||
V.WfAGCEnabled := FController.FWfAGCEnabled;
|
||||
V.WfAGCOffset := FController.FWfAGCOffset;
|
||||
V.WfManualHigh := FController.FWfManualHigh;
|
||||
V.WfManualLow := FController.FWfManualLow;
|
||||
end;
|
||||
|
||||
procedure TMainForm.MarkPanDisplayDirty(P: TPanafallPanel; ClearWf: Boolean);
|
||||
// Полный репейнт пана после смены рендера: сетка спектра + водопад
|
||||
// (LUT/палитра пересоберётся в SpectrumView по своим no-op-гардам).
|
||||
// ClearWf=True (смена палитры/gamma/авто) чистит историю водопада — GL хранит
|
||||
// уже раскрашенные пиксели, иначе старые строки остаются в прежней палитре.
|
||||
begin
|
||||
if P = nil then Exit;
|
||||
P.View.InvalidateGridCache;
|
||||
if ClearWf then P.View.ClearWaterfall;
|
||||
if P = FPan then
|
||||
begin
|
||||
FSpectrumDirty := True;
|
||||
FWaterfallDirty := True;
|
||||
if Assigned(PbSpectrum) then PbSpectrum.Invalidate;
|
||||
if Assigned(PbWaterfall) then PbWaterfall.Invalidate;
|
||||
Exit;
|
||||
end;
|
||||
P.View.SpectrumDirty := True;
|
||||
FPanSpecDirty[P.PanId] := True;
|
||||
FPanWfDirty[P.PanId] := True;
|
||||
if P.PbSpectrum <> nil then P.PbSpectrum.Invalidate;
|
||||
if P.PbWaterfall <> nil then P.PbWaterfall.Invalidate;
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnPanDisplayClick(Sender: TObject);
|
||||
// «◑» в шапке пана: показать поповер под кнопкой.
|
||||
var
|
||||
P: TPanafallPanel;
|
||||
Pop: TPanDisplayPopup;
|
||||
Pt: TPoint;
|
||||
begin
|
||||
if not (Sender is TPanafallPanel) then Exit;
|
||||
P := TPanafallPanel(Sender);
|
||||
if P.BtnDisplay = nil then Exit;
|
||||
if FPanDisplayPopup = nil then
|
||||
begin
|
||||
Pop := TPanDisplayPopup.CreateNew(Self);
|
||||
Pop.OnApplied := OnPanDisplayApplied;
|
||||
Pop.OnResetDefault := OnPanDisplayReset;
|
||||
Pop.OnApplyAll := OnPanDisplayApplyAll;
|
||||
FPanDisplayPopup := Pop;
|
||||
end
|
||||
else
|
||||
Pop := TPanDisplayPopup(FPanDisplayPopup);
|
||||
// Экранная позиция левого-нижнего угла кнопки.
|
||||
Pt := P.BtnDisplay.ClientToScreen(Point(0, P.BtnDisplay.Height + 2));
|
||||
Pop.ShowFor(P, Pt.X, Pt.Y);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnPanDisplayApplied(P: TPanafallPanel; FullRefresh: Boolean);
|
||||
// Поповер применил правку к P.View. Пан 0 = глобальный дефолт (зеркалим в
|
||||
// контроллер, чтобы персистилось и наследовалось новыми панами); паны N —
|
||||
// ставим override-флаг (персист в воркспейс).
|
||||
begin
|
||||
if P = nil then Exit;
|
||||
if P.PanId = 0 then
|
||||
begin
|
||||
FController.FWfPalette := P.View.WfPalette;
|
||||
FController.FWfGamma := P.View.WfGamma;
|
||||
FController.FSpecRefLevel := P.View.SpecRefLevel;
|
||||
FController.FSpecRange := P.View.SpecRange;
|
||||
FController.FWfAGCEnabled := P.View.WfAGCEnabled;
|
||||
FController.FWfAGCOffset := P.View.WfAGCOffset;
|
||||
FController.FWfManualHigh := P.View.WfManualHigh;
|
||||
FController.FWfManualLow := P.View.WfManualLow;
|
||||
// На диск не пишем на каждый тик — глобальные сохранятся при закрытии
|
||||
// (FormDestroy → SaveGlobalSettings), как и для главного диалога настроек.
|
||||
end
|
||||
else
|
||||
P.DisplayOverride := True;
|
||||
MarkPanDisplayDirty(P, FullRefresh);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnPanDisplayReset(P: TPanafallPanel);
|
||||
// «Сброс к дефолту»: пан снова наследует глобальный рендер.
|
||||
begin
|
||||
if (P = nil) or (P.PanId = 0) then Exit;
|
||||
ApplyGlobalRenderToView(P.View);
|
||||
P.DisplayOverride := False;
|
||||
MarkPanDisplayDirty(P, True); // палитра могла смениться → чистим водопад
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnPanDisplayApplyAll(P: TPanafallPanel);
|
||||
// «Применить ко всем»: рендер пана P раскатывается на все паны + становится
|
||||
// глобальным дефолтом (для пана 0 и новых панов).
|
||||
var i: Integer; Q: TPanafallPanel;
|
||||
begin
|
||||
if P = nil then Exit;
|
||||
// Глобальный дефолт ← P.
|
||||
FController.FWfPalette := P.View.WfPalette;
|
||||
FController.FWfGamma := P.View.WfGamma;
|
||||
FController.FSpecRefLevel := P.View.SpecRefLevel;
|
||||
FController.FSpecRange := P.View.SpecRange;
|
||||
FController.FWfAGCEnabled := P.View.WfAGCEnabled;
|
||||
FController.FWfAGCOffset := P.View.WfAGCOffset;
|
||||
FController.FWfManualHigh := P.View.WfManualHigh;
|
||||
FController.FWfManualLow := P.View.WfManualLow;
|
||||
FController.SaveGlobalSettings;
|
||||
for i := 0 to MAX_PANS - 1 do
|
||||
begin
|
||||
Q := FPans[i];
|
||||
if Q = nil then Continue;
|
||||
if Q <> P then ApplyGlobalRenderToView(Q.View);
|
||||
Q.DisplayOverride := False; // все синхронны с дефолтом ⇒ не override
|
||||
MarkPanDisplayDirty(Q, True); // палитра раскатана → чистим водопад
|
||||
end;
|
||||
end;
|
||||
|
||||
// ---- Pop-out (3.5): пан N в отдельном OS-окне ----
|
||||
|
||||
function TMainForm.PanFloating(PanId: Integer): Boolean;
|
||||
@@ -5822,6 +5958,8 @@ begin
|
||||
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);
|
||||
// «◑» дисплей-поповер — на всех панах с видимой шапкой (вкл. пан 0).
|
||||
if P.BtnDisplay <> nil then P.BtnDisplay.Visible := P.HeaderVisible;
|
||||
if i = 0 then
|
||||
begin
|
||||
P.SetHeaderText(Format('PAN 0 · %.6f MHz ·',
|
||||
@@ -5888,6 +6026,7 @@ begin
|
||||
P.RateClickable := True;
|
||||
P.OnADCClick := OnPanADCClick; // «RX1/RX2» тумблер АЦП (3.4)
|
||||
P.OnPanPopOut := OnPanPopOutClick; // «⧉» в отдельное окно (3.5)
|
||||
P.OnDisplayClick := OnPanDisplayClick; // «◑» дисплей-поповер (3.6)
|
||||
P.SpectrumMouseDown := PanNSpectrumMouseDown;
|
||||
P.SpectrumMouseMove := PanNSpectrumMouseMove;
|
||||
P.SpectrumMouseUp := PanNSpectrumMouseUp;
|
||||
@@ -6010,6 +6149,9 @@ begin
|
||||
if (PanId < 1) or (PanId >= MAX_PANS) then Exit;
|
||||
P := FPans[PanId];
|
||||
if P = nil then Exit;
|
||||
// Дисплей-поповер мог быть привязан к этому пану — спрятать (FPan станет
|
||||
// висячим; поповер перепривяжется при следующем показе через LoadFrom).
|
||||
if FPanDisplayPopup <> nil then TPanDisplayPopup(FPanDisplayPopup).Hide;
|
||||
// Отписываем от пиксельных колбэков ДО удаления DDC/панели.
|
||||
FPanCbLock.Enter;
|
||||
try
|
||||
@@ -6094,6 +6236,19 @@ begin
|
||||
Cfg.Pans[i].Zoom := FController.PanZoomFactor(i);
|
||||
Cfg.Pans[i].PanSlider := FController.PanPanSlider(i);
|
||||
Cfg.Pans[i].ColShare := FPanColShare[i];
|
||||
// Per-pan дисплей (3.6): пишем override только если пан его задал.
|
||||
if (FPans[i] <> nil) and FPans[i].DisplayOverride then
|
||||
begin
|
||||
Cfg.Pans[i].HasDisplay := True;
|
||||
Cfg.Pans[i].DPalette := FPans[i].View.WfPalette;
|
||||
Cfg.Pans[i].DGamma := FPans[i].View.WfGamma;
|
||||
Cfg.Pans[i].DSpecRef := FPans[i].View.SpecRefLevel;
|
||||
Cfg.Pans[i].DSpecRange := FPans[i].View.SpecRange;
|
||||
Cfg.Pans[i].DWfAGC := FPans[i].View.WfAGCEnabled;
|
||||
Cfg.Pans[i].DWfOffset := FPans[i].View.WfAGCOffset;
|
||||
Cfg.Pans[i].DWfHigh := FPans[i].View.WfManualHigh;
|
||||
Cfg.Pans[i].DWfLow := FPans[i].View.WfManualLow;
|
||||
end;
|
||||
end;
|
||||
// Слайсы этого пана (главный VFO пана 0 — не слайс, его тут нет).
|
||||
n := 0;
|
||||
@@ -6188,6 +6343,20 @@ begin
|
||||
FPanShare[i] := Cfg.Pans[i].StackShare;
|
||||
FPanColShare[i] := Cfg.Pans[i].ColShare;
|
||||
P.SplitterRatio := Cfg.Pans[i].SplitRatio;
|
||||
// Per-pan дисплей (3.6): CreatePanCore уже залил глоб. дефолт — при наличии
|
||||
// override перекрываем сохранёнкой.
|
||||
if Cfg.Pans[i].HasDisplay then
|
||||
begin
|
||||
P.View.WfPalette := EnsureRange(Cfg.Pans[i].DPalette, 0, 2);
|
||||
P.View.WfGamma := EnsureRange(Cfg.Pans[i].DGamma, 0.30, 3.00);
|
||||
P.View.SpecRefLevel := Cfg.Pans[i].DSpecRef;
|
||||
P.View.SpecRange := Cfg.Pans[i].DSpecRange;
|
||||
P.View.WfAGCEnabled := Cfg.Pans[i].DWfAGC;
|
||||
P.View.WfAGCOffset := Cfg.Pans[i].DWfOffset;
|
||||
P.View.WfManualHigh := Cfg.Pans[i].DWfHigh;
|
||||
P.View.WfManualLow := Cfg.Pans[i].DWfLow;
|
||||
P.DisplayOverride := True;
|
||||
end;
|
||||
FController.SetPanZoomPan(i, Cfg.Pans[i].Zoom, Cfg.Pans[i].PanSlider);
|
||||
for k := 0 to High(Cfg.Pans[i].Slices) do
|
||||
RestoreSliceInto(P, i, Cfg.Pans[i].Slices[k]);
|
||||
|
||||
Reference in New Issue
Block a user